@smileid/usesmileid_vision_face 12.0.0-beta01 → 12.0.1
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 +8 -0
- package/README.md +0 -1
- package/ios/UseSmileIDVisionFaceModule/ResizingFrameStorage.swift +136 -31
- package/ios/UseSmileIDVisionFaceModule/UseSmileIDVisionFaceModule.swift +1 -0
- package/package.json +5 -6
- package/plugin/build/index.js +3 -3
- package/plugin/build/index.js.map +1 -1
- package/plugin/src/index.ts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## 12.0.1
|
|
2
|
+
|
|
3
|
+
See the repository CHANGELOG for the 12.0.1 release notes.
|
|
4
|
+
|
|
5
|
+
## 12.0.0
|
|
6
|
+
|
|
7
|
+
First stable release of the Smile ID v12 SDK. Provides the iOS Apple Vision face detection module for `usesmileid`.
|
|
8
|
+
|
|
1
9
|
## 12.0.0-beta01
|
|
2
10
|
|
|
3
11
|
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
|
|
2
|
-
// (usesmileid_vision_face / usesmileid_vision_document)
|
|
3
|
-
//
|
|
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`
|
|
16
|
-
///
|
|
17
|
-
///
|
|
18
|
-
///
|
|
19
|
-
///
|
|
20
|
-
///
|
|
21
|
-
///
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "12.0.1",
|
|
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.
|
|
52
|
-
"@smileid/usesmileid_platform_interface": "12.0.
|
|
51
|
+
"@smileid/usesmileid_bridge": "12.0.1",
|
|
52
|
+
"@smileid/usesmileid_platform_interface": "12.0.1"
|
|
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",
|
package/plugin/build/index.js
CHANGED
|
@@ -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
|
|
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
|
|
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.
|
|
62
|
+
version: "12.0.1-SNAPSHOT.20260729180601.30478033861",
|
|
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,
|
|
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,4CAA4C;KACtD;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"}
|
package/plugin/src/index.ts
CHANGED
|
@@ -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
|
|
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
|
|
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.
|
|
34
|
+
version: "12.0.1-SNAPSHOT.20260729180601.30478033861",
|
|
35
35
|
},
|
|
36
36
|
};
|
|
37
37
|
|