rn-linkrunner 2.5.2 → 2.6.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.
Files changed (21) hide show
  1. package/LinkrunnerSDK.podspec +1 -1
  2. package/ios/Podfile +1 -1
  3. package/ios/Pods/LinkrunnerKit/LICENSE +21 -0
  4. package/ios/Pods/LinkrunnerKit/README.md +15 -0
  5. package/ios/Pods/LinkrunnerKit/Sources/Linkrunner/HmacSignatureGenerator.swift +95 -0
  6. package/ios/Pods/LinkrunnerKit/Sources/Linkrunner/Linkrunner.swift +1109 -0
  7. package/ios/Pods/LinkrunnerKit/Sources/Linkrunner/Models.swift +356 -0
  8. package/ios/Pods/LinkrunnerKit/Sources/Linkrunner/RequestSigningInterceptor.swift +110 -0
  9. package/ios/Pods/LinkrunnerKit/Sources/Linkrunner/SHA256.swift +12 -0
  10. package/ios/Pods/LinkrunnerKit/Sources/Linkrunner/SKAdNetworkService.swift +428 -0
  11. package/ios/Pods/Pods.xcodeproj/project.pbxproj +440 -0
  12. package/ios/Pods/Pods.xcodeproj/xcuserdata/shofiyabootwala.xcuserdatad/xcschemes/LinkrunnerKit.xcscheme +58 -0
  13. package/ios/Pods/Pods.xcodeproj/xcuserdata/shofiyabootwala.xcuserdatad/xcschemes/xcschememanagement.plist +16 -0
  14. package/ios/Pods/Target Support Files/LinkrunnerKit/LinkrunnerKit-Info.plist +26 -0
  15. package/ios/Pods/Target Support Files/LinkrunnerKit/LinkrunnerKit-dummy.m +5 -0
  16. package/ios/Pods/Target Support Files/LinkrunnerKit/LinkrunnerKit-prefix.pch +12 -0
  17. package/ios/Pods/Target Support Files/LinkrunnerKit/LinkrunnerKit-umbrella.h +16 -0
  18. package/ios/Pods/Target Support Files/LinkrunnerKit/LinkrunnerKit.debug.xcconfig +17 -0
  19. package/ios/Pods/Target Support Files/LinkrunnerKit/LinkrunnerKit.modulemap +6 -0
  20. package/ios/Pods/Target Support Files/LinkrunnerKit/LinkrunnerKit.release.xcconfig +17 -0
  21. package/package.json +1 -1
@@ -0,0 +1,428 @@
1
+ import Foundation
2
+ import StoreKit
3
+
4
+ // Import AdAttributionKit for iOS 17.4+
5
+ #if canImport(AdAttributionKit)
6
+ import AdAttributionKit
7
+ #endif
8
+
9
+ /// Attribution service that handles both modern AdAttributionKit (iOS 17.4+) and legacy SKAdNetwork (iOS 14.0+)
10
+ /// Automatically selects the appropriate API based on iOS version availability
11
+ @available(iOS 14.0, *)
12
+ public class SKAdNetworkService {
13
+ public static let shared = SKAdNetworkService()
14
+
15
+ // Thread safety
16
+ private let serialQueue = DispatchQueue(label: "com.linkrunner.skan", qos: .utility)
17
+
18
+ // Storage keys
19
+ private static let LAST_CONVERSION_VALUE_KEY = "linkrunner_last_conversion_value"
20
+ private static let LAST_COARSE_VALUE_KEY = "linkrunner_last_coarse_value"
21
+ private static let LAST_LOCK_WINDOW_KEY = "linkrunner_last_lock_window"
22
+ private static let SKAN_REGISTER_TIMESTAMP_KEY = "linkrunner_skan_register_timestamp"
23
+
24
+ private init() {}
25
+
26
+ // MARK: - Public API
27
+
28
+ /// Register initial conversion value (0/low) on first app install
29
+ public func registerInitialConversionValue() async {
30
+ await withCheckedContinuation { continuation in
31
+ serialQueue.async { [weak self] in
32
+ guard let self = self else {
33
+ continuation.resume()
34
+ return
35
+ }
36
+
37
+ // Check if we've already registered
38
+ if self.hasRegisteredBefore() {
39
+ continuation.resume()
40
+ return
41
+ }
42
+
43
+ #if DEBUG
44
+ print("LinkrunnerKit: Registering initial SKAN conversion value (0/low)")
45
+ #endif
46
+
47
+ self.performSKANRegistration(
48
+ fineValue: 0,
49
+ coarseValue: "low",
50
+ lockWindow: false,
51
+ source: "sdk_init"
52
+ ) { result in
53
+ if result.success {
54
+ self.markAsRegistered()
55
+ self.saveLastConversionData(
56
+ fineValue: 0,
57
+ coarseValue: "low",
58
+ lockWindow: false
59
+ )
60
+ }
61
+ continuation.resume()
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ /// Update conversion value from API response
68
+ public func updateConversionValue(
69
+ fineValue: Int,
70
+ coarseValue: String?,
71
+ lockWindow: Bool,
72
+ source: String = "api"
73
+ ) async -> Bool {
74
+ return await withCheckedContinuation { continuation in
75
+ serialQueue.async { [weak self] in
76
+ guard let self = self else {
77
+ continuation.resume(returning: false)
78
+ return
79
+ }
80
+
81
+ #if DEBUG
82
+ print("LinkrunnerKit: Processing SKAN update for \(source): fine=\(fineValue)")
83
+ #endif
84
+
85
+ // Check if new value is lower than last updated value
86
+ let lastFineValue = self.getLastConversionValue()
87
+ if fineValue < lastFineValue {
88
+ #if DEBUG
89
+ print("LinkrunnerKit: New conversion value (\(fineValue)) is lower than last value (\(lastFineValue)), skipping update for \(source)")
90
+ #endif
91
+ continuation.resume(returning: false)
92
+ return
93
+ }
94
+
95
+ // Check if this exact same value is already the current value
96
+ if fineValue == lastFineValue {
97
+ let lastCoarseValue = self.getLastCoarseValue()
98
+ let lastLockWindow = self.getLastLockWindow()
99
+ if coarseValue == lastCoarseValue && lockWindow == lastLockWindow {
100
+ #if DEBUG
101
+ print("LinkrunnerKit: Same conversion value (\(fineValue)) already set, skipping update for \(source)")
102
+ #endif
103
+ continuation.resume(returning: true) // Return true since it's already at the desired state
104
+ return
105
+ }
106
+ }
107
+
108
+ #if DEBUG
109
+ print("LinkrunnerKit: Executing SKAN update for \(source): fine=\(fineValue), coarse=\(coarseValue ?? "nil"), lock=\(lockWindow)")
110
+ #endif
111
+
112
+ self.performSKANUpdate(
113
+ fineValue: fineValue,
114
+ coarseValue: coarseValue,
115
+ lockWindow: lockWindow,
116
+ source: source
117
+ ) { result in
118
+ if result.success {
119
+ self.saveLastConversionData(
120
+ fineValue: fineValue,
121
+ coarseValue: coarseValue,
122
+ lockWindow: lockWindow
123
+ )
124
+ #if DEBUG
125
+ print("LinkrunnerKit: Successfully updated SKAN conversion value for \(source)")
126
+ #endif
127
+ } else {
128
+ #if DEBUG
129
+ print("LinkrunnerKit: Failed to update SKAN conversion value for \(source): \(result.error ?? "Unknown error")")
130
+ #endif
131
+ }
132
+
133
+ continuation.resume(returning: result.success)
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ // MARK: - Private Implementation
140
+
141
+ private func hasRegisteredBefore() -> Bool {
142
+ return UserDefaults.standard.object(forKey: SKAdNetworkService.SKAN_REGISTER_TIMESTAMP_KEY) != nil
143
+ }
144
+
145
+ private func markAsRegistered() {
146
+ UserDefaults.standard.set(Date(), forKey: SKAdNetworkService.SKAN_REGISTER_TIMESTAMP_KEY)
147
+ }
148
+
149
+ private func getLastConversionValue() -> Int {
150
+ return UserDefaults.standard.integer(forKey: SKAdNetworkService.LAST_CONVERSION_VALUE_KEY)
151
+ }
152
+
153
+ private func getLastCoarseValue() -> String? {
154
+ return UserDefaults.standard.string(forKey: SKAdNetworkService.LAST_COARSE_VALUE_KEY)
155
+ }
156
+
157
+ private func getLastLockWindow() -> Bool {
158
+ return UserDefaults.standard.bool(forKey: SKAdNetworkService.LAST_LOCK_WINDOW_KEY)
159
+ }
160
+
161
+ private func saveLastConversionData(fineValue: Int, coarseValue: String?, lockWindow: Bool) {
162
+ UserDefaults.standard.set(fineValue, forKey: SKAdNetworkService.LAST_CONVERSION_VALUE_KEY)
163
+ if let coarseValue = coarseValue {
164
+ UserDefaults.standard.set(coarseValue, forKey: SKAdNetworkService.LAST_COARSE_VALUE_KEY)
165
+ }
166
+ UserDefaults.standard.set(lockWindow, forKey: SKAdNetworkService.LAST_LOCK_WINDOW_KEY)
167
+ }
168
+
169
+ // MARK: - SKAdNetwork API Calls
170
+
171
+ private func performSKANRegistration(
172
+ fineValue: Int,
173
+ coarseValue: String,
174
+ lockWindow: Bool,
175
+ source: String,
176
+ completion: @escaping (SKANResult) -> Void
177
+ ) {
178
+ #if canImport(AdAttributionKit)
179
+ if #available(iOS 17.4, *) {
180
+ // iOS 17.4+ use AdAttributionKit for registration
181
+ self.updateWithAdAttributionKit(
182
+ fineValue: fineValue,
183
+ coarseValue: coarseValue,
184
+ lockWindow: lockWindow,
185
+ completion: completion
186
+ )
187
+ return
188
+ }
189
+ #endif
190
+
191
+ if #available(iOS 16.1, *) {
192
+ // iOS 16.1+ supports coarse value and lock window in registration
193
+ self.registerWithCoarseValue(
194
+ fineValue: fineValue,
195
+ coarseValue: coarseValue,
196
+ lockWindow: lockWindow,
197
+ completion: completion
198
+ )
199
+ } else if #available(iOS 15.4, *) {
200
+ // iOS 15.4+ supports completion handler
201
+ self.registerWithCompletionHandler(
202
+ fineValue: fineValue,
203
+ completion: completion
204
+ )
205
+ } else {
206
+ // iOS 14.0+ basic registration
207
+ self.registerBasic(completion: completion)
208
+ }
209
+ }
210
+
211
+ private func performSKANUpdate(
212
+ fineValue: Int,
213
+ coarseValue: String?,
214
+ lockWindow: Bool,
215
+ source: String,
216
+ completion: @escaping (SKANResult) -> Void
217
+ ) {
218
+ #if canImport(AdAttributionKit)
219
+ if #available(iOS 17.4, *) {
220
+ // iOS 17.4+ use AdAttributionKit (modern approach)
221
+ self.updateWithAdAttributionKit(
222
+ fineValue: fineValue,
223
+ coarseValue: coarseValue,
224
+ lockWindow: lockWindow,
225
+ completion: completion
226
+ )
227
+ return
228
+ }
229
+ #endif
230
+
231
+ if #available(iOS 16.1, *) {
232
+ // iOS 16.1+ supports all parameters with SKAdNetwork
233
+ if let coarseValue = coarseValue {
234
+ self.updateWithCoarseValue(
235
+ fineValue: fineValue,
236
+ coarseValue: coarseValue,
237
+ lockWindow: lockWindow,
238
+ completion: completion
239
+ )
240
+ } else {
241
+ self.updateWithCompletionHandler(
242
+ fineValue: fineValue,
243
+ completion: completion
244
+ )
245
+ }
246
+ } else if #available(iOS 15.4, *) {
247
+ // iOS 15.4+ supports completion handler
248
+ self.updateWithCompletionHandler(
249
+ fineValue: fineValue,
250
+ completion: completion
251
+ )
252
+ } else {
253
+ // iOS 14.0+ basic update
254
+ self.updateBasic(fineValue: fineValue, completion: completion)
255
+ }
256
+ }
257
+
258
+ // MARK: - iOS Version-Specific Implementations
259
+
260
+ @available(iOS 16.1, *)
261
+ private func registerWithCoarseValue(
262
+ fineValue: Int,
263
+ coarseValue: String,
264
+ lockWindow: Bool,
265
+ completion: @escaping (SKANResult) -> Void
266
+ ) {
267
+ guard let skanCoarseValue = mapCoarseValue(coarseValue) else {
268
+ completion(SKANResult(success: false, error: "Invalid coarse value: \(coarseValue)"))
269
+ return
270
+ }
271
+
272
+ SKAdNetwork.updatePostbackConversionValue(
273
+ fineValue,
274
+ coarseValue: skanCoarseValue,
275
+ lockWindow: lockWindow
276
+ ) { error in
277
+ if let error = error {
278
+ completion(SKANResult(success: false, error: error.localizedDescription))
279
+ } else {
280
+ completion(SKANResult(success: true, error: nil))
281
+ }
282
+ }
283
+ }
284
+
285
+ @available(iOS 16.1, *)
286
+ private func updateWithCoarseValue(
287
+ fineValue: Int,
288
+ coarseValue: String,
289
+ lockWindow: Bool,
290
+ completion: @escaping (SKANResult) -> Void
291
+ ) {
292
+ guard let skanCoarseValue = mapCoarseValue(coarseValue) else {
293
+ completion(SKANResult(success: false, error: "Invalid coarse value: \(coarseValue)"))
294
+ return
295
+ }
296
+
297
+ SKAdNetwork.updatePostbackConversionValue(
298
+ fineValue,
299
+ coarseValue: skanCoarseValue,
300
+ lockWindow: lockWindow
301
+ ) { error in
302
+ if let error = error {
303
+ completion(SKANResult(success: false, error: error.localizedDescription))
304
+ } else {
305
+ completion(SKANResult(success: true, error: nil))
306
+ }
307
+ }
308
+ }
309
+
310
+ @available(iOS 15.4, *)
311
+ private func registerWithCompletionHandler(
312
+ fineValue: Int,
313
+ completion: @escaping (SKANResult) -> Void
314
+ ) {
315
+ SKAdNetwork.updatePostbackConversionValue(fineValue) { error in
316
+ if let error = error {
317
+ completion(SKANResult(success: false, error: error.localizedDescription))
318
+ } else {
319
+ completion(SKANResult(success: true, error: nil))
320
+ }
321
+ }
322
+ }
323
+
324
+ @available(iOS 15.4, *)
325
+ private func updateWithCompletionHandler(
326
+ fineValue: Int,
327
+ completion: @escaping (SKANResult) -> Void
328
+ ) {
329
+ SKAdNetwork.updatePostbackConversionValue(fineValue) { error in
330
+ if let error = error {
331
+ completion(SKANResult(success: false, error: error.localizedDescription))
332
+ } else {
333
+ completion(SKANResult(success: true, error: nil))
334
+ }
335
+ }
336
+ }
337
+
338
+ @available(iOS 14.0, *)
339
+ private func registerBasic(completion: @escaping (SKANResult) -> Void) {
340
+ SKAdNetwork.registerAppForAdNetworkAttribution()
341
+ // Basic registration doesn't provide completion callback
342
+ completion(SKANResult(success: true, error: nil))
343
+ }
344
+
345
+ @available(iOS 14.0, *)
346
+ private func updateBasic(fineValue: Int, completion: @escaping (SKANResult) -> Void) {
347
+ SKAdNetwork.updateConversionValue(fineValue)
348
+ // Basic update doesn't provide completion callback
349
+ completion(SKANResult(success: true, error: nil))
350
+ }
351
+
352
+ // MARK: - AdAttributionKit Implementation (iOS 17.4+)
353
+
354
+ #if canImport(AdAttributionKit)
355
+ @available(iOS 17.4, *)
356
+ private func updateWithAdAttributionKit(
357
+ fineValue: Int,
358
+ coarseValue: String?,
359
+ lockWindow: Bool,
360
+ completion: @escaping (SKANResult) -> Void
361
+ ) {
362
+ Task {
363
+ do {
364
+ // Map coarse value for AdAttributionKit
365
+ var adKitCoarseValue: AdAttributionKit.CoarseConversionValue?
366
+ if let coarseValue = coarseValue {
367
+ adKitCoarseValue = self.mapAdKitCoarseValue(coarseValue)
368
+ }
369
+
370
+ #if DEBUG
371
+ print("LinkrunnerKit: Using AdAttributionKit to update conversion value: fine=\(fineValue), coarse=\(coarseValue ?? "nil"), lock=\(lockWindow)")
372
+ #endif
373
+
374
+ // Update conversion value using AdAttributionKit
375
+ try await Postback.updateConversionValue(
376
+ fineValue,
377
+ coarseConversionValue: adKitCoarseValue ?? .low,
378
+ lockPostback: lockWindow
379
+ )
380
+
381
+ completion(SKANResult(success: true, error: nil))
382
+ } catch {
383
+ #if DEBUG
384
+ print("LinkrunnerKit: AdAttributionKit update failed: \(error.localizedDescription)")
385
+ #endif
386
+ completion(SKANResult(success: false, error: error.localizedDescription))
387
+ }
388
+ }
389
+ }
390
+
391
+ @available(iOS 17.4, *)
392
+ private func mapAdKitCoarseValue(_ value: String) -> AdAttributionKit.CoarseConversionValue {
393
+ switch value.lowercased() {
394
+ case "low":
395
+ return .low
396
+ case "medium":
397
+ return .medium
398
+ case "high":
399
+ return .high
400
+ default:
401
+ return .low
402
+ }
403
+ }
404
+ #endif
405
+
406
+ // MARK: - Helper Methods
407
+
408
+ @available(iOS 16.1, *)
409
+ private func mapCoarseValue(_ value: String) -> SKAdNetwork.CoarseConversionValue? {
410
+ switch value.lowercased() {
411
+ case "low":
412
+ return .low
413
+ case "medium":
414
+ return .medium
415
+ case "high":
416
+ return .high
417
+ default:
418
+ return nil
419
+ }
420
+ }
421
+ }
422
+
423
+ // MARK: - Result Types
424
+
425
+ struct SKANResult {
426
+ let success: Bool
427
+ let error: String?
428
+ }