@infilectorg/infiviz-shots-react-sdk 1.2.1 → 1.2.2

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.
Files changed (48) hide show
  1. package/ios/InfivizShotSDK.xcframework/Info.plist +5 -5
  2. package/ios/InfivizShotSDK.xcframework/ios-arm64/InfivizShotSDK.framework/Assets.car +0 -0
  3. package/ios/InfivizShotSDK.xcframework/ios-arm64/InfivizShotSDK.framework/InfivizShotSDK +0 -0
  4. package/ios/InfivizShotSDK.xcframework/ios-arm64/InfivizShotSDK.framework/Modules/InfivizShotSDK.swiftmodule/arm64-apple-ios.abi.json +60 -60
  5. package/ios/InfivizShotSDK.xcframework/ios-arm64/InfivizShotSDK.framework/Modules/InfivizShotSDK.swiftmodule/arm64-apple-ios.swiftmodule +0 -0
  6. package/ios/InfivizShotSDK.xcframework/ios-arm64/InfivizShotSDK.framework/shotsSDK.storyboardc/xUL-ol-gRQ-view-6A8-mO-Rhi.nib +0 -0
  7. package/ios/InfivizShotSDK.xcframework/ios-arm64_x86_64-simulator/InfivizShotSDK.framework/InfivizShotSDK +0 -0
  8. package/ios/InfivizShotSDK.xcframework/ios-arm64_x86_64-simulator/InfivizShotSDK.framework/Modules/InfivizShotSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +60 -60
  9. package/ios/InfivizShotSDK.xcframework/ios-arm64_x86_64-simulator/InfivizShotSDK.framework/Modules/InfivizShotSDK.swiftmodule/arm64-apple-ios-simulator.swiftmodule +0 -0
  10. package/ios/InfivizShotSDK.xcframework/ios-arm64_x86_64-simulator/InfivizShotSDK.framework/Modules/InfivizShotSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +60 -60
  11. package/ios/InfivizShotSDK.xcframework/ios-arm64_x86_64-simulator/InfivizShotSDK.framework/Modules/InfivizShotSDK.swiftmodule/x86_64-apple-ios-simulator.swiftmodule +0 -0
  12. package/ios/InfivizShotSDK.xcframework/ios-arm64_x86_64-simulator/InfivizShotSDK.framework/shotsSDK.storyboardc/xUL-ol-gRQ-view-6A8-mO-Rhi.nib +0 -0
  13. package/ios/InfivizShotsReactSdk.swift +587 -571
  14. package/lib/module/NativeInfivizShotsReactSdk.js +0 -2
  15. package/lib/module/NativeInfivizShotsReactSdk.js.map +1 -1
  16. package/lib/module/index.js +2 -4
  17. package/lib/module/index.js.map +1 -1
  18. package/lib/module/types/Environment.js +0 -2
  19. package/lib/module/types/Environment.js.map +1 -1
  20. package/lib/module/types/ImageMetaData.js +0 -2
  21. package/lib/module/types/ImageMetaData.js.map +1 -1
  22. package/lib/module/types/InfiSession.js +0 -2
  23. package/lib/module/types/InfiSession.js.map +1 -1
  24. package/lib/module/types/LabelScore.js +1 -1
  25. package/lib/module/types/LabelScore.js.map +1 -1
  26. package/lib/module/types/SessionStatus.js +1 -1
  27. package/lib/module/types/SessionStatus.js.map +1 -1
  28. package/lib/module/types/SessionSyncStateEnum.js +0 -2
  29. package/lib/module/types/SessionSyncStateEnum.js.map +1 -1
  30. package/package.json +1 -1
  31. package/lib/module/package.json +0 -1
  32. package/lib/typescript/package.json +0 -1
  33. package/lib/typescript/src/NativeInfivizShotsReactSdk.d.ts +0 -23
  34. package/lib/typescript/src/NativeInfivizShotsReactSdk.d.ts.map +0 -1
  35. package/lib/typescript/src/index.d.ts +0 -24
  36. package/lib/typescript/src/index.d.ts.map +0 -1
  37. package/lib/typescript/src/types/Environment.d.ts +0 -5
  38. package/lib/typescript/src/types/Environment.d.ts.map +0 -1
  39. package/lib/typescript/src/types/ImageMetaData.d.ts +0 -13
  40. package/lib/typescript/src/types/ImageMetaData.d.ts.map +0 -1
  41. package/lib/typescript/src/types/InfiSession.d.ts +0 -18
  42. package/lib/typescript/src/types/InfiSession.d.ts.map +0 -1
  43. package/lib/typescript/src/types/LabelScore.d.ts +0 -5
  44. package/lib/typescript/src/types/LabelScore.d.ts.map +0 -1
  45. package/lib/typescript/src/types/SessionStatus.d.ts +0 -5
  46. package/lib/typescript/src/types/SessionStatus.d.ts.map +0 -1
  47. package/lib/typescript/src/types/SessionSyncStateEnum.d.ts +0 -8
  48. package/lib/typescript/src/types/SessionSyncStateEnum.d.ts.map +0 -1
@@ -12,313 +12,329 @@ import React
12
12
 
13
13
  @objc(InfivizShotsReactSdk)
14
14
  class InfivizShotsReactSdk: RCTEventEmitter {
15
- @objc
16
- override static func moduleName() -> String! {
17
- return "InfivizShotsReactSdk"
18
- }
19
-
20
- @objc
21
- override static func requiresMainQueueSetup() -> Bool {
22
- return false
23
- }
24
-
25
- // MARK: – RCTEventEmitter Boilerplate
26
-
27
- private var hasListeners = false
28
-
29
- // 3. List of events that JS can subscribe to:
30
- override func supportedEvents() -> [String]! {
31
- return [
32
- "SyncEvent",
33
- "ActionsGeneratedEvent",
34
- "SessionRealTimeResultEvent"]
35
- }
36
-
37
- // 4. Only send events if JS has at least one listener:
38
- override func startObserving() {
39
- hasListeners = true
40
-
15
+ @objc
16
+ override static func moduleName() -> String! {
17
+ return "InfivizShotsReactSdk"
18
+ }
19
+
20
+ @objc
21
+ override static func requiresMainQueueSetup() -> Bool {
22
+ return false
23
+ }
24
+
25
+ // MARK: – RCTEventEmitter Boilerplate
26
+
27
+ private var hasListeners = false
28
+
29
+ // 3. List of events that JS can subscribe to:
30
+ override func supportedEvents() -> [String]! {
31
+ return [
32
+ "SyncEvent",
33
+ "ActionsGeneratedEvent",
34
+ "SessionRealTimeResultEvent"]
35
+ }
36
+
37
+ // 4. Only send events if JS has at least one listener:
38
+ override func startObserving() {
39
+ hasListeners = true
40
+
41
41
  NotificationCenter.default.addObserver(self,
42
- selector: #selector(handleActionsGeneratedEvent(_:)),
43
- name: NSNotification.Name("ActionsGeneratedEvent"),
44
- object: nil)
45
- NotificationCenter.default.addObserver(self,
46
- selector: #selector(handleSessionRealTimeResultEvent(_:)),
47
- name: NSNotification.Name("SessionRealTimeResultEvent"),
48
- object: nil)
49
-
50
- }
51
-
52
- override func stopObserving() {
53
- hasListeners = false
54
- NotificationCenter.default.removeObserver(self, name: NSNotification.Name("ActionsGeneratedEvent"), object: nil)
55
- NotificationCenter.default.removeObserver(self, name: NSNotification.Name("SessionRealTimeResultEvent"), object: nil)
56
- }
57
-
58
- // 5. If you have no constants to export, just return empty:
59
- override func constantsToExport() -> [AnyHashable: Any]! {
60
- return [:]
61
- }
62
-
63
-
64
- var activity: InfivizShotsActivity?
65
-
66
- // MARK: –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
67
- // MARK: – Helper Methods (not directly exposed to JS)
68
-
69
-
70
- // MARK: - Universal Session ID
71
- @objc
72
- func fetchUniversalSessionId(_ tempID: NSString, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
73
- InfivizShotsSDK.shared.fetchUniversalSessionId(temporarySessionID: tempID as String, completion: { sessionID, error in
74
- if let id = sessionID {
75
- resolve(id)
76
- } else {
77
- reject("FETCH_SESSION_ID_ERROR", "Session not found", nil)
78
- }
79
- })
80
- }
81
-
82
- @objc
83
- func syncSession(_ sessionId: NSString) {
84
- // Kick off the upload. No resolver/rejecter—just emit events once it’s done.
85
- InfivizShotsSDK.shared.uploadSession(temporarySessionID: sessionId as String) { result in
86
- switch result {
87
- case .success(let response):
88
- if self.hasListeners {
89
- let payload: [String: Any] = [
90
- "status": "200",
91
- "universalSessionID": response,
92
- "temporarySessionID": sessionId as String
93
- ]
94
- self.sendEvent(withName: "SyncEvent", body: payload)
95
- }
96
- case .failure(let error):
97
- if self.hasListeners {
98
- let payload: [String: Any] = [
99
- "status": "400",
100
- "message": error.localizedDescription,
101
- "temporarySessionID": sessionId as String
102
- ]
103
- self.sendEvent(withName: "SyncEvent", body: payload)
104
- }
105
- }
106
- }
107
- }
108
-
109
- // MARK: - Sync Multiple Sessions
110
- /// Bridge to InfivizShotsSDK.shared.syncSessions(temporarySessionIDs:completionHandler:)
111
-
112
- @objc
113
- func uploadSession(temporarySessionIDs: NSArray) {
42
+ selector: #selector(handleActionsGeneratedEvent(_:)),
43
+ name: NSNotification.Name("ActionsGeneratedEvent"),
44
+ object: nil)
45
+ NotificationCenter.default.addObserver(self,
46
+ selector: #selector(handleSessionRealTimeResultEvent(_:)),
47
+ name: NSNotification.Name("SessionRealTimeResultEvent"),
48
+ object: nil)
49
+
50
+ }
51
+
52
+ override func stopObserving() {
53
+ hasListeners = false
54
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name("ActionsGeneratedEvent"), object: nil)
55
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name("SessionRealTimeResultEvent"), object: nil)
56
+ }
57
+
58
+ // 5. If you have no constants to export, just return empty:
59
+ override func constantsToExport() -> [AnyHashable: Any]! {
60
+ return [:]
61
+ }
62
+
63
+
64
+ var activity: InfivizShotsActivity?
65
+
66
+ // MARK: –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
67
+ // MARK: – Helper Methods (not directly exposed to JS)
68
+
69
+
70
+ // MARK: - Universal Session ID
71
+ @objc
72
+ func fetchUniversalSessionId(_ tempID: NSString, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
73
+ InfivizShotsSDK.shared.fetchUniversalSessionId(temporarySessionID: tempID as String, completion: { sessionID, error in
74
+ if let id = sessionID {
75
+ resolve(id)
76
+ } else {
77
+ reject("FETCH_SESSION_ID_ERROR", "Session not found", nil)
78
+ }
79
+ })
80
+ }
81
+
82
+ @objc
83
+ func syncSession(_ sessionId: NSString) {
84
+ // Kick off the upload. No resolver/rejecter—just emit events once it’s done.
85
+ InfivizShotsSDK.shared.uploadSession(temporarySessionID: sessionId as String) { result in
86
+ switch result {
87
+ case .success(let response):
88
+ if self.hasListeners {
89
+ let payload: [String: Any] = [
90
+ "status": "200",
91
+ "universalSessionID": response,
92
+ "temporarySessionID": sessionId as String
93
+ ]
94
+ self.sendEvent(withName: "SyncEvent", body: payload)
95
+ }
96
+ case .failure(let error):
97
+ if self.hasListeners {
98
+ let payload: [String: Any] = [
99
+ "status": "400",
100
+ "message": error.localizedDescription,
101
+ "temporarySessionID": sessionId as String
102
+ ]
103
+ self.sendEvent(withName: "SyncEvent", body: payload)
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ // MARK: - Sync Multiple Sessions
110
+ /// Bridge to InfivizShotsSDK.shared.syncSessions(temporarySessionIDs:completionHandler:)
111
+
112
+ @objc
113
+ func uploadSession(temporarySessionIDs: NSArray) {
114
114
  var ids: [String] = []
115
115
  if let arr = temporarySessionIDs as? [String] {
116
- ids = arr
116
+ ids = arr
117
117
  } else if let arr = temporarySessionIDs as? [NSString] {
118
- ids = arr.map { $0 as String }
118
+ ids = arr.map { $0 as String }
119
119
  }
120
-
120
+
121
121
  guard !ids.isEmpty else { return }
122
-
122
+
123
123
  InfivizShotsSDK.shared.syncSessions(temporarySessionIDs: ids) { sessionId, result in
124
- switch result {
125
- case .success(let message):
126
- if self.hasListeners {
127
- let payload: [String: Any] = [
128
- "status": "200",
129
- "universalSessionID": message,
130
- "temporarySessionID": sessionId
131
- ]
132
- self.sendEvent(withName: "SyncEvent", body: payload)
133
- }
134
- case .failure(let error):
135
- if self.hasListeners {
136
- let payload: [String: Any] = [
137
- "status": "400",
138
- "universalSessionID": error,
139
- "temporarySessionID": sessionId
140
- ]
141
- self.sendEvent(withName: "SyncEvent", body: payload)
124
+ switch result {
125
+ case .success(let message):
126
+ if self.hasListeners {
127
+ let payload: [String: Any] = [
128
+ "status": "200",
129
+ "universalSessionID": message,
130
+ "temporarySessionID": sessionId
131
+ ]
132
+ self.sendEvent(withName: "SyncEvent", body: payload)
133
+ }
134
+ case .failure(let error):
135
+ if self.hasListeners {
136
+ let payload: [String: Any] = [
137
+ "status": "400",
138
+ "universalSessionID": error,
139
+ "temporarySessionID": sessionId
140
+ ]
141
+ self.sendEvent(withName: "SyncEvent", body: payload)
142
+ }
142
143
  }
143
- }
144
144
  }
145
- }
146
-
147
- // MARK: - Get Session Result
148
-
145
+ }
146
+
147
+ // MARK: - Get Session Result
148
+
149
149
  @objc
150
150
  func getSessionStatus(
151
- _ temporarySessionID: NSString,
152
- resolver resolve: @escaping RCTPromiseResolveBlock,
153
- rejecter reject: @escaping RCTPromiseRejectBlock
151
+ _ temporarySessionID: NSString,
152
+ resolver resolve: @escaping RCTPromiseResolveBlock,
153
+ rejecter reject: @escaping RCTPromiseRejectBlock
154
154
  ) {
155
- InfivizShotsSDK.shared.checkState(sessionID: temporarySessionID as String) { result in
156
- switch result {
157
- case .success(let response):
158
- let json = ["sessionId": temporarySessionID,
159
- "status": response]
160
-
161
- if let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []) {
162
- let jsonString = String(data: jsonData, encoding: .utf8)
163
- resolve(jsonString)
155
+ InfivizShotsSDK.shared.checkState(sessionID: temporarySessionID as String) { result in
156
+ switch result {
157
+ case .success(let response):
158
+ let json = ["sessionId": temporarySessionID,
159
+ "status": response]
160
+
161
+ if let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []) {
162
+ let jsonString = String(data: jsonData, encoding: .utf8)
163
+ resolve(jsonString)
164
+ }
165
+
166
+ case .failure(let error):
167
+ reject(
168
+ "RESULT_FAILED",
169
+ "Failed to get result for \(temporarySessionID): \(error.localizedDescription)",
170
+ nil
171
+ )
164
172
  }
165
-
166
- case .failure(let error):
167
- reject(
168
- "RESULT_FAILED",
169
- "Failed to get result for \(temporarySessionID): \(error.localizedDescription)",
170
- nil
171
- )
172
173
  }
173
- }
174
174
  }
175
-
176
- // MARK: - Sync All Data
177
- @objc
178
- func syncAllData(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
179
- InfivizShotsSDK.shared.syncAllData { result in
180
- switch result {
181
- case .success(let message):
182
- resolve(message)
183
- case .failure(let error):
184
- reject("SYNC_FAILED", "Sync failed: \(error)", nil)
185
- }
186
- }
187
- }
188
-
189
- // MARK: - Fetch Session Details
190
- @objc
191
- func fetchSessionDetails(_ temporarySessionID: NSString, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
192
- InfivizShotsSDK.shared.fetchSessionDetails(temporarySessionID: temporarySessionID as String) { result in
193
- switch result {
194
- case .success(let value):
195
- // Convert any value to a clean dictionary that handles Optionals properly
196
- let cleanDict = self.convertToCleanDictionary(value)
197
- // Return the dictionary directly instead of JSON string
198
- resolve(cleanDict)
199
- case .failure(let error):
200
- reject("RESULT_FAILED", "Failed to get result: \(error)", nil)
201
- }
202
- }
203
- }
204
-
205
- // Helper method to convert any value to a clean dictionary that handles Optionals
206
- private func convertToCleanDictionary(_ value: Any) -> [String: Any] {
207
- if let dict = value as? [String: Any] {
208
- return cleanDictionary(dict)
209
- } else {
210
- // Try to convert to dictionary using Mirror reflection
211
- let mirror = Mirror(reflecting: value)
212
- var result: [String: Any] = [:]
213
-
214
- for child in mirror.children {
215
- if let label = child.label {
216
- result[label] = unwrapOptional(child.value)
217
- }
218
- }
219
- return result
220
- }
221
- }
222
-
223
- // Recursively clean a dictionary to handle Optionals
224
- private func cleanDictionary(_ dict: [String: Any]) -> [String: Any] {
225
- var cleaned: [String: Any] = [:]
226
-
227
- for (key, value) in dict {
228
- cleaned[key] = unwrapOptional(value)
229
- }
230
-
231
- return cleaned
232
- }
233
-
234
- // Unwrap Optional values and clean nested structures
235
- private func unwrapOptional(_ value: Any) -> Any {
236
- let mirror = Mirror(reflecting: value)
237
-
238
- // Check if it's an Optional by checking the type name
239
- let typeName = String(describing: type(of: value))
240
- if typeName.hasPrefix("Optional<") {
241
- if let unwrapped = mirror.children.first?.value {
242
- return unwrapOptional(unwrapped)
243
- } else {
244
- return NSNull() // nil value
245
- }
246
- }
247
-
248
- // Handle arrays
249
- if let array = value as? [Any] {
250
- return array.map { unwrapOptional($0) }
251
- }
252
-
253
- // Handle dictionaries
254
- if let dict = value as? [String: Any] {
255
- return cleanDictionary(dict)
256
- }
257
-
258
- // Handle Date objects
259
- if let date = value as? Date {
260
- let formatter = DateFormatter()
261
- formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
262
- return formatter.string(from: date)
263
- }
264
-
265
- // Handle custom objects (like ImageMetaData) using Mirror reflection
266
- // This ensures that objects with optional properties are properly serialized
267
- let objectMirror = Mirror(reflecting: value)
268
- if !objectMirror.children.isEmpty {
269
- var result: [String: Any] = [:]
270
- for child in objectMirror.children {
271
- if let label = child.label {
272
- result[label] = unwrapOptional(child.value)
273
- }
274
- }
275
- return result
276
- }
277
-
278
- // Return the value as-is if it's a basic type
279
- return value
280
- }
281
-
282
- // MARK: - Logout
283
- @objc
284
- func logout(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
285
- InfivizShotsSDK.shared.callLogoutAPI { result in
286
- switch result {
287
- case .success(let success):
288
- resolve(success)
289
- case .failure(let error):
290
- reject("LOGOUT_FAILED", "Logout error: \(error)", nil)
291
- }
292
- }
293
- }
294
-
295
- // MARK: - Reprocessing Enable Time
296
- @objc
297
- func getReprocessingButtonEnableTime(_ sessionID: NSString, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
298
- InfivizShotsSDK.shared.getReprocessingButtonEnableTime(sessionID: sessionID as String) { result in
299
- switch result {
300
- case .success(let time):
301
- resolve(time)
302
- case .failure(let error):
303
- reject("REPROCESS_TIME_ERROR", "Error fetching reprocessing time: \(error)", nil)
304
- }
305
- }
306
- }
307
-
308
- // MARK: - Start Reprocessing Workflow
309
- @objc
310
- func startReprocessingWorkflow(_ sessionID: NSString, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
311
- InfivizShotsSDK.shared.startReprocessingWorkflow(sessionID: sessionID as String) { result in
312
- switch result {
313
- case .success(let time):
314
- resolve(time)
315
- case .failure(let error):
316
- reject("REPROCESS_WORKFLOW_ERROR", "Reprocessing workflow error: \(error)", nil)
317
- }
318
- }
319
- }
320
-
321
- // MARK: - Start Camera
175
+
176
+ // MARK: - Sync All Data
177
+ @objc
178
+ func syncAllData(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
179
+ InfivizShotsSDK.shared.syncAllData { result in
180
+ switch result {
181
+ case .success(let message):
182
+ resolve(message)
183
+ case .failure(let error):
184
+ reject("SYNC_FAILED", "Sync failed: \(error)", nil)
185
+ }
186
+ }
187
+ }
188
+
189
+ // MARK: - Fetch Session Details
190
+ @objc
191
+ func fetchSessionDetails(_ temporarySessionID: NSString, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
192
+ InfivizShotsSDK.shared.fetchSessionDetails(temporarySessionID: temporarySessionID as String) { result in
193
+ switch result {
194
+ case .success(let value):
195
+ // Convert any value to a clean dictionary that handles Optionals properly
196
+ let cleanDict = self.convertToCleanDictionary(value)
197
+ // Return the dictionary directly instead of JSON string
198
+ resolve(cleanDict)
199
+ case .failure(let error):
200
+ reject("RESULT_FAILED", "Failed to get result: \(error)", nil)
201
+ }
202
+ }
203
+ }
204
+
205
+ // Helper method to convert any value to a clean dictionary that handles Optionals
206
+ // MARK: - Convert Any Value to Clean Dictionary
207
+ private func convertToCleanDictionary(_ value: Any) -> [String: Any] {
208
+ if let dict = value as? [String: Any] {
209
+ return cleanDictionary(dict)
210
+ } else if let dict = value as? [AnyHashable: Any] {
211
+ var result: [String: Any] = [:]
212
+ for (k, v) in dict {
213
+ result[String(describing: k)] = unwrapOptional(v)
214
+ }
215
+ return result
216
+ } else {
217
+ // Try to convert to dictionary using Mirror reflection
218
+ let mirror = Mirror(reflecting: value)
219
+ var result: [String: Any] = [:]
220
+ for child in mirror.children {
221
+ if let label = child.label {
222
+ result[label] = unwrapOptional(child.value)
223
+ }
224
+ }
225
+ return result
226
+ }
227
+ }
228
+
229
+ // MARK: - Recursively Clean Dictionary
230
+ private func cleanDictionary(_ dict: [String: Any]) -> [String: Any] {
231
+ var cleaned: [String: Any] = [:]
232
+ for (key, value) in dict {
233
+ cleaned[key] = unwrapOptional(value)
234
+ }
235
+ return cleaned
236
+ }
237
+
238
+ // MARK: - Unwrap Optionals and Clean Nested Structures
239
+ private func unwrapOptional(_ value: Any) -> Any {
240
+ let mirror = Mirror(reflecting: value)
241
+
242
+ // 1. Check if it's an Optional
243
+ let typeName = String(describing: type(of: value))
244
+ if typeName.hasPrefix("Optional<") {
245
+ if let unwrapped = mirror.children.first?.value {
246
+ return unwrapOptional(unwrapped)
247
+ } else {
248
+ return NSNull() // nil value
249
+ }
250
+ }
251
+
252
+ // 2. Handle String cleanup: remove "Optional(...)"
253
+ if let str = value as? String {
254
+ return str.replacingOccurrences(of: "Optional\\(|\\)",
255
+ with: "",
256
+ options: .regularExpression)
257
+ }
258
+
259
+ // 3. Handle Arrays
260
+ if let array = value as? [Any] {
261
+ return array.map { unwrapOptional($0) }
262
+ }
263
+
264
+ // 4. Handle Dictionaries
265
+ if let dict = value as? [String: Any] {
266
+ return cleanDictionary(dict)
267
+ } else if let dict = value as? [AnyHashable: Any] {
268
+ var result: [String: Any] = [:]
269
+ for (k, v) in dict {
270
+ result[String(describing: k)] = unwrapOptional(v)
271
+ }
272
+ return result
273
+ }
274
+
275
+ // 5. Handle Date objects
276
+ if let date = value as? Date {
277
+ let formatter = DateFormatter()
278
+ formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
279
+ return formatter.string(from: date)
280
+ }
281
+
282
+ // 6. Handle custom objects via Mirror reflection
283
+ let objectMirror = Mirror(reflecting: value)
284
+ if !objectMirror.children.isEmpty {
285
+ var result: [String: Any] = [:]
286
+ for child in objectMirror.children {
287
+ if let label = child.label {
288
+ result[label] = unwrapOptional(child.value)
289
+ }
290
+ }
291
+ return result
292
+ }
293
+
294
+ // 7. Return primitive types as-is
295
+ return value
296
+ }
297
+
298
+ // MARK: - Logout
299
+ @objc
300
+ func logout(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
301
+ InfivizShotsSDK.shared.callLogoutAPI { result in
302
+ switch result {
303
+ case .success(let success):
304
+ resolve(success)
305
+ case .failure(let error):
306
+ reject("LOGOUT_FAILED", "Logout error: \(error)", nil)
307
+ }
308
+ }
309
+ }
310
+
311
+ // MARK: - Reprocessing Enable Time
312
+ @objc
313
+ func getReprocessingButtonEnableTime(_ sessionID: NSString, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
314
+ InfivizShotsSDK.shared.getReprocessingButtonEnableTime(sessionID: sessionID as String) { result in
315
+ switch result {
316
+ case .success(let time):
317
+ resolve(time)
318
+ case .failure(let error):
319
+ reject("REPROCESS_TIME_ERROR", "Error fetching reprocessing time: \(error)", nil)
320
+ }
321
+ }
322
+ }
323
+
324
+ // MARK: - Start Reprocessing Workflow
325
+ @objc
326
+ func startReprocessingWorkflow(_ sessionID: NSString, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
327
+ InfivizShotsSDK.shared.startReprocessingWorkflow(sessionID: sessionID as String) { result in
328
+ switch result {
329
+ case .success(let time):
330
+ resolve(time)
331
+ case .failure(let error):
332
+ reject("REPROCESS_WORKFLOW_ERROR", "Reprocessing workflow error: \(error)", nil)
333
+ }
334
+ }
335
+ }
336
+
337
+ // MARK: - Start Camera
322
338
  @objc(startCamera:storeTitle:storeId:photoType:category:maxPhoto:maxPhotoOneGo:metaData:resolver:rejecter:)
323
339
  func startCamera(_ taskName: NSString,
324
340
  storeTitle: NSString,
@@ -330,229 +346,229 @@ class InfivizShotsReactSdk: RCTEventEmitter {
330
346
  metaData: NSDictionary,
331
347
  resolver resolve: @escaping RCTPromiseResolveBlock,
332
348
  rejecter reject: @escaping RCTPromiseRejectBlock) {
333
-
334
- DispatchQueue.main.async {
335
- // 1️⃣ Build the CameraContext just like before
336
- let metadataDict: [String: Any]? = metaData as? [String: Any]
337
- let context = CameraContext(
338
- taskName: taskName as String,
339
- storeTitle: storeTitle as String,
340
- storeId: storeId as String,
341
- photoType: photoType as String,
342
- category: category as String,
343
- maxPhoto: maxPhoto.intValue,
344
- maxPhotoOneGo: maxPhotoOneGo.intValue,
345
- tempSessionId: nil,
346
- imageURI: nil,
347
- navigationController: nil,
348
- retake: false,
349
- metaData: metadataDict
349
+
350
+ DispatchQueue.main.async {
351
+ // 1️⃣ Build the CameraContext just like before
352
+ let metadataDict: [String: Any]? = metaData as? [String: Any]
353
+ let context = CameraContext(
354
+ taskName: taskName as String,
355
+ storeTitle: storeTitle as String,
356
+ storeId: storeId as String,
357
+ photoType: photoType as String,
358
+ category: category as String,
359
+ maxPhoto: maxPhoto.intValue,
360
+ maxPhotoOneGo: maxPhotoOneGo.intValue,
361
+ tempSessionId: nil,
362
+ imageURI: nil,
363
+ navigationController: nil,
364
+ retake: false,
365
+ metaData: metadataDict
366
+ )
367
+
368
+ // 2️⃣ Instantiate the SDK activity
369
+ let activity = InfilectSDKCameraActivityForResult(cameraContext: context)
370
+
371
+ // 3️⃣ Call its existing `start(completion:)` which will handle validation,
372
+ activity.start { result in
373
+ switch result {
374
+ case .success(let sessionID):
375
+ resolve(sessionID)
376
+ case .failure(let apiError):
377
+ reject("CAMERA_ERROR", apiError.localizedDescription, nil)
378
+ }
379
+ }
380
+ }
381
+ }
382
+
383
+ @objc
384
+ func initialize(_ appID: NSString,
385
+ authSecret: NSString,
386
+ accountKey: NSString,
387
+ userID: NSString,
388
+ clientID: NSString,
389
+ apiToken: NSString,
390
+ env: NSString,
391
+ resolver resolve: @escaping RCTPromiseResolveBlock,
392
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
393
+
394
+ let model = InfivizUserLoginContext(
395
+ appID: appID as String,
396
+ authSecret: authSecret as String,
397
+ accountKey: accountKey as String,
398
+ userID: userID as String,
399
+ clientID: clientID as String,
400
+ apiToken: apiToken as String
350
401
  )
351
-
352
- // 2️⃣ Instantiate the SDK activity
353
- let activity = InfilectSDKCameraActivityForResult(cameraContext: context)
354
-
355
- // 3️⃣ Call its existing `start(completion:)` which will handle validation,
356
- activity.start { result in
357
- switch result {
358
- case .success(let sessionID):
359
- resolve(sessionID)
360
- case .failure(let apiError):
361
- reject("CAMERA_ERROR", apiError.localizedDescription, nil)
362
- }
402
+
403
+ let environment = APIEnvironment(rawValue: env as String) ?? .production
404
+ InfivizShotsSDK.shared.initializeEnvironent(environment)
405
+ activity = InfivizShotsActivity(model: model)
406
+ activity?.initialize { result in
407
+ switch result {
408
+ case .success(let success):
409
+ resolve(success)
410
+ case .failure(let error):
411
+ reject("LOGIN_FAILED", error.localizedDescription, nil)
412
+ }
363
413
  }
364
- }
365
414
  }
366
-
367
- @objc
368
- func initialize(_ appID: NSString,
369
- authSecret: NSString,
370
- accountKey: NSString,
371
- userID: NSString,
372
- clientID: NSString,
373
- apiToken: NSString,
374
- env: NSString,
415
+
416
+ @objc
417
+ func fetchSessionID(_ taskCategory: NSString,
418
+ storeID: NSString,
419
+ taskPhotoType: NSString,
420
+ resolver resolve: @escaping RCTPromiseResolveBlock,
421
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
422
+
423
+ InfivizShotsSDK.shared.fetchSessionID(
424
+ taskCategory: taskCategory as String,
425
+ storeID: storeID as String,
426
+ taskPhotoType: taskPhotoType as String
427
+ ) { sessionID, error in
428
+ if let id = sessionID {
429
+ resolve(id)
430
+ } else {
431
+ reject("SESSION_NOT_FOUND", "Session not found", nil)
432
+ }
433
+ }
434
+ }
435
+
436
+ // MARK: - View IR Results via bridge
437
+ @objc(viewIRResults:resolver:rejecter:)
438
+ func viewIRResults(_ sessionID: NSString,
375
439
  resolver resolve: @escaping RCTPromiseResolveBlock,
376
- rejecter reject: @escaping RCTPromiseRejectBlock) {
377
-
378
- let model = InfivizUserLoginContext(
379
- appID: appID as String,
380
- authSecret: authSecret as String,
381
- accountKey: accountKey as String,
382
- userID: userID as String,
383
- clientID: clientID as String,
384
- apiToken: apiToken as String
385
- )
386
-
387
- let environment = APIEnvironment(rawValue: env as String) ?? .production
388
- InfivizShotsSDK.shared.initializeEnvironent(environment)
389
- activity = InfivizShotsActivity(model: model)
390
- activity?.initialize { result in
391
- switch result {
392
- case .success(let success):
393
- resolve(success)
394
- case .failure(let error):
395
- reject("LOGIN_FAILED", error.localizedDescription, nil)
396
- }
397
- }
398
- }
399
-
400
- @objc
401
- func fetchSessionID(_ taskCategory: NSString,
402
- storeID: NSString,
403
- taskPhotoType: NSString,
404
- resolver resolve: @escaping RCTPromiseResolveBlock,
405
- rejecter reject: @escaping RCTPromiseRejectBlock) {
406
-
407
- InfivizShotsSDK.shared.fetchSessionID(
408
- taskCategory: taskCategory as String,
409
- storeID: storeID as String,
410
- taskPhotoType: taskPhotoType as String
411
- ) { sessionID, error in
412
- if let id = sessionID {
413
- resolve(id)
414
- } else {
415
- reject("SESSION_NOT_FOUND", "Session not found", nil)
416
- }
417
- }
418
- }
419
-
420
- // MARK: - View IR Results via bridge
421
- @objc(viewIRResults:resolver:rejecter:)
422
- func viewIRResults(_ sessionID: NSString,
423
- resolver resolve: @escaping RCTPromiseResolveBlock,
424
- rejecter reject: @escaping RCTPromiseRejectBlock) {
425
- DispatchQueue.main.async {
426
- guard let navigationController = UIApplication.shared.connectedScenes
427
- .compactMap({ $0 as? UIWindowScene })
428
- .flatMap({ $0.windows })
429
- .first(where: { $0.isKeyWindow })?
430
- .rootViewController?.navigationController else {
431
- reject("NAV_ERROR", "No navigation controller available", nil)
432
- return
433
- }
434
- let realTimeView = InfivizRealTimeView(sessionID: sessionID as String,
435
- navigationController: navigationController)
436
- realTimeView.viewIRResults(sessionID: sessionID as String) { result in
437
- switch result {
438
- case .success(let value): resolve(value)
439
- case .failure(let error): reject("VIEW_IR_FAILED", error.localizedDescription, nil)
440
- }
441
- }
442
- }
443
- }
444
-
445
- // MARK: - Start Actions Workflow via bridge
446
- @objc(startActionsWorkFlow:sessionId:latitude:longitude:dateTimeStamp:resolver:rejecter:)
447
- func startActionsWorkFlow(_ storeId: NSString,
448
- sessionId: NSString,
449
- latitude: NSString,
450
- longitude: NSString,
451
- dateTimeStamp: NSString,
452
- resolver resolve: @escaping RCTPromiseResolveBlock,
453
- rejecter reject: @escaping RCTPromiseRejectBlock) {
454
- DispatchQueue.main.async {
455
- guard let navigationController = UIApplication.shared.connectedScenes
456
- .compactMap({ $0 as? UIWindowScene })
457
- .flatMap({ $0.windows })
458
- .first(where: { $0.isKeyWindow })?
459
- .rootViewController?.navigationController else {
460
- reject("NAV_ERROR", "No navigation controller available", nil)
461
- return
462
- }
463
- let realTimeView = InfivizRealTimeView(sessionID: sessionId as String,
464
- navigationController: navigationController)
465
- realTimeView.startActionsWorkFlow(storeId: storeId as String,
466
- sessionId: sessionId as String,
467
- latitude: latitude as String,
468
- longitude: longitude as String,
469
- dateTimeStamp: dateTimeStamp as String) { result in
470
- switch result {
471
- case .success(let value): resolve(value)
472
- case .failure(let error): reject("ACTIONS_WORKFLOW_FAILED", error.localizedDescription, nil)
473
- }
474
- }
475
- }
476
- }
477
-
478
- @objc(resumeCamera:metaData:resolver:rejecter:)
440
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
441
+ DispatchQueue.main.async {
442
+ guard let navigationController = UIApplication.shared.connectedScenes
443
+ .compactMap({ $0 as? UIWindowScene })
444
+ .flatMap({ $0.windows })
445
+ .first(where: { $0.isKeyWindow })?
446
+ .rootViewController?.navigationController else {
447
+ reject("NAV_ERROR", "No navigation controller available", nil)
448
+ return
449
+ }
450
+ let realTimeView = InfivizRealTimeView(sessionID: sessionID as String,
451
+ navigationController: navigationController)
452
+ realTimeView.viewIRResults(sessionID: sessionID as String) { result in
453
+ switch result {
454
+ case .success(let value): resolve(value)
455
+ case .failure(let error): reject("VIEW_IR_FAILED", error.localizedDescription, nil)
456
+ }
457
+ }
458
+ }
459
+ }
460
+
461
+ // MARK: - Start Actions Workflow via bridge
462
+ @objc(startActionsWorkFlow:sessionId:latitude:longitude:dateTimeStamp:resolver:rejecter:)
463
+ func startActionsWorkFlow(_ storeId: NSString,
464
+ sessionId: NSString,
465
+ latitude: NSString,
466
+ longitude: NSString,
467
+ dateTimeStamp: NSString,
468
+ resolver resolve: @escaping RCTPromiseResolveBlock,
469
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
470
+ DispatchQueue.main.async {
471
+ guard let navigationController = UIApplication.shared.connectedScenes
472
+ .compactMap({ $0 as? UIWindowScene })
473
+ .flatMap({ $0.windows })
474
+ .first(where: { $0.isKeyWindow })?
475
+ .rootViewController?.navigationController else {
476
+ reject("NAV_ERROR", "No navigation controller available", nil)
477
+ return
478
+ }
479
+ let realTimeView = InfivizRealTimeView(sessionID: sessionId as String,
480
+ navigationController: navigationController)
481
+ realTimeView.startActionsWorkFlow(storeId: storeId as String,
482
+ sessionId: sessionId as String,
483
+ latitude: latitude as String,
484
+ longitude: longitude as String,
485
+ dateTimeStamp: dateTimeStamp as String) { result in
486
+ switch result {
487
+ case .success(let value): resolve(value)
488
+ case .failure(let error): reject("ACTIONS_WORKFLOW_FAILED", error.localizedDescription, nil)
489
+ }
490
+ }
491
+ }
492
+ }
493
+
494
+ @objc(resumeCamera:metaData:resolver:rejecter:)
479
495
  func resumeCamera(
480
- _ temporarySessionID: NSString,
481
- metaData: NSDictionary?,
482
- resolver resolve: @escaping RCTPromiseResolveBlock,
483
- rejecter reject: @escaping RCTPromiseRejectBlock
496
+ _ temporarySessionID: NSString,
497
+ metaData: NSDictionary?,
498
+ resolver resolve: @escaping RCTPromiseResolveBlock,
499
+ rejecter reject: @escaping RCTPromiseRejectBlock
484
500
  ) {
485
- // Build whatever CameraContext your SDK needs. In your original UI code,
486
- // you were reading from text fields (e.g. shelfTextField.text, traderTextField.text, etc.).
487
- // In a module method you typically won't have those outlets available, so you'll
488
- // need to pass any required "taskName", "storeTitle" etc. from JS into this call
489
- // or hard‐code defaults here. For now, I'll show a minimal example:
490
-
491
- let context = CameraContext(
492
- taskName: "", // ← pass in from JS if you really need it
493
- storeTitle: "",
494
- storeId: "",
495
- photoType: "",
496
- category: "",
497
- maxPhoto: 0,
498
- maxPhotoOneGo: 0,
499
- navigationController: nil // ← no UIKit nav controller in RN module
500
- )
501
-
502
- // Instantiate your SDK's camera controller just like before:
503
- let controller = InfilectSDKCameraActivityForResult(cameraContext: context)
504
-
505
- // Convert NSDictionary to [String: Any]
506
- let metadataDict: [String: Any]? = metaData as? [String: Any]
507
-
508
- // Call the updated `resumeCamera(sessionID:metaData:completion:)` API with metadata
509
- controller.resumeCamera(sessionID: temporarySessionID as String, metaData: metadataDict) { result in
510
- switch result {
511
- case .success(let successString):
512
- // fulfill the JS promise with the returned string
513
- resolve(successString)
514
-
515
- case .failure(let sdkError):
516
- // reject the JS promise
517
- let nsError = sdkError as NSError
518
- reject(
519
- "ResumeCameraError",
520
- "Failed to resume camera: \(nsError.localizedDescription)",
521
- nsError
522
- )
501
+ // Build whatever CameraContext your SDK needs. In your original UI code,
502
+ // you were reading from text fields (e.g. shelfTextField.text, traderTextField.text, etc.).
503
+ // In a module method you typically won't have those outlets available, so you'll
504
+ // need to pass any required "taskName", "storeTitle" etc. from JS into this call
505
+ // or hard‐code defaults here. For now, I'll show a minimal example:
506
+
507
+ let context = CameraContext(
508
+ taskName: "", // ← pass in from JS if you really need it
509
+ storeTitle: "",
510
+ storeId: "",
511
+ photoType: "",
512
+ category: "",
513
+ maxPhoto: 0,
514
+ maxPhotoOneGo: 0,
515
+ navigationController: nil // ← no UIKit nav controller in RN module
516
+ )
517
+
518
+ // Instantiate your SDK's camera controller just like before:
519
+ let controller = InfilectSDKCameraActivityForResult(cameraContext: context)
520
+
521
+ // Convert NSDictionary to [String: Any]
522
+ let metadataDict: [String: Any]? = metaData as? [String: Any]
523
+
524
+ // Call the updated `resumeCamera(sessionID:metaData:completion:)` API with metadata
525
+ controller.resumeCamera(sessionID: temporarySessionID as String, metaData: metadataDict) { result in
526
+ switch result {
527
+ case .success(let successString):
528
+ // fulfill the JS promise with the returned string
529
+ resolve(successString)
530
+
531
+ case .failure(let sdkError):
532
+ // reject the JS promise
533
+ let nsError = sdkError as NSError
534
+ reject(
535
+ "ResumeCameraError",
536
+ "Failed to resume camera: \(nsError.localizedDescription)",
537
+ nsError
538
+ )
539
+ }
523
540
  }
524
- }
525
541
  }
526
-
527
- @objc
528
- func deletePhoto(
529
- _ imageURI: NSString,
530
- temporarySessionId: NSString,
531
- resolver resolve: @escaping RCTPromiseResolveBlock,
532
- rejecter reject: @escaping RCTPromiseRejectBlock
533
- ) {
534
- let context = CameraContext(
535
- taskName: "",
536
- storeTitle: "",
537
- storeId: "",
538
- photoType: "",
539
-
540
- category: "",
541
- maxPhoto: 0,
542
- maxPhotoOneGo: 0,
543
- navigationController: nil
544
- )
545
- let controller = InfilectSDKCameraActivityForResult(cameraContext: context)
546
- controller.deleteImage(imageID: imageURI as String, sessionID: temporarySessionId as String) { result in
547
- switch result {
548
- case .success(let successMessage):
549
- resolve(successMessage)
550
- case .failure(let error):
551
- reject("DELETE_IMAGE_ERROR", "Failed to delete image: \(error.localizedDescription)", error)
552
- }
553
- }
554
- }
555
-
542
+
543
+ @objc
544
+ func deletePhoto(
545
+ _ imageURI: NSString,
546
+ temporarySessionId: NSString,
547
+ resolver resolve: @escaping RCTPromiseResolveBlock,
548
+ rejecter reject: @escaping RCTPromiseRejectBlock
549
+ ) {
550
+ let context = CameraContext(
551
+ taskName: "",
552
+ storeTitle: "",
553
+ storeId: "",
554
+ photoType: "",
555
+
556
+ category: "",
557
+ maxPhoto: 0,
558
+ maxPhotoOneGo: 0,
559
+ navigationController: nil
560
+ )
561
+ let controller = InfilectSDKCameraActivityForResult(cameraContext: context)
562
+ controller.deleteImage(imageID: imageURI as String, sessionID: temporarySessionId as String) { result in
563
+ switch result {
564
+ case .success(let successMessage):
565
+ resolve(successMessage)
566
+ case .failure(let error):
567
+ reject("DELETE_IMAGE_ERROR", "Failed to delete image: \(error.localizedDescription)", error)
568
+ }
569
+ }
570
+ }
571
+
556
572
  @objc(retakeImage:imageURL:metaData:resolver:rejecter:)
557
573
  func retakeImage(
558
574
  _ temporarySessionId: NSString,
@@ -593,70 +609,70 @@ class InfivizShotsReactSdk: RCTEventEmitter {
593
609
  }
594
610
  }
595
611
  }
596
-
597
- // MARK: – React Native Promise Exposures for OutputAPI
598
- /// Expose `getIRResult(sessionID:completion:)` to JavaScript as:
599
- /// InfiBridge.getIRResult("session-123").then(...).catch(...)
600
- @objc
601
- func getIrResult(
602
- _ sessionId: NSString,
603
- resolver resolve: @escaping RCTPromiseResolveBlock,
604
- rejecter reject: @escaping RCTPromiseRejectBlock
605
- ) {
606
- print("Inside GetIrResult sessionId -> \(sessionId)")
607
- // Call the Swift helper (which calls outputAPI.getIRResult)
608
- InfivizShotsSDK.shared.getIrResult(sessionID: sessionId as String) { (dataAny, error) in
609
- if let err = error {
610
- // If the SDK returned an error, reject the JS promise
611
- print("err", err)
612
- reject(
613
- "IR_RESULT_ERROR", // error code (custom identifier)
614
- err as? String, // error message
615
- nil // optional NSError (can be nil)
616
- )
617
- } else if let output = dataAny {
618
- // If the SDK returned data (Any), pass it back to JS.
619
- resolve(output)
620
- } else {
621
- // Neither data nor error? Just resolve with `nil`.
622
- resolve(nil)
623
- }
624
- }
625
- }
626
-
627
- /// Expose `getActionsResult(sessionID:completion:)` to JavaScript as:
628
- /// InfiBridge.getActionsResult("session-123").then(...).catch(...)
629
- @objc
630
- func getActionsResult(
631
- _ sessionId: NSString,
632
- resolver resolve: @escaping RCTPromiseResolveBlock,
633
- rejecter reject: @escaping RCTPromiseRejectBlock
634
- ) {
635
- // Call the Swift helper (which calls outputAPI.getActionsResult)
636
- InfivizShotsSDK.shared.getActionsResult(sessionID: sessionId as String) { (dataAny, error) in
637
- if let err = error {
638
- reject("ACTIONS_FAILED", err as? String, nil)
639
- } else if let output = dataAny {
640
- resolve(output)
641
- } else {
642
- resolve(nil)
643
- }
644
- }
645
- }
646
-
647
- @objc
648
- private func handleActionsGeneratedEvent(_ notification: Notification) {
649
- guard hasListeners else { return }
650
-
651
- let payload = notification.userInfo ?? [:]
652
- sendEvent(withName: "ActionsGeneratedEvent", body: payload)
612
+
613
+ // MARK: – React Native Promise Exposures for OutputAPI
614
+ /// Expose `getIRResult(sessionID:completion:)` to JavaScript as:
615
+ /// InfiBridge.getIRResult("session-123").then(...).catch(...)
616
+ @objc
617
+ func getIrResult(
618
+ _ sessionId: NSString,
619
+ resolver resolve: @escaping RCTPromiseResolveBlock,
620
+ rejecter reject: @escaping RCTPromiseRejectBlock
621
+ ) {
622
+ print("Inside GetIrResult sessionId -> \(sessionId)")
623
+ // Call the Swift helper (which calls outputAPI.getIRResult)
624
+ InfivizShotsSDK.shared.getIrResult(sessionID: sessionId as String) { (dataAny, error) in
625
+ if let err = error {
626
+ // If the SDK returned an error, reject the JS promise
627
+ print("err", err)
628
+ reject(
629
+ "IR_RESULT_ERROR", // error code (custom identifier)
630
+ err as? String, // error message
631
+ nil // optional NSError (can be nil)
632
+ )
633
+ } else if let output = dataAny {
634
+ // If the SDK returned data (Any), pass it back to JS.
635
+ resolve(output)
636
+ } else {
637
+ // Neither data nor error? Just resolve with `nil`.
638
+ resolve(nil)
639
+ }
653
640
  }
654
-
655
- @objc
656
- private func handleSessionRealTimeResultEvent(_ notification: Notification) {
657
- guard hasListeners else { return }
658
-
659
- let payload = notification.userInfo ?? [:]
660
- sendEvent(withName: "SessionRealTimeResultEvent", body: payload)
641
+ }
642
+
643
+ /// Expose `getActionsResult(sessionID:completion:)` to JavaScript as:
644
+ /// InfiBridge.getActionsResult("session-123").then(...).catch(...)
645
+ @objc
646
+ func getActionsResult(
647
+ _ sessionId: NSString,
648
+ resolver resolve: @escaping RCTPromiseResolveBlock,
649
+ rejecter reject: @escaping RCTPromiseRejectBlock
650
+ ) {
651
+ // Call the Swift helper (which calls outputAPI.getActionsResult)
652
+ InfivizShotsSDK.shared.getActionsResult(sessionID: sessionId as String) { (dataAny, error) in
653
+ if let err = error {
654
+ reject("ACTIONS_FAILED", err as? String, nil)
655
+ } else if let output = dataAny {
656
+ resolve(output)
657
+ } else {
658
+ resolve(nil)
659
+ }
661
660
  }
661
+ }
662
+
663
+ @objc
664
+ private func handleActionsGeneratedEvent(_ notification: Notification) {
665
+ guard hasListeners else { return }
666
+
667
+ let payload = notification.userInfo ?? [:]
668
+ sendEvent(withName: "ActionsGeneratedEvent", body: payload)
669
+ }
670
+
671
+ @objc
672
+ private func handleSessionRealTimeResultEvent(_ notification: Notification) {
673
+ guard hasListeners else { return }
674
+
675
+ let payload = notification.userInfo ?? [:]
676
+ sendEvent(withName: "SessionRealTimeResultEvent", body: payload)
677
+ }
662
678
  }