@smileid/usesmileid_vision_face 12.0.0-beta01 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 12.0.0
2
+
3
+ First stable release of the Smile ID v12 SDK. Provides the iOS Apple Vision face detection module for `usesmileid`.
4
+
1
5
  ## 12.0.0-beta01
2
6
 
3
7
  First public beta release. Apple Vision face detection for the Smile ID React Native (Expo) SDK v12.
package/README.md CHANGED
@@ -13,7 +13,6 @@ and a TS wrapper that bridges the four contract methods (`configure /
13
13
  analyze / reset / close`) with Zod-validated wire payloads.
14
14
 
15
15
  No ML logic is reimplemented in this package — it's a pure passthrough.
16
- See `react-native-expo/docs/Architecture.md`.
17
16
 
18
17
  ## Installation
19
18
 
@@ -1,34 +1,30 @@
1
- // MIRRORED FILE — duplicated into both RN Expo iOS vision modules
2
- // (usesmileid_vision_face / usesmileid_vision_document). The per-module
3
- // CocoaPods bridge target sources cannot share files without a new pod.
4
- // Canonical location:
5
- // usesmileid_vision_face/ios/UseSmileIDVisionFaceModule/ResizingFrameStorage.swift
6
- // Mirror location:
7
- // usesmileid_vision_document/ios/UseSmileIDVisionDocumentModule/ResizingFrameStorage.swift
8
- //
9
- // Any edit here MUST be applied to the other location in the same commit.
1
+ // MIRRORED FILE — duplicated in both RN Expo iOS vision modules
2
+ // (usesmileid_vision_face / usesmileid_vision_document); the per-module pod
3
+ // targets can't share sources. Apply any edit to BOTH copies in the same commit.
10
4
 
11
5
  import Foundation
12
6
  import UIKit
13
7
  import UseSmileIDBridge
14
8
 
15
- /// `FrameStorage` implementation that center-crops and scales every selfie and
16
- /// liveness JPEG to the configured target dimensions before writing.
17
- ///
18
- /// Target dimensions are resolved at configure-time from the token
19
- /// `sdk_config` claim; the executor falls back to 480 × 640 when the token
20
- /// carries no override, so the on-disk pixel size always matches the
21
- /// `image_width` / `image_height` values reported in job metadata.
22
- ///
23
- /// Document frames are not resized — they are written at the analyzer's
24
- /// native frame resolution using the specified `documentQuality`. Document
25
- /// dimensions are not part of the token `sdk_config` contract.
26
- ///
27
- /// Encode and write run on background tasks; `persist` is fire-and-forget so
28
- /// the analyzer keeps running at frame rate. `flush()` awaits all in-flight
29
- /// writes before upload proceeds.
9
+ /// `FrameStorage` that center-crops + scales selfie/liveness JPEGs to the
10
+ /// token-resolved target size (falling back to 480×640, matching the
11
+ /// `image_width`/`image_height` job metadata), and downscales document frames
12
+ /// so their longest edge is bounded at `documentMaxDimension` (no upscaling).
13
+ /// When a crop region is set, document frames are first cropped to the on-screen
14
+ /// guide so the saved capture matches what the user framed (`setCropRegion(nil)`
15
+ /// clears it; selfie/liveness are never guide-cropped). Encode + write run on
16
+ /// background tasks; `persist` is fire-and-forget, `flush()` awaits all writes.
30
17
  final class ResizingFrameStorage: FrameStorage {
18
+ /// Longest-edge pixel bound applied to document frames before encoding.
19
+ private static let documentMaxDimension = 1600
20
+ /// Outward padding fraction applied to the guide-rect crop on each side
21
+ /// before cropping. Fifteen percent of the box's own width/height keeps the
22
+ /// document's corners and edges from being clipped when the card is framed
23
+ /// tight against the on-screen guide.
24
+ private static let documentCropPaddingFraction: CGFloat = 0.15
25
+
31
26
  private let sessionDirectory: URL
27
+ private let captureType: CaptureType
32
28
  private let documentSide: DocumentSide
33
29
  private let selfieTargetWidth: Int
34
30
  private let selfieTargetHeight: Int
@@ -38,9 +34,16 @@ final class ResizingFrameStorage: FrameStorage {
38
34
  private let livenessQuality: CGFloat
39
35
  private let documentQuality: CGFloat
40
36
  private let writer: Writer
37
+ // The frame-processor plugin calls `setCropRegion` on the VisionCamera FP
38
+ // thread; `persist` reads the value on that same serialised scan path before
39
+ // handing it (by value) to the background encode task. The Swift value is a
40
+ // `Sendable` struct captured by value into `Writer.enqueue`, so no background
41
+ // task ever observes a torn read of this property.
42
+ nonisolated(unsafe) private var cropRegion: ViewportCropRegion?
41
43
 
42
44
  init(
43
45
  sessionDirectory: URL,
46
+ captureType: CaptureType,
44
47
  documentSide: DocumentSide = .front,
45
48
  selfieTargetWidth: Int = 480,
46
49
  selfieTargetHeight: Int = 640,
@@ -51,6 +54,7 @@ final class ResizingFrameStorage: FrameStorage {
51
54
  documentQuality: CGFloat = 0.85
52
55
  ) {
53
56
  self.sessionDirectory = sessionDirectory
57
+ self.captureType = captureType
54
58
  self.documentSide = documentSide
55
59
  self.selfieTargetWidth = selfieTargetWidth
56
60
  self.selfieTargetHeight = selfieTargetHeight
@@ -62,6 +66,13 @@ final class ResizingFrameStorage: FrameStorage {
62
66
  self.writer = Writer()
63
67
  }
64
68
 
69
+ /// Set the on-screen document guide rectangle so document frames are cropped
70
+ /// to exactly what the user saw. Pass `nil` to clear the crop and save the
71
+ /// full upright frame. Selfie and liveness frames are unaffected.
72
+ func setCropRegion(_ region: ViewportCropRegion?) {
73
+ cropRegion = region
74
+ }
75
+
65
76
  func url(for kind: FrameKind, captureIndex: Int) -> URL {
66
77
  let name: String
67
78
  switch kind {
@@ -84,20 +95,37 @@ final class ResizingFrameStorage: FrameStorage {
84
95
  func persist(image: UIImage, to url: URL) {
85
96
  let name = url.lastPathComponent
86
97
  let resize: (Int, Int)?
98
+ let maxDimension: Int?
87
99
  let quality: CGFloat
100
+ let documentCropRegion: ViewportCropRegion?
88
101
  if name.hasPrefix("selfie") {
89
102
  resize = (selfieTargetWidth, selfieTargetHeight)
103
+ maxDimension = nil
90
104
  quality = selfieQuality
105
+ documentCropRegion = nil
91
106
  } else if name.hasPrefix("liveness") {
92
107
  resize = (livenessTargetWidth, livenessTargetHeight)
108
+ maxDimension = nil
93
109
  quality = livenessQuality
110
+ documentCropRegion = nil
94
111
  } else {
95
- // Document frames: encode at the analyzer's native resolution.
112
+ // Document frames: optionally crop to the guide overlay, then downscale
113
+ // so the longest edge is bounded at documentMaxDimension; frames already
114
+ // within the bound are written as-is (no upscaling).
96
115
  resize = nil
116
+ maxDimension = Self.documentMaxDimension
97
117
  quality = documentQuality
118
+ documentCropRegion = cropRegion
98
119
  }
99
120
  Task {
100
- await writer.enqueue(image: image, to: url, resize: resize, quality: quality)
121
+ await writer.enqueue(
122
+ image: image,
123
+ to: url,
124
+ resize: resize,
125
+ maxDimension: maxDimension,
126
+ quality: quality,
127
+ documentCropRegion: documentCropRegion
128
+ )
101
129
  }
102
130
  }
103
131
 
@@ -106,25 +134,53 @@ final class ResizingFrameStorage: FrameStorage {
106
134
  }
107
135
 
108
136
  func clear() {
137
+ // Selfie and document captures share one session directory; delete
138
+ // only this role's own frames.
109
139
  let fileManager = FileManager.default
110
140
  let contents = (try? fileManager.contentsOfDirectory(
111
141
  at: sessionDirectory,
112
142
  includingPropertiesForKeys: nil
113
143
  )) ?? []
114
- for fileURL in contents {
144
+ for fileURL in contents where ownsForClear(fileURL) {
115
145
  try? fileManager.removeItem(at: fileURL)
116
146
  }
117
147
  }
118
148
 
149
+ private func ownsForClear(_ fileURL: URL) -> Bool {
150
+ let name = fileURL.lastPathComponent
151
+ switch captureType {
152
+ case .selfie:
153
+ return name.hasPrefix("selfie") || name.hasPrefix("liveness")
154
+ case .document:
155
+ return name == url(for: .document, captureIndex: 0).lastPathComponent
156
+ @unknown default:
157
+ return false
158
+ }
159
+ }
160
+
119
161
  /// Serialises writes through an actor so concurrent `persist` calls for
120
162
  /// the same URL don't race and `flush()` can join every outstanding task
121
163
  /// without external synchronisation.
122
164
  private actor Writer {
123
165
  private var inFlight: [Task<Void, Error>] = []
124
166
 
125
- func enqueue(image: UIImage, to url: URL, resize: (Int, Int)?, quality: CGFloat) {
167
+ func enqueue(
168
+ image: UIImage,
169
+ to url: URL,
170
+ resize: (Int, Int)?,
171
+ maxDimension: Int?,
172
+ quality: CGFloat,
173
+ documentCropRegion: ViewportCropRegion?
174
+ ) {
126
175
  let task = Task<Void, Error>.detached(priority: .utility) {
127
- try Self.encodeAndWrite(image: image, to: url, resize: resize, quality: quality)
176
+ try Self.encodeAndWrite(
177
+ image: image,
178
+ to: url,
179
+ resize: resize,
180
+ maxDimension: maxDimension,
181
+ quality: quality,
182
+ documentCropRegion: documentCropRegion
183
+ )
128
184
  }
129
185
  inFlight.append(task)
130
186
  }
@@ -149,7 +205,9 @@ final class ResizingFrameStorage: FrameStorage {
149
205
  image: UIImage,
150
206
  to url: URL,
151
207
  resize: (Int, Int)?,
152
- quality: CGFloat
208
+ maxDimension: Int?,
209
+ quality: CGFloat,
210
+ documentCropRegion: ViewportCropRegion?
153
211
  ) throws {
154
212
  try FileManager.default.createDirectory(
155
213
  at: url.deletingLastPathComponent(),
@@ -162,9 +220,56 @@ final class ResizingFrameStorage: FrameStorage {
162
220
  let upright = image.normalizingOrientation()
163
221
  let data: Data?
164
222
  if let (width, height) = resize, let cgImage = upright.cgImage {
165
- // Center-crop and scale to the token-resolved target size.
223
+ // Center-crop and scale to the token-resolved target size
224
+ // (selfie / liveness path).
166
225
  let cropped = cgImage.centerCroppedExact(width: width, height: height)
167
226
  data = cropped.flatMap { UIImage(cgImage: $0).jpegData(compressionQuality: quality) }
227
+ } else if let maxDimension, var cgImage = upright.cgImage {
228
+ // Document path: optionally crop to the on-screen guide overlay, then
229
+ // downscale so the longest pixel edge is bounded at maxDimension.
230
+ // Never upscale frames already within the bound.
231
+ //
232
+ // Geometric correctness of the crop depends on `upright` being
233
+ // pixel-upright (orientation baked above); `toFrameBoundingBox`
234
+ // maps viewport coordinates onto an upright frame.
235
+ if let region = documentCropRegion {
236
+ let pixelWidth = cgImage.width
237
+ let pixelHeight = cgImage.height
238
+ let box = region.toFrameBoundingBox(frameWidth: pixelWidth, frameHeight: pixelHeight)
239
+ // Pad the guide rect outward by documentCropPaddingFraction of its
240
+ // own size on each side so the document's corners and edges keep
241
+ // breathing room and are never clipped when framed tight. Intersect
242
+ // with the image bounds so a padded edge never runs off-frame.
243
+ let padX = CGFloat(box.width) * ResizingFrameStorage.documentCropPaddingFraction
244
+ let padY = CGFloat(box.height) * ResizingFrameStorage.documentCropPaddingFraction
245
+ let cropRect = CGRect(
246
+ x: (CGFloat(box.left) - padX) * CGFloat(pixelWidth),
247
+ y: (CGFloat(box.top) - padY) * CGFloat(pixelHeight),
248
+ width: (CGFloat(box.width) + padX * 2) * CGFloat(pixelWidth),
249
+ height: (CGFloat(box.height) + padY * 2) * CGFloat(pixelHeight)
250
+ ).intersection(CGRect(x: 0, y: 0, width: pixelWidth, height: pixelHeight))
251
+ if let cropped = cgImage.cropping(to: cropRect) {
252
+ cgImage = cropped
253
+ }
254
+ }
255
+ let pixelWidth = cgImage.width
256
+ let pixelHeight = cgImage.height
257
+ let longest = max(pixelWidth, pixelHeight)
258
+ if longest > maxDimension {
259
+ let scale = CGFloat(maxDimension) / CGFloat(longest)
260
+ let newSize = CGSize(
261
+ width: (CGFloat(pixelWidth) * scale).rounded(),
262
+ height: (CGFloat(pixelHeight) * scale).rounded()
263
+ )
264
+ let format = UIGraphicsImageRendererFormat()
265
+ format.scale = 1
266
+ let scaled = UIGraphicsImageRenderer(size: newSize, format: format).image { _ in
267
+ UIImage(cgImage: cgImage).draw(in: CGRect(origin: .zero, size: newSize))
268
+ }
269
+ data = scaled.jpegData(compressionQuality: quality)
270
+ } else {
271
+ data = UIImage(cgImage: cgImage).jpegData(compressionQuality: quality)
272
+ }
168
273
  } else {
169
274
  data = upright.jpegData(compressionQuality: quality)
170
275
  }
@@ -114,6 +114,7 @@ private extension Dictionary where Key == String, Value == Any? {
114
114
  let livenessQuality = ((self["livenessJpegQuality"] as? NSNumber)?.intValue).map { CGFloat($0) / 100.0 } ?? 0.80
115
115
  return ResizingFrameStorage(
116
116
  sessionDirectory: sessionDirectory,
117
+ captureType: .selfie,
117
118
  documentSide: .front,
118
119
  selfieTargetWidth: selfieTargetWidth,
119
120
  selfieTargetHeight: selfieTargetHeight,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@smileid/usesmileid_vision_face",
3
- "version": "12.0.0-beta01",
4
- "description": "UseSmileID v12 — Apple Vision face provider (iOS-only). Thin Expo Module shim around the existing Swift Package `UseSmileIDVisionFace`.",
3
+ "version": "12.0.0",
4
+ "description": "Apple Vision face detection implementation for the UseSmileID React Native (Expo) SDK.",
5
5
  "homepage": "https://usesmileid.com",
6
6
  "repository": {
7
7
  "type": "git",
@@ -48,8 +48,8 @@
48
48
  "CHANGELOG.md"
49
49
  ],
50
50
  "dependencies": {
51
- "@smileid/usesmileid_bridge": "12.0.0-beta01",
52
- "@smileid/usesmileid_platform_interface": "12.0.0-beta01"
51
+ "@smileid/usesmileid_platform_interface": "12.0.0",
52
+ "@smileid/usesmileid_bridge": "12.0.0"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "expo": ">=56.0.0",
@@ -64,8 +64,7 @@
64
64
  "typescript": "^6.0.3"
65
65
  },
66
66
  "publishConfig": {
67
- "access": "public",
68
- "provenance": false
67
+ "access": "public"
69
68
  },
70
69
  "scripts": {
71
70
  "build": "pnpm run build:lib && pnpm run build:plugin",
@@ -51,15 +51,15 @@ const fs = __importStar(require("fs"));
51
51
  const path = __importStar(require("path"));
52
52
  const podfile_1 = require("./lib/podfile");
53
53
  const spm_1 = require("./lib/spm");
54
- // Pinned to the exact `12.0.0-beta01` release tag — `from: 12.0.0`
54
+ // Pinned to the exact `12.0.0` release tag — `from: 12.0.0`
55
55
  // would not resolve because `ios-spm` publishes pre-release tags (e.g.
56
- // `12.0.0-beta01`), and SPM excludes pre-release tags from open-ended
56
+ // `12.0.0`), and SPM excludes pre-release tags from open-ended
57
57
  // major-version requirements.
58
58
  const SMILE_ID_PACKAGE = {
59
59
  url: "https://github.com/smileidentity/ios-spm",
60
60
  requirement: {
61
61
  kind: "exactVersion",
62
- version: "12.0.0-beta01",
62
+ version: "12.0.0",
63
63
  },
64
64
  };
65
65
  const POD_TARGET = "UseSmileIDVisionFaceExpo";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,iEAAiE;AACjE,0DAA0D;AAC1D,qEAAqE;AACrE,2CAA2C;AAC3C,mEAAmE;AACnE,oEAAoE;AACpE,uEAAuE;AACvE,EAAE;AACF,oDAAoD;AACpD,oEAAoE;AACpE,sEAAsE;AACtE,oEAAoE;AACpE,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE9B,yDAI8B;AAC9B,uCAAyB;AACzB,2CAA6B;AAE7B,2CAAsD;AACtD,mCAAuD;AAEvD,mEAAmE;AACnE,uEAAuE;AACvE,sEAAsE;AACtE,8BAA8B;AAC9B,MAAM,gBAAgB,GAAe;IACnC,GAAG,EAAE,0CAA0C;IAC/C,WAAW,EAAE;QACX,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,eAAe;KACzB;CACF,CAAC;AAEF,MAAM,UAAU,GAAG,0BAA0B,CAAC;AAC9C,sEAAsE;AACtE,qEAAqE;AACrE,sEAAsE;AACtE,+DAA+D;AAC/D,uDAAuD;AACvD,yEAAyE;AACzE,qEAAqE;AACrE,kEAAkE;AAClE,MAAM,YAAY,GAAG,CAAC,sBAAsB,EAAE,kBAAkB,CAAU,CAAC;AAC3E,MAAM,cAAc,GAAG,wBAAwB,CAAC;AAEhD,MAAM,iBAAiB,GAAiB,CAAC,MAAM,EAAE,EAAE;IACjD,mEAAmE;IACnE,mEAAmE;IACnE,wBAAwB;IACxB,MAAM,GAAG,IAAA,iCAAgB,EAAC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;QAC3C,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC;QAChD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,0FAA0F,CAC3F,CAAC;QACJ,CAAC;QACD,IAAA,oBAAc,EAAC,MAAM,CAAC,UAAU,EAAE,gBAAgB,EAAE;YAClD,UAAU,EAAE,SAAS;YACrB,QAAQ,EAAE,CAAC,GAAG,YAAY,CAAC;SAC5B,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,mEAAmE;IACnE,iEAAiE;IACjE,+DAA+D;IAC/D,oEAAoE;IACpE,MAAM,GAAG,IAAA,iCAAgB,EAAC,MAAM,EAAE;QAChC,KAAK;QACL,CAAC,MAAM,EAAE,EAAE;YACT,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAC3B,MAAM,CAAC,UAAU,CAAC,mBAAmB,EACrC,SAAS,CACV,CAAC;YACF,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACvD,MAAM,IAAI,GAAG,IAAA,+BAAqB,EAAC,QAAQ,EAAE;gBAC3C,MAAM,EAAE,cAAc;gBACtB,OAAO,EAAE,gBAAgB;gBACzB,SAAS,EAAE,UAAU;gBACrB,QAAQ,EAAE,CAAC,GAAG,YAAY,CAAC;aAC5B,CAAC,CAAC;YACH,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtB,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACtC,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,kBAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,iEAAiE;AACjE,0DAA0D;AAC1D,qEAAqE;AACrE,2CAA2C;AAC3C,mEAAmE;AACnE,oEAAoE;AACpE,uEAAuE;AACvE,EAAE;AACF,oDAAoD;AACpD,oEAAoE;AACpE,sEAAsE;AACtE,oEAAoE;AACpE,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE9B,yDAI8B;AAC9B,uCAAyB;AACzB,2CAA6B;AAE7B,2CAAsD;AACtD,mCAAuD;AAEvD,4DAA4D;AAC5D,uEAAuE;AACvE,+DAA+D;AAC/D,8BAA8B;AAC9B,MAAM,gBAAgB,GAAe;IACnC,GAAG,EAAE,0CAA0C;IAC/C,WAAW,EAAE;QACX,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,QAAQ;KAClB;CACF,CAAC;AAEF,MAAM,UAAU,GAAG,0BAA0B,CAAC;AAC9C,sEAAsE;AACtE,qEAAqE;AACrE,sEAAsE;AACtE,+DAA+D;AAC/D,uDAAuD;AACvD,yEAAyE;AACzE,qEAAqE;AACrE,kEAAkE;AAClE,MAAM,YAAY,GAAG,CAAC,sBAAsB,EAAE,kBAAkB,CAAU,CAAC;AAC3E,MAAM,cAAc,GAAG,wBAAwB,CAAC;AAEhD,MAAM,iBAAiB,GAAiB,CAAC,MAAM,EAAE,EAAE;IACjD,mEAAmE;IACnE,mEAAmE;IACnE,wBAAwB;IACxB,MAAM,GAAG,IAAA,iCAAgB,EAAC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;QAC3C,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC;QAChD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,0FAA0F,CAC3F,CAAC;QACJ,CAAC;QACD,IAAA,oBAAc,EAAC,MAAM,CAAC,UAAU,EAAE,gBAAgB,EAAE;YAClD,UAAU,EAAE,SAAS;YACrB,QAAQ,EAAE,CAAC,GAAG,YAAY,CAAC;SAC5B,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,mEAAmE;IACnE,iEAAiE;IACjE,+DAA+D;IAC/D,oEAAoE;IACpE,MAAM,GAAG,IAAA,iCAAgB,EAAC,MAAM,EAAE;QAChC,KAAK;QACL,CAAC,MAAM,EAAE,EAAE;YACT,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAC3B,MAAM,CAAC,UAAU,CAAC,mBAAmB,EACrC,SAAS,CACV,CAAC;YACF,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACvD,MAAM,IAAI,GAAG,IAAA,+BAAqB,EAAC,QAAQ,EAAE;gBAC3C,MAAM,EAAE,cAAc;gBACtB,OAAO,EAAE,gBAAgB;gBACzB,SAAS,EAAE,UAAU;gBACrB,QAAQ,EAAE,CAAC,GAAG,YAAY,CAAC;aAC5B,CAAC,CAAC;YACH,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtB,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACtC,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,kBAAe,iBAAiB,CAAC"}
@@ -23,15 +23,15 @@ import * as path from "path";
23
23
  import { injectPodfileSpmBlock } from "./lib/podfile";
24
24
  import { linkSpmPackage, SpmPackage } from "./lib/spm";
25
25
 
26
- // Pinned to the exact `12.0.0-beta01` release tag — `from: 12.0.0`
26
+ // Pinned to the exact `12.0.0` release tag — `from: 12.0.0`
27
27
  // would not resolve because `ios-spm` publishes pre-release tags (e.g.
28
- // `12.0.0-beta01`), and SPM excludes pre-release tags from open-ended
28
+ // `12.0.0`), and SPM excludes pre-release tags from open-ended
29
29
  // major-version requirements.
30
30
  const SMILE_ID_PACKAGE: SpmPackage = {
31
31
  url: "https://github.com/smileidentity/ios-spm",
32
32
  requirement: {
33
33
  kind: "exactVersion",
34
- version: "12.0.0-beta01",
34
+ version: "12.0.0",
35
35
  },
36
36
  };
37
37