@attentive-mobile/attentive-react-native-sdk 2.0.0-beta.6 → 2.0.0-beta.7

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.
@@ -280,7 +280,7 @@ struct DebugEvent {
280
280
 
281
281
  /**
282
282
  * Handle when a push notification is opened by the user.
283
- * Note: SDK 2.0.8 changed the API to require UNNotificationResponse instead of userInfo dictionary.
283
+ * Note: The SDK requires UNNotificationResponse instead of a plain userInfo dictionary.
284
284
  * This method is kept for backward compatibility but has limited functionality.
285
285
  * For full functionality, handle push notifications natively in AppDelegate.
286
286
  *
@@ -290,11 +290,11 @@ struct DebugEvent {
290
290
  */
291
291
  @objc(handlePushOpened:applicationState:authorizationStatus:)
292
292
  public func handlePushOpened(_ userInfo: [String: Any], applicationState: String, authorizationStatus: String) {
293
- // Note: SDK 2.0.8 changed the API to require UNNotificationResponse
293
+ // Note: The native SDK requires UNNotificationResponse for push tracking.
294
294
  // Since React Native doesn't provide direct access to UNNotificationResponse,
295
- // apps should handle push notifications natively in AppDelegate for full functionality
296
- print("[AttentiveSDK] Warning: Push notification handling from React Native is limited in SDK 2.0.8")
297
- print("[AttentiveSDK] The native SDK now requires UNNotificationResponse for push tracking")
295
+ // apps should handle push notifications natively in AppDelegate for full functionality.
296
+ print("[AttentiveSDK] Warning: Push notification handling from React Native is limited")
297
+ print("[AttentiveSDK] The native SDK requires UNNotificationResponse for push tracking")
298
298
  print("[AttentiveSDK] Please implement push handling in AppDelegate for full functionality")
299
299
 
300
300
  if debuggingEnabled {
@@ -302,7 +302,7 @@ struct DebugEvent {
302
302
  "applicationState": applicationState,
303
303
  "authorizationStatus": authorizationStatus,
304
304
  "userInfo": userInfo,
305
- "warning": "SDK 2.0.8 requires native UNNotificationResponse handling in AppDelegate"
305
+ "warning": "Native SDK requires UNNotificationResponse handling in AppDelegate"
306
306
  ])
307
307
  }
308
308
  }
@@ -313,15 +313,15 @@ struct DebugEvent {
313
313
  */
314
314
  @objc(handleForegroundNotification:)
315
315
  public func handleForegroundNotification(_ userInfo: [String: Any]) {
316
- // Note: SDK 2.0.8 changed the API to require UNNotificationResponse
317
- // Since React Native doesn't provide this, we'll log a warning
318
- print("[AttentiveSDK] Warning: Foreground notification handling from React Native is limited in SDK 2.0.8")
316
+ // Note: The native SDK requires UNNotificationResponse for push tracking.
317
+ // Since React Native doesn't provide this, we'll log a warning.
318
+ print("[AttentiveSDK] Warning: Foreground notification handling from React Native is limited")
319
319
  print("[AttentiveSDK] Please handle foreground notifications natively in AppDelegate for full functionality")
320
320
 
321
321
  if debuggingEnabled {
322
322
  showDebugInfo(event: "Foreground Notification", data: [
323
323
  "userInfo": userInfo,
324
- "warning": "SDK 2.0.8 requires native UNNotificationResponse handling"
324
+ "warning": "Native SDK requires UNNotificationResponse handling"
325
325
  ])
326
326
  }
327
327
  }
@@ -383,63 +383,126 @@ struct DebugEvent {
383
383
  /**
384
384
  * React Native bridge entry point for foreground push tracking.
385
385
  *
386
- * **Important — iOS limitation**: The Attentive iOS SDK requires a `UNNotificationResponse`
387
- * object for `handleForegroundPush`, which is an opaque system type that cannot be
388
- * serialised across the React Native bridge. Calling this method from JS therefore
389
- * **cannot invoke the native SDK** and is a no-op.
386
+ * The Attentive iOS SDK requires a `UNNotificationResponse` which cannot be
387
+ * serialised across the React Native bridge. To work around this, the method
388
+ * checks `AttentiveSDKManager.shared` for a cached response (set by the
389
+ * recommended `handleNotificationResponse(_:)` call in AppDelegate).
390
390
  *
391
- * The correct solution for iOS is to call
392
- * `AttentiveSDKManager.shared.handleForegroundPush(response:authorizationStatus:)`
393
- * directly from the host app's `UNUserNotificationCenterDelegate` implementation
394
- * (i.e. `AppDelegate.userNotificationCenter(_:didReceive:withCompletionHandler:)`).
395
- * Bonni's `AppDelegate.swift` already does this, so foreground push events are tracked
396
- * correctly via the native path — this RN bridge variant is intentionally unused on iOS.
391
+ * - If a cached response exists **and was already tracked**, this is a no-op
392
+ * (prevents double-tracking).
393
+ * - If a cached response exists **but was not yet tracked**, it is used to
394
+ * call the real native SDK method.
395
+ * - If no cached response is available, a warning is logged instructing the
396
+ * developer to add `AttentiveSDKManager.shared.handleNotificationResponse(response)`
397
+ * to their AppDelegate.
397
398
  *
398
- * @param userInfo The notification payload dictionary (unused on iOS).
399
- * @param authorizationStatus Current push authorization status string (unused on iOS).
399
+ * @param userInfo The notification payload dictionary.
400
+ * @param authorizationStatus Current push authorization status string.
400
401
  */
401
402
  @objc(handleForegroundPushFromRN:authorizationStatus:)
402
403
  public func handleForegroundPushFromRN(_ userInfo: [String: Any], authorizationStatus: String) {
403
- // iOS: no-op tracking is performed natively via AttentiveSDKManager in AppDelegate.
404
- // The UNNotificationResponse required by the iOS SDK cannot cross the RN bridge.
405
- print("[AttentiveSDK] handleForegroundPushFromRN: iOS push tracking is handled natively via AttentiveSDKManager in AppDelegate. This RN bridge call is a no-op on iOS.")
404
+ if let pending = AttentiveSDKManager.shared.consumePendingResponse() {
405
+ if pending.alreadyTracked {
406
+ // Already tracked by handleNotificationResponse nothing to do.
407
+ if debuggingEnabled {
408
+ showDebugInfo(event: "Foreground Push (already tracked natively)", data: [
409
+ "authorizationStatus": authorizationStatus,
410
+ "note": "Push was already tracked via AttentiveSDKManager.handleNotificationResponse."
411
+ ])
412
+ }
413
+ return
414
+ }
406
415
 
407
- if debuggingEnabled {
408
- showDebugInfo(event: "Foreground Push (RN bridge — iOS no-op)", data: [
409
- "authorizationStatus": authorizationStatus,
410
- "userInfo": userInfo,
411
- "note": "iOS: tracked via AppDelegate → AttentiveSDKManager. UNNotificationResponse not available from RN bridge."
412
- ])
416
+ // Cached response exists but wasn't tracked yet — track it now.
417
+ let authStatus = mapAuthorizationStatus(authorizationStatus)
418
+ sdk.handleForegroundPush(response: pending.response, authorizationStatus: authStatus)
419
+
420
+ if debuggingEnabled {
421
+ let cachedUserInfo = pending.response.notification.request.content.userInfo
422
+ showDebugInfo(event: "Foreground Push (tracked via cached response)", data: [
423
+ "authorizationStatus": authorizationStatus,
424
+ "userInfo": cachedUserInfo as? [String: Any] ?? [:],
425
+ "actionIdentifier": pending.response.actionIdentifier
426
+ ])
427
+ }
428
+ } else {
429
+ // No cached response. Either:
430
+ // (a) The client's AppDelegate is missing handleNotificationResponse, or
431
+ // (b) A prior bridge call (handlePushOpen / handleForegroundPush) already consumed it.
432
+ print("[AttentiveSDK] handleForegroundPushFromRN: No cached UNNotificationResponse available.")
433
+ print("[AttentiveSDK] If push tracking is not working, add this line to your AppDelegate's didReceive handler:")
434
+ print("[AttentiveSDK] AttentiveSDKManager.shared.handleNotificationResponse(response)")
435
+
436
+ if debuggingEnabled {
437
+ showDebugInfo(event: "Foreground Push (no cached response)", data: [
438
+ "authorizationStatus": authorizationStatus,
439
+ "userInfo": userInfo,
440
+ "note": "No cached response — either already consumed by a prior call or AppDelegate setup missing."
441
+ ])
442
+ }
413
443
  }
414
444
  }
415
445
 
416
446
  /**
417
447
  * React Native bridge entry point for push-open tracking (background/inactive tap).
418
448
  *
419
- * **Important — iOS limitation**: The Attentive iOS SDK requires a `UNNotificationResponse`
420
- * object for `handlePushOpen`, which cannot be serialised across the React Native bridge.
421
- * Calling this method from JS is therefore a **no-op on iOS**.
449
+ * The Attentive iOS SDK requires a `UNNotificationResponse` which cannot be
450
+ * serialised across the React Native bridge. To work around this, the method
451
+ * checks `AttentiveSDKManager.shared` for a cached response (set by the
452
+ * recommended `handleNotificationResponse(_:)` call in AppDelegate).
422
453
  *
423
- * The correct solution is to call
424
- * `AttentiveSDKManager.shared.handlePushOpen(response:authorizationStatus:)` directly
425
- * from `AppDelegate.userNotificationCenter(_:didReceive:withCompletionHandler:)`.
426
- * Bonni's `AppDelegate.swift` already does this via the native path.
454
+ * - If a cached response exists **and was already tracked**, this is a no-op
455
+ * (prevents double-tracking).
456
+ * - If a cached response exists **but was not yet tracked**, it is used to
457
+ * call the real native SDK method.
458
+ * - If no cached response is available, a warning is logged instructing the
459
+ * developer to add `AttentiveSDKManager.shared.handleNotificationResponse(response)`
460
+ * to their AppDelegate.
427
461
  *
428
- * @param userInfo The notification payload dictionary (unused on iOS).
429
- * @param authorizationStatus Current push authorization status string (unused on iOS).
462
+ * @param userInfo The notification payload dictionary.
463
+ * @param authorizationStatus Current push authorization status string.
430
464
  */
431
465
  @objc(handlePushOpenFromRN:authorizationStatus:)
432
466
  public func handlePushOpenFromRN(_ userInfo: [String: Any], authorizationStatus: String) {
433
- // iOS: no-op tracking is performed natively via AttentiveSDKManager in AppDelegate.
434
- // The UNNotificationResponse required by the iOS SDK cannot cross the RN bridge.
435
- print("[AttentiveSDK] handlePushOpenFromRN: iOS push tracking is handled natively via AttentiveSDKManager in AppDelegate. This RN bridge call is a no-op on iOS.")
467
+ if let pending = AttentiveSDKManager.shared.consumePendingResponse() {
468
+ if pending.alreadyTracked {
469
+ // Already tracked by handleNotificationResponse nothing to do.
470
+ if debuggingEnabled {
471
+ showDebugInfo(event: "Push Open (already tracked natively)", data: [
472
+ "authorizationStatus": authorizationStatus,
473
+ "note": "Push was already tracked via AttentiveSDKManager.handleNotificationResponse."
474
+ ])
475
+ }
476
+ return
477
+ }
436
478
 
437
- if debuggingEnabled {
438
- showDebugInfo(event: "Push Open (RN bridge — iOS no-op)", data: [
439
- "authorizationStatus": authorizationStatus,
440
- "userInfo": userInfo,
441
- "note": "iOS: tracked via AppDelegate → AttentiveSDKManager. UNNotificationResponse not available from RN bridge."
442
- ])
479
+ // Cached response exists but wasn't tracked yet — track it now.
480
+ let authStatus = mapAuthorizationStatus(authorizationStatus)
481
+ sdk.handlePushOpen(response: pending.response, authorizationStatus: authStatus)
482
+
483
+ if debuggingEnabled {
484
+ let cachedUserInfo = pending.response.notification.request.content.userInfo
485
+ showDebugInfo(event: "Push Open (tracked via cached response)", data: [
486
+ "authorizationStatus": authorizationStatus,
487
+ "userInfo": cachedUserInfo as? [String: Any] ?? [:],
488
+ "actionIdentifier": pending.response.actionIdentifier
489
+ ])
490
+ }
491
+ } else {
492
+ // No cached response. Either:
493
+ // (a) The client's AppDelegate is missing handleNotificationResponse, or
494
+ // (b) A prior bridge call (handlePushOpen / handleForegroundPush) already consumed it.
495
+ print("[AttentiveSDK] handlePushOpenFromRN: No cached UNNotificationResponse available.")
496
+ print("[AttentiveSDK] If push tracking is not working, add this line to your AppDelegate's didReceive handler:")
497
+ print("[AttentiveSDK] AttentiveSDKManager.shared.handleNotificationResponse(response)")
498
+
499
+ if debuggingEnabled {
500
+ showDebugInfo(event: "Push Open (no cached response)", data: [
501
+ "authorizationStatus": authorizationStatus,
502
+ "userInfo": userInfo,
503
+ "note": "No cached response — either already consumed by a prior call or AppDelegate setup missing."
504
+ ])
505
+ }
443
506
  }
444
507
  }
445
508
 
@@ -7,6 +7,7 @@
7
7
  //
8
8
 
9
9
  import Foundation
10
+ import UIKit
10
11
  import UserNotifications
11
12
 
12
13
  /**
@@ -14,21 +15,21 @@ import UserNotifications
14
15
  * This allows AppDelegate and other native code to access the SDK instance
15
16
  * that was initialized from React Native.
16
17
  *
17
- * Usage in AppDelegate:
18
+ * ## Minimal Integration (recommended)
19
+ *
20
+ * In your AppDelegate's `userNotificationCenter(_:didReceive:withCompletionHandler:)`,
21
+ * add **one line** to enable push-open and foreground-push tracking:
22
+ *
18
23
  * ```swift
19
- * // In userNotificationCenter(_:didReceive:withCompletionHandler:)
20
- * UNUserNotificationCenter.current().getNotificationSettings { settings in
21
- * let authStatus = settings.authorizationStatus
22
- * DispatchQueue.main.async {
23
- * switch UIApplication.shared.applicationState {
24
- * case .active:
25
- * AttentiveSDKManager.shared.handleForegroundPush(response: response, authorizationStatus: authStatus)
26
- * case .background, .inactive:
27
- * AttentiveSDKManager.shared.handlePushOpen(response: response, authorizationStatus: authStatus)
28
- * @unknown default:
29
- * AttentiveSDKManager.shared.handlePushOpen(response: response, authorizationStatus: authStatus)
30
- * }
31
- * }
24
+ * func userNotificationCenter(_ center: UNUserNotificationCenter,
25
+ * didReceive response: UNNotificationResponse,
26
+ * withCompletionHandler completionHandler: @escaping () -> Void) {
27
+ * // Track push interaction with Attentive (handles app-state detection + auth status internally)
28
+ * AttentiveSDKManager.shared.handleNotificationResponse(response)
29
+ *
30
+ * // Forward to your push library (e.g. RNCPushNotificationIOS) for JS-side handling
31
+ * RNCPushNotificationIOS.didReceive(response)
32
+ * completionHandler()
32
33
  * }
33
34
  * ```
34
35
  */
@@ -36,10 +37,40 @@ import UserNotifications
36
37
  /// Shared singleton instance
37
38
  @objc public static let shared: AttentiveSDKManager = AttentiveSDKManager()
38
39
 
39
- /// The Attentive SDK instance as AnyObject for Objective-C compatibility
40
- /// This should be set when the SDK is initialized from React Native
41
- /// Cast to ATTNNativeSDK in Swift code
42
- @objc public var sdk: AnyObject?
40
+ /// The Attentive SDK instance as AnyObject for Objective-C compatibility.
41
+ /// When set, any pending (untracked) notification response is automatically flushed.
42
+ @objc public var sdk: AnyObject? {
43
+ didSet {
44
+ if sdk != nil {
45
+ flushPendingResponseIfNeeded()
46
+ }
47
+ }
48
+ }
49
+
50
+ // MARK: - Notification Response Cache
51
+
52
+ /// The most recent UNNotificationResponse, cached so the RN bridge can use it
53
+ /// when JS calls handlePushOpen / handleForegroundPush.
54
+ ///
55
+ /// **Known limitation:** This is a single-slot cache. If two notifications
56
+ /// arrive in rapid succession (e.g. a tap followed immediately by a
57
+ /// foreground delivery), the second call to `handleNotificationResponse`
58
+ /// overwrites the first before its async tracking completes. In practice
59
+ /// this is extremely rare because `didReceive` is serialized by the system.
60
+ private var pendingResponse: UNNotificationResponse?
61
+
62
+ /// Whether the pending response has already been tracked via `handleNotificationResponse`.
63
+ /// Prevents double-tracking when both the native convenience method and the JS bridge fire.
64
+ private var pendingResponseTracked: Bool = false
65
+
66
+ /// The app state captured at the moment `handleNotificationResponse` was called.
67
+ /// Stored so that cold-launch replays use the original state (typically `.inactive`
68
+ /// or `.background`) rather than `.active` which is what the app will be in by the
69
+ /// time the SDK finishes initializing.
70
+ private var pendingAppState: UIApplication.State = .inactive
71
+
72
+ /// Serialises access to the cache fields above.
73
+ private let responseLock = NSLock()
43
74
 
44
75
  private override init() {
45
76
  super.init()
@@ -55,29 +86,167 @@ import UserNotifications
55
86
  sdk = nativeSDK
56
87
  }
57
88
 
58
- // MARK: - Push Notification Handlers (for AppDelegate)
89
+ // MARK: - Push Notification Convenience Method
90
+
91
+ /**
92
+ * Handle a push notification response in one call.
93
+ *
94
+ * This is the **recommended** way to track push interactions on iOS.
95
+ * It automatically determines the app state, fetches the current
96
+ * authorization status, and calls the appropriate native SDK method
97
+ * (`handlePushOpen` or `handleForegroundPush`).
98
+ *
99
+ * Call this from your AppDelegate's
100
+ * `userNotificationCenter(_:didReceive:withCompletionHandler:)`.
101
+ *
102
+ * The response is also cached so that if the JS layer later calls
103
+ * `handlePushOpen()` or `handleForegroundPush()` via the RN bridge,
104
+ * the bridge can fulfil the call using the real `UNNotificationResponse`
105
+ * without double-tracking.
106
+ *
107
+ * **Cold-launch safety:** If the SDK has not yet been initialized (e.g. the
108
+ * app was launched from a killed state via a push tap), the response is
109
+ * cached and will be automatically tracked once `sdk` is set during RN
110
+ * bridge initialization.
111
+ *
112
+ * @param response The `UNNotificationResponse` from the notification center delegate.
113
+ */
114
+ @objc public func handleNotificationResponse(_ response: UNNotificationResponse) {
115
+ // UIApplication.shared.applicationState is a UIKit property that must
116
+ // be read on the main thread. userNotificationCenter(_:didReceive:) is
117
+ // almost always delivered on main, but we guard defensively.
118
+ let cacheAndTrack = { [self] (appState: UIApplication.State) in
119
+ // Cache the response for the RN bridge (consumed by handlePushOpenFromRN /
120
+ // handleForegroundPushFromRN).
121
+ responseLock.lock()
122
+ pendingResponse = response
123
+ pendingResponseTracked = false
124
+ pendingAppState = appState
125
+ responseLock.unlock()
126
+
127
+ trackResponse(response, appState: appState)
128
+ }
129
+
130
+ if Thread.isMainThread {
131
+ cacheAndTrack(UIApplication.shared.applicationState)
132
+ } else {
133
+ DispatchQueue.main.async {
134
+ cacheAndTrack(UIApplication.shared.applicationState)
135
+ }
136
+ }
137
+ }
138
+
139
+ // MARK: - Push Notification Handlers (legacy — prefer handleNotificationResponse)
59
140
 
60
141
  /**
61
142
  * Handle a push notification when the app is in the foreground (active state).
62
- * Call this from AppDelegate's userNotificationCenter(_:didReceive:withCompletionHandler:)
63
- * when UIApplication.shared.applicationState == .active
64
143
  *
65
- * @param response The UNNotificationResponse from the notification center delegate
66
- * @param authorizationStatus Current push authorization status from notification settings
144
+ * - Important: Deprecated use ``handleNotificationResponse(_:)`` instead,
145
+ * which handles app-state detection and auth-status lookup automatically.
146
+ * Do **not** call both methods for the same notification.
67
147
  */
148
+ @available(*, deprecated, message: "Use handleNotificationResponse(_:) instead")
68
149
  @objc public func handleForegroundPush(response: UNNotificationResponse, authorizationStatus: UNAuthorizationStatus) {
69
150
  nativeSDK?.handleForegroundPush(response: response, authorizationStatus: authorizationStatus)
70
151
  }
71
152
 
72
153
  /**
73
154
  * Handle when a push notification is opened by the user (app in background/inactive state).
74
- * Call this from AppDelegate's userNotificationCenter(_:didReceive:withCompletionHandler:)
75
- * when UIApplication.shared.applicationState == .background or .inactive
76
155
  *
77
- * @param response The UNNotificationResponse from the notification center delegate
78
- * @param authorizationStatus Current push authorization status from notification settings
156
+ * - Important: Deprecated use ``handleNotificationResponse(_:)`` instead,
157
+ * which handles app-state detection and auth-status lookup automatically.
158
+ * Do **not** call both methods for the same notification.
79
159
  */
160
+ @available(*, deprecated, message: "Use handleNotificationResponse(_:) instead")
80
161
  @objc public func handlePushOpen(response: UNNotificationResponse, authorizationStatus: UNAuthorizationStatus) {
81
162
  nativeSDK?.handlePushOpen(response: response, authorizationStatus: authorizationStatus)
82
163
  }
164
+
165
+ // MARK: - Response Cache (internal, used by RN bridge)
166
+
167
+ /**
168
+ * Consume the cached notification response.
169
+ *
170
+ * Returns the cached `UNNotificationResponse` and whether it was already
171
+ * tracked by `handleNotificationResponse`. The bridge uses this to decide
172
+ * whether to call the native SDK again.
173
+ *
174
+ * After this call the cache is cleared.
175
+ */
176
+ func consumePendingResponse() -> (response: UNNotificationResponse, alreadyTracked: Bool)? {
177
+ responseLock.lock()
178
+ defer { responseLock.unlock() }
179
+
180
+ guard let response = pendingResponse else { return nil }
181
+ let tracked = pendingResponseTracked
182
+ pendingResponse = nil
183
+ pendingResponseTracked = false
184
+ return (response, tracked)
185
+ }
186
+
187
+ // MARK: - Private
188
+
189
+ /// Attempt to track a cached response via the native SDK.
190
+ /// If nativeSDK is nil (cold launch), returns without marking tracked;
191
+ /// the `sdk` didSet will call `flushPendingResponseIfNeeded` later.
192
+ ///
193
+ /// Uses the supplied `appState` (captured at `handleNotificationResponse` time)
194
+ /// rather than the current app state, so cold-launch replays classify the
195
+ /// event correctly.
196
+ private func trackResponse(_ response: UNNotificationResponse, appState: UIApplication.State) {
197
+ UNUserNotificationCenter.current().getNotificationSettings { [weak self] settings in
198
+ guard let self = self else { return }
199
+ let authStatus = settings.authorizationStatus
200
+ DispatchQueue.main.async {
201
+ // Check that this response is still pending and untracked, then
202
+ // mark tracked optimistically *before* releasing the lock. This
203
+ // closes the window where consumePendingResponse() could run on
204
+ // another thread, see alreadyTracked: false, and also call the
205
+ // native SDK — resulting in double-tracking.
206
+ self.responseLock.lock()
207
+ guard self.pendingResponse === response, !self.pendingResponseTracked else {
208
+ self.responseLock.unlock()
209
+ return
210
+ }
211
+
212
+ guard self.nativeSDK != nil else {
213
+ // SDK not yet initialized (cold launch). Leave
214
+ // pendingResponseTracked = false so the didSet observer
215
+ // on `sdk` can flush later.
216
+ self.responseLock.unlock()
217
+ return
218
+ }
219
+
220
+ // Mark tracked while still holding the lock, before the SDK call.
221
+ self.pendingResponseTracked = true
222
+ self.responseLock.unlock()
223
+
224
+ // Safe to call outside the lock — we've already claimed ownership.
225
+ switch appState {
226
+ case .active:
227
+ self.nativeSDK?.handleForegroundPush(response: response, authorizationStatus: authStatus)
228
+ case .background, .inactive:
229
+ self.nativeSDK?.handlePushOpen(response: response, authorizationStatus: authStatus)
230
+ @unknown default:
231
+ self.nativeSDK?.handlePushOpen(response: response, authorizationStatus: authStatus)
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ /// Called from `sdk` didSet when the SDK becomes available.
238
+ /// If there is a pending untracked response (cold-launch scenario),
239
+ /// track it now using the app state captured at the original
240
+ /// `handleNotificationResponse` call.
241
+ private func flushPendingResponseIfNeeded() {
242
+ responseLock.lock()
243
+ guard let response = pendingResponse, !pendingResponseTracked else {
244
+ responseLock.unlock()
245
+ return
246
+ }
247
+ let appState = pendingAppState
248
+ responseLock.unlock()
249
+
250
+ trackResponse(response, appState: appState)
251
+ }
83
252
  }
package/ios/Podfile CHANGED
@@ -20,7 +20,7 @@ target 'AttentiveReactNativeSdk' do
20
20
  flags = get_default_flags()
21
21
 
22
22
  # Ensure Swift support
23
- pod 'attentive-ios-sdk', '2.0.8'
23
+ pod 'attentive-ios-sdk', '2.0.13'
24
24
 
25
25
  use_react_native!(
26
26
  :path => config[:reactNativePath],
@@ -338,17 +338,16 @@ function handleForegroundNotification(userInfo) {
338
338
 
339
339
  /**
340
340
  * Handle a push notification when the app is in the foreground (active state).
341
- * This is the React Native equivalent of the native iOS handleForegroundPush method.
342
341
  *
343
342
  * Call this when you receive a notification response and the app state is 'active'.
344
- * This is part of implementing the native iOS pattern:
345
- * ```swift
346
- * case .active:
347
- * self.attentiveSdk?.handleForegroundPush(response: response, authorizationStatus: authStatus)
348
- * ```
349
343
  *
350
- * On iOS, this properly tracks foreground push notifications.
351
- * On Android, registers the FCM token when provided by the host app.
344
+ * **iOS prerequisite:** Your AppDelegate's
345
+ * `userNotificationCenter(_:didReceive:withCompletionHandler:)` must call
346
+ * `AttentiveSDKManager.shared.handleNotificationResponse(response)` so that
347
+ * the SDK can cache the `UNNotificationResponse` required by the native iOS SDK.
348
+ * Without that one line of native code, this function cannot track the event.
349
+ *
350
+ * On Android, this tracks the foreground push as a custom event.
352
351
  *
353
352
  * @param userInfo - The notification payload from the push notification
354
353
  * @param authorizationStatus - Current push authorization status
@@ -371,17 +370,16 @@ function handleForegroundPush(userInfo, authorizationStatus) {
371
370
 
372
371
  /**
373
372
  * Handle when a push notification is opened by the user (app in background/inactive state).
374
- * This is the React Native equivalent of the native iOS handlePushOpen method.
375
373
  *
376
374
  * Call this when you receive a notification response and the app state is 'background' or 'inactive'.
377
- * This is part of implementing the native iOS pattern:
378
- * ```swift
379
- * case .background, .inactive:
380
- * self.attentiveSdk?.handlePushOpen(response: response, authorizationStatus: authStatus)
381
- * ```
382
375
  *
383
- * On iOS, this properly tracks push notification opens.
384
- * On Android, registers the FCM token when provided by the host app.
376
+ * **iOS prerequisite:** Your AppDelegate's
377
+ * `userNotificationCenter(_:didReceive:withCompletionHandler:)` must call
378
+ * `AttentiveSDKManager.shared.handleNotificationResponse(response)` so that
379
+ * the SDK can cache the `UNNotificationResponse` required by the native iOS SDK.
380
+ * Without that one line of native code, this function cannot track the event.
381
+ *
382
+ * On Android, this tracks the push open as a custom event.
385
383
  *
386
384
  * @param userInfo - The notification payload from the push notification
387
385
  * @param authorizationStatus - Current push authorization status
@@ -1 +1 @@
1
- {"version":3,"names":["_reactNative","require","_NativeAttentiveReactNativeSdk","_interopRequireDefault","obj","__esModule","default","LINKING_ERROR","Platform","select","ios","AttentiveReactNativeSdk","NativeAttentiveReactNativeSdkModule","Proxy","get","Error","initialize","configuration","attentiveDomain","mode","skipFatigueOnCreatives","enableDebugger","triggerCreative","creativeId","destroyCreative","updateDomain","domain","identify","identifiers","phone","email","klaviyoId","shopifyId","clientUserId","customIdentifiers","clearUser","recordAddToCartEvent","attrs","items","deeplink","recordProductViewEvent","recordPurchaseEvent","orderId","cartId","cartCoupon","recordCustomEvent","type","properties","invokeAttentiveDebugHelper","exportDebugLogs","registerForPushNotifications","getPushAuthorizationStatus","registerDeviceToken","token","authorizationStatus","registerDeviceTokenWithCallback","callback","handleRegularOpen","console","log","handlePushOpened","userInfo","applicationState","handleForegroundNotification","handleForegroundPush","handlePushOpen","getInitialPushNotification","result"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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BAA0B,CAAC,CAAC;AAC7D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,mBAAmBA,CAC1BC,KAAa,EACbC,mBAA4C,EACtC;EACN3C,uBAAuB,CAACyC,mBAAmB,CAACC,KAAK,EAAEC,mBAAmB,CAAC;AACzE;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,+BAA+BA,CACtCF,KAAa,EACbC,mBAA4C,EAC5CE,QAKS,EACH;EACN7C,uBAAuB,CAAC4C,+BAA+B,CACrDF,KAAK,EACLC,mBAAmB,EACnBE,QACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,iBAAiBA,CAACH,mBAA4C,EAAQ;EAC7EI,OAAO,CAACC,GAAG,CAAC,6DAA6D,CAAC;EAC1ED,OAAO,CAACC,GAAG,CAAC,4BAA4BL,mBAAmB,EAAE,CAAC;EAC9DI,OAAO,CAACC,GAAG,CACT,mEACF,CAAC;EAEDhD,uBAAuB,CAAC8C,iBAAiB,CAACH,mBAAmB,CAAC;EAE9DI,OAAO,CAACC,GAAG,CAAC,mDAAmD,CAAC;AAClE;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,gBAAgBA,CACvBC,QAAkC,EAClCC,gBAAkC,EAClCR,mBAA4C,EACtC;EACN3C,uBAAuB,CAACiD,gBAAgB,CACtCC,QAAQ,EACRC,gBAAgB,EAChBR,mBACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASS,4BAA4BA,CACnCF,QAAkC,EAC5B;EACNlD,uBAAuB,CAACoD,4BAA4B,CAACF,QAAkB,CAAC;AAC1E;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASG,oBAAoBA,CAC3BH,QAAkC,EAClCP,mBAA4C,EACtC;EACN3C,uBAAuB,CAACqD,oBAAoB,CAC1CH,QAAQ,EACRP,mBACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASW,cAAcA,CACrBJ,QAAkC,EAClCP,mBAA4C,EACtC;EACN3C,uBAAuB,CAACsD,cAAc,CACpCJ,QAAQ,EACRP,mBACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAeY,0BAA0BA,CAAA,EAA2C;EAClF,MAAMC,MAAM,GAAG,MAAMxD,uBAAuB,CAACuD,0BAA0B,CAAC,CAAC;EACzE,OAAOC,MAAM;AACf","ignoreList":[]}
1
+ {"version":3,"names":["_reactNative","require","_NativeAttentiveReactNativeSdk","_interopRequireDefault","obj","__esModule","default","LINKING_ERROR","Platform","select","ios","AttentiveReactNativeSdk","NativeAttentiveReactNativeSdkModule","Proxy","get","Error","initialize","configuration","attentiveDomain","mode","skipFatigueOnCreatives","enableDebugger","triggerCreative","creativeId","destroyCreative","updateDomain","domain","identify","identifiers","phone","email","klaviyoId","shopifyId","clientUserId","customIdentifiers","clearUser","recordAddToCartEvent","attrs","items","deeplink","recordProductViewEvent","recordPurchaseEvent","orderId","cartId","cartCoupon","recordCustomEvent","type","properties","invokeAttentiveDebugHelper","exportDebugLogs","registerForPushNotifications","getPushAuthorizationStatus","registerDeviceToken","token","authorizationStatus","registerDeviceTokenWithCallback","callback","handleRegularOpen","console","log","handlePushOpened","userInfo","applicationState","handleForegroundNotification","handleForegroundPush","handlePushOpen","getInitialPushNotification","result"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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BAA0B,CAAC,CAAC;AAC7D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,mBAAmBA,CAC1BC,KAAa,EACbC,mBAA4C,EACtC;EACN3C,uBAAuB,CAACyC,mBAAmB,CAACC,KAAK,EAAEC,mBAAmB,CAAC;AACzE;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,+BAA+BA,CACtCF,KAAa,EACbC,mBAA4C,EAC5CE,QAKS,EACH;EACN7C,uBAAuB,CAAC4C,+BAA+B,CACrDF,KAAK,EACLC,mBAAmB,EACnBE,QACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,iBAAiBA,CAACH,mBAA4C,EAAQ;EAC7EI,OAAO,CAACC,GAAG,CAAC,6DAA6D,CAAC;EAC1ED,OAAO,CAACC,GAAG,CAAC,4BAA4BL,mBAAmB,EAAE,CAAC;EAC9DI,OAAO,CAACC,GAAG,CACT,mEACF,CAAC;EAEDhD,uBAAuB,CAAC8C,iBAAiB,CAACH,mBAAmB,CAAC;EAE9DI,OAAO,CAACC,GAAG,CAAC,mDAAmD,CAAC;AAClE;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,gBAAgBA,CACvBC,QAAkC,EAClCC,gBAAkC,EAClCR,mBAA4C,EACtC;EACN3C,uBAAuB,CAACiD,gBAAgB,CACtCC,QAAQ,EACRC,gBAAgB,EAChBR,mBACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASS,4BAA4BA,CACnCF,QAAkC,EAC5B;EACNlD,uBAAuB,CAACoD,4BAA4B,CAACF,QAAkB,CAAC;AAC1E;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASG,oBAAoBA,CAC3BH,QAAkC,EAClCP,mBAA4C,EACtC;EACN3C,uBAAuB,CAACqD,oBAAoB,CAC1CH,QAAQ,EACRP,mBACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASW,cAAcA,CACrBJ,QAAkC,EAClCP,mBAA4C,EACtC;EACN3C,uBAAuB,CAACsD,cAAc,CACpCJ,QAAQ,EACRP,mBACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAeY,0BAA0BA,CAAA,EAG/B;EACR,MAAMC,MAAM,GAAG,MAAMxD,uBAAuB,CAACuD,0BAA0B,CAAC,CAAC;EACzE,OAAOC,MAAM;AACf","ignoreList":[]}
@@ -310,17 +310,16 @@ function handleForegroundNotification(userInfo) {
310
310
 
311
311
  /**
312
312
  * Handle a push notification when the app is in the foreground (active state).
313
- * This is the React Native equivalent of the native iOS handleForegroundPush method.
314
313
  *
315
314
  * Call this when you receive a notification response and the app state is 'active'.
316
- * This is part of implementing the native iOS pattern:
317
- * ```swift
318
- * case .active:
319
- * self.attentiveSdk?.handleForegroundPush(response: response, authorizationStatus: authStatus)
320
- * ```
321
315
  *
322
- * On iOS, this properly tracks foreground push notifications.
323
- * On Android, registers the FCM token when provided by the host app.
316
+ * **iOS prerequisite:** Your AppDelegate's
317
+ * `userNotificationCenter(_:didReceive:withCompletionHandler:)` must call
318
+ * `AttentiveSDKManager.shared.handleNotificationResponse(response)` so that
319
+ * the SDK can cache the `UNNotificationResponse` required by the native iOS SDK.
320
+ * Without that one line of native code, this function cannot track the event.
321
+ *
322
+ * On Android, this tracks the foreground push as a custom event.
324
323
  *
325
324
  * @param userInfo - The notification payload from the push notification
326
325
  * @param authorizationStatus - Current push authorization status
@@ -343,17 +342,16 @@ function handleForegroundPush(userInfo, authorizationStatus) {
343
342
 
344
343
  /**
345
344
  * Handle when a push notification is opened by the user (app in background/inactive state).
346
- * This is the React Native equivalent of the native iOS handlePushOpen method.
347
345
  *
348
346
  * Call this when you receive a notification response and the app state is 'background' or 'inactive'.
349
- * This is part of implementing the native iOS pattern:
350
- * ```swift
351
- * case .background, .inactive:
352
- * self.attentiveSdk?.handlePushOpen(response: response, authorizationStatus: authStatus)
353
- * ```
354
347
  *
355
- * On iOS, this properly tracks push notification opens.
356
- * On Android, registers the FCM token when provided by the host app.
348
+ * **iOS prerequisite:** Your AppDelegate's
349
+ * `userNotificationCenter(_:didReceive:withCompletionHandler:)` must call
350
+ * `AttentiveSDKManager.shared.handleNotificationResponse(response)` so that
351
+ * the SDK can cache the `UNNotificationResponse` required by the native iOS SDK.
352
+ * Without that one line of native code, this function cannot track the event.
353
+ *
354
+ * On Android, this tracks the push open as a custom event.
357
355
  *
358
356
  * @param userInfo - The notification payload from the push notification
359
357
  * @param authorizationStatus - Current push authorization status