@attentive-mobile/attentive-react-native-sdk 1.0.5 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +126 -0
  2. package/android/build.gradle +1 -0
  3. package/android/src/main/kotlin/com/attentivereactnativesdk/AttentiveReactNativeSdkModule.kt +384 -0
  4. package/android/src/main/kotlin/com/attentivereactnativesdk/AttentiveReactNativeSdkPackage.kt +36 -0
  5. package/android/src/main/kotlin/com/attentivereactnativesdk/debug/AttentiveDebugHelper.kt +22 -6
  6. package/attentive-react-native-sdk.podspec +3 -3
  7. package/ios/AttentiveReactNativeSdk.h +1 -1
  8. package/ios/AttentiveReactNativeSdk.mm +317 -37
  9. package/ios/Bridging/ATTNNativeSDK.swift +392 -45
  10. package/ios/Bridging/AttentiveReactNativeSdk-Bridging-Header.h +3 -0
  11. package/ios/Bridging/AttentiveSDKManager.swift +83 -0
  12. package/ios/Podfile +3 -16
  13. package/lib/commonjs/NativeAttentiveReactNativeSdk.js +14 -0
  14. package/lib/commonjs/NativeAttentiveReactNativeSdk.js.map +1 -0
  15. package/lib/commonjs/eventTypes.js.map +1 -1
  16. package/lib/commonjs/index.js +362 -52
  17. package/lib/commonjs/index.js.map +1 -1
  18. package/lib/module/NativeAttentiveReactNativeSdk.js +7 -0
  19. package/lib/module/NativeAttentiveReactNativeSdk.js.map +1 -0
  20. package/lib/module/eventTypes.js.map +1 -1
  21. package/lib/module/index.js +345 -50
  22. package/lib/module/index.js.map +1 -1
  23. package/lib/typescript/NativeAttentiveReactNativeSdk.d.ts +103 -0
  24. package/lib/typescript/NativeAttentiveReactNativeSdk.d.ts.map +1 -0
  25. package/lib/typescript/eventTypes.d.ts +44 -17
  26. package/lib/typescript/eventTypes.d.ts.map +1 -1
  27. package/lib/typescript/index.d.ts +276 -41
  28. package/lib/typescript/index.d.ts.map +1 -1
  29. package/package.json +21 -7
  30. package/src/NativeAttentiveReactNativeSdk.ts +152 -0
  31. package/src/eventTypes.tsx +57 -20
  32. package/src/index.tsx +472 -96
  33. package/android/src/main/java/com/attentivereactnativesdk/AttentiveReactNativeSdkModule.java +0 -310
  34. package/android/src/main/java/com/attentivereactnativesdk/AttentiveReactNativeSdkPackage.java +0 -28
  35. package/ios/AttentiveReactNativeSdk.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  36. package/ios/AttentiveReactNativeSdk.xcodeproj/project.xcworkspace/xcuserdata/zheref.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  37. package/ios/AttentiveReactNativeSdk.xcodeproj/xcuserdata/zheref.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
@@ -9,6 +9,7 @@
9
9
  import Foundation
10
10
  import attentive_ios_sdk
11
11
  import UIKit
12
+ import UserNotifications
12
13
 
13
14
  // Debug Event structure for session history
14
15
  struct DebugEvent {
@@ -23,7 +24,7 @@ struct DebugEvent {
23
24
  self.eventType = eventType
24
25
  self.data = data
25
26
  }
26
-
27
+
27
28
  /**
28
29
  * Formats the debug event as a human-readable string for export
29
30
  * @return A formatted string containing timestamp, event type, and data
@@ -32,17 +33,17 @@ struct DebugEvent {
32
33
  let formatter = DateFormatter()
33
34
  formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
34
35
  let timeString = formatter.string(from: timestamp)
35
-
36
+
36
37
  var output = "[\(timeString)] \(eventType)\n"
37
-
38
+
38
39
  // Add summary information if available
39
40
  let summary = getSummary()
40
41
  if !summary.isEmpty {
41
42
  output += "Summary: \(summary)\n"
42
43
  }
43
-
44
+
44
45
  output += "Data:\n"
45
-
46
+
46
47
  // Format data as JSON for better readability
47
48
  do {
48
49
  let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
@@ -54,17 +55,17 @@ struct DebugEvent {
54
55
  } catch {
55
56
  output += "\(data)"
56
57
  }
57
-
58
+
58
59
  return output + "\n" + String(repeating: "=", count: 50) + "\n"
59
60
  }
60
-
61
+
61
62
  /**
62
63
  * Generates a summary of the debug event for quick overview
63
64
  * @return A brief summary string highlighting key information
64
65
  */
65
66
  private func getSummary() -> String {
66
67
  var summaryParts: [String] = []
67
-
68
+
68
69
  if let itemsCount = data["items_count"] as? String {
69
70
  summaryParts.append("Items: \(itemsCount)")
70
71
  }
@@ -77,10 +78,10 @@ struct DebugEvent {
77
78
  if let eventType = data["event_type"] as? String {
78
79
  summaryParts.append("Type: \(eventType)")
79
80
  }
80
-
81
+
81
82
  // Always show payload size info
82
83
  summaryParts.append("Payload: \(data.count) fields")
83
-
84
+
84
85
  return summaryParts.joined(separator: " • ")
85
86
  }
86
87
  }
@@ -94,17 +95,16 @@ struct DebugEvent {
94
95
  @objc(initWithDomain:mode:skipFatigueOnCreatives:enableDebugger:)
95
96
  public init(domain: String, mode: String, skipFatigueOnCreatives: Bool, enableDebugger: Bool) {
96
97
  self.sdk = ATTNSDK(domain: domain, mode: ATTNSDKMode(rawValue: mode) ?? .production)
97
- self.sdk.skipFatigueOnCreative = skipFatigueOnCreatives ?? false
98
-
98
+ self.sdk.skipFatigueOnCreative = skipFatigueOnCreatives
99
+
99
100
  // Only enable debugging if both enableDebugger is true AND the app is running in debug mode
100
- let enableDebuggerFromConfig = enableDebugger ?? false
101
101
  #if DEBUG
102
102
  let isDebugBuild = true
103
103
  #else
104
104
  let isDebugBuild = false
105
105
  #endif
106
- self.debuggingEnabled = enableDebuggerFromConfig && isDebugBuild
107
-
106
+ self.debuggingEnabled = enableDebugger && isDebugBuild
107
+
108
108
  ATTNEventTracker.setup(with: sdk)
109
109
  }
110
110
 
@@ -131,7 +131,14 @@ struct DebugEvent {
131
131
 
132
132
  @objc(identify:)
133
133
  public func identify(_ identifiers: [String: Any]) {
134
+ print("👤 [AttentiveSDK] identify called from React Native")
135
+ print(" Identifiers: \(identifiers)")
136
+
134
137
  sdk.identify(identifiers)
138
+
139
+ print("✅ [AttentiveSDK] identify completed")
140
+ print(" User is now identified with the SDK")
141
+ print(" SDK can now make network calls")
135
142
  }
136
143
 
137
144
  @objc
@@ -139,6 +146,346 @@ struct DebugEvent {
139
146
  sdk.clearUser()
140
147
  }
141
148
 
149
+ // MARK: - Push Notification Methods
150
+
151
+ /**
152
+ * Request push notification permission from the user.
153
+ * This will trigger the iOS permission dialog.
154
+ */
155
+ @objc
156
+ public func registerForPushNotifications() {
157
+ sdk.registerForPushNotifications()
158
+ if debuggingEnabled {
159
+ showDebugInfo(event: "Push Registration Requested", data: ["action": "registerForPushNotifications"])
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Register the device token received from APNs with the Attentive backend.
165
+ * @param token The hex-encoded device token string
166
+ * @param authorizationStatus Current push authorization status string
167
+ */
168
+ @objc(registerDeviceToken:authorizationStatus:)
169
+ public func registerDeviceToken(_ token: String, authorizationStatus: String) {
170
+ // Convert hex string back to Data
171
+ guard let tokenData = hexStringToData(token) else {
172
+ print("[AttentiveSDK] Invalid device token format")
173
+ return
174
+ }
175
+
176
+ // Map string to UNAuthorizationStatus
177
+ let status = mapAuthorizationStatus(authorizationStatus)
178
+
179
+ sdk.registerDeviceToken(tokenData, authorizationStatus: status)
180
+
181
+ if debuggingEnabled {
182
+ showDebugInfo(event: "Device Token Registered", data: [
183
+ "token": String(token.prefix(16)) + "...",
184
+ "authorizationStatus": authorizationStatus
185
+ ])
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Register the device token with callback for use in AppDelegate.
191
+ * This method is intended to be called directly from the host app's AppDelegate,
192
+ * not from React Native JavaScript.
193
+ *
194
+ * @param token The device token Data from APNs
195
+ * @param authorizationStatus Current push authorization status
196
+ * @param callback Completion handler called after registration attempt
197
+ */
198
+ @objc(registerDeviceTokenWithCallback:authorizationStatus:callback:)
199
+ public func registerDeviceToken(
200
+ _ token: Data,
201
+ authorizationStatus: UNAuthorizationStatus,
202
+ callback: @escaping (_ data: Data?, _ url: URL?, _ response: URLResponse?, _ error: Error?) -> Void
203
+ ) {
204
+ sdk.registerDeviceToken(token, authorizationStatus: authorizationStatus, callback: callback)
205
+
206
+ if debuggingEnabled {
207
+ let tokenString = token.map { String(format: "%02.2hhx", $0) }.joined()
208
+ showDebugInfo(event: "Device Token Registered (with callback)", data: [
209
+ "token": String(tokenString.prefix(16)) + "...",
210
+ "authorizationStatus": authorizationStatusToString(authorizationStatus)
211
+ ])
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Handle a regular/direct app open event.
217
+ * This should be called after device token registration completes (success or failure).
218
+ *
219
+ * This is the TypeScript equivalent of the native iOS AppDelegate method:
220
+ * ```swift
221
+ * func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
222
+ * UNUserNotificationCenter.current().getNotificationSettings { [weak self] settings in
223
+ * guard let self = self else { return }
224
+ * let authStatus = settings.authorizationStatus
225
+ * attentiveSdk?.registerDeviceToken(deviceToken, authorizationStatus: authStatus, callback: { data, url, response, error in
226
+ * DispatchQueue.main.async {
227
+ * self.attentiveSdk?.handleRegularOpen(authorizationStatus: authStatus)
228
+ * }
229
+ * })
230
+ * }
231
+ * }
232
+ * ```
233
+ *
234
+ * @param authorizationStatus Current push authorization status
235
+ */
236
+ @objc(handleRegularOpen:)
237
+ public func handleRegularOpen(authorizationStatus: UNAuthorizationStatus) {
238
+ print("🌉 [AttentiveSDK] handleRegularOpen called from React Native")
239
+ print(" Authorization Status: \(authorizationStatusToString(authorizationStatus))")
240
+ print(" Calling underlying iOS SDK handleRegularOpen...")
241
+
242
+ // Call the underlying Attentive iOS SDK
243
+ sdk.handleRegularOpen(authorizationStatus: authorizationStatus)
244
+
245
+ print("✅ [AttentiveSDK] handleRegularOpen completed")
246
+ print(" This should trigger a network call to: https://mobile.attentivemobile.com/mtctrl")
247
+ print(" If you don't see the network call:")
248
+ print(" 1. Check that user is identified (call identify() before handleRegularOpen)")
249
+ print(" 2. Check your proxy debugger is configured for mobile.attentivemobile.com")
250
+ print(" 3. Verify SSL proxying is enabled")
251
+ print(" 4. Check device has internet connection")
252
+
253
+ if debuggingEnabled {
254
+ showDebugInfo(event: "Regular Open Event", data: [
255
+ "authorizationStatus": authorizationStatusToString(authorizationStatus),
256
+ "expectedEndpoint": "https://mobile.attentivemobile.com/mtctrl",
257
+ "note": "Check network logs to verify endpoint was called"
258
+ ])
259
+ }
260
+ }
261
+
262
+
263
+ /**
264
+ * Handle when a push notification is opened by the user.
265
+ * Note: SDK 2.0.8 changed the API to require UNNotificationResponse instead of userInfo dictionary.
266
+ * This method is kept for backward compatibility but has limited functionality.
267
+ * For full functionality, handle push notifications natively in AppDelegate.
268
+ *
269
+ * @param userInfo The notification payload
270
+ * @param applicationState The app state when notification was opened
271
+ * @param authorizationStatus Current push authorization status
272
+ */
273
+ @objc(handlePushOpened:applicationState:authorizationStatus:)
274
+ public func handlePushOpened(_ userInfo: [String: Any], applicationState: String, authorizationStatus: String) {
275
+ // Note: SDK 2.0.8 changed the API to require UNNotificationResponse
276
+ // Since React Native doesn't provide direct access to UNNotificationResponse,
277
+ // apps should handle push notifications natively in AppDelegate for full functionality
278
+ print("[AttentiveSDK] Warning: Push notification handling from React Native is limited in SDK 2.0.8")
279
+ print("[AttentiveSDK] The native SDK now requires UNNotificationResponse for push tracking")
280
+ print("[AttentiveSDK] Please implement push handling in AppDelegate for full functionality")
281
+
282
+ if debuggingEnabled {
283
+ showDebugInfo(event: "Push Opened (Limited)", data: [
284
+ "applicationState": applicationState,
285
+ "authorizationStatus": authorizationStatus,
286
+ "userInfo": userInfo,
287
+ "warning": "SDK 2.0.8 requires native UNNotificationResponse handling in AppDelegate"
288
+ ])
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Handle when a push notification arrives while the app is in foreground.
294
+ * @param userInfo The notification payload
295
+ */
296
+ @objc(handleForegroundNotification:)
297
+ public func handleForegroundNotification(_ userInfo: [String: Any]) {
298
+ // Note: SDK 2.0.8 changed the API to require UNNotificationResponse
299
+ // Since React Native doesn't provide this, we'll log a warning
300
+ print("[AttentiveSDK] Warning: Foreground notification handling from React Native is limited in SDK 2.0.8")
301
+ print("[AttentiveSDK] Please handle foreground notifications natively in AppDelegate for full functionality")
302
+
303
+ if debuggingEnabled {
304
+ showDebugInfo(event: "Foreground Notification", data: [
305
+ "userInfo": userInfo,
306
+ "warning": "SDK 2.0.8 requires native UNNotificationResponse handling"
307
+ ])
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Handle a push notification when the app is in the foreground (active state).
313
+ * This is the native equivalent that should be called from AppDelegate when the app is active.
314
+ *
315
+ * Equivalent to Swift AppDelegate code:
316
+ * ```swift
317
+ * case .active:
318
+ * self.attentiveSdk?.handleForegroundPush(response: response, authorizationStatus: authStatus)
319
+ * ```
320
+ *
321
+ * @param response The UNNotificationResponse from the notification center delegate
322
+ * @param authorizationStatus Current push authorization status
323
+ */
324
+ @objc(handleForegroundPush:authorizationStatus:)
325
+ public func handleForegroundPush(response: UNNotificationResponse, authorizationStatus: UNAuthorizationStatus) {
326
+ sdk.handleForegroundPush(response: response, authorizationStatus: authorizationStatus)
327
+
328
+ if debuggingEnabled {
329
+ let userInfo = response.notification.request.content.userInfo
330
+ showDebugInfo(event: "Foreground Push", data: [
331
+ "authorizationStatus": authorizationStatusToString(authorizationStatus),
332
+ "userInfo": userInfo as? [String: Any] ?? [:],
333
+ "actionIdentifier": response.actionIdentifier
334
+ ])
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Handle when a push notification is opened by the user (app in background/inactive state).
340
+ * This is the native equivalent that should be called from AppDelegate when the app is background or inactive.
341
+ *
342
+ * Equivalent to Swift AppDelegate code:
343
+ * ```swift
344
+ * case .background, .inactive:
345
+ * self.attentiveSdk?.handlePushOpen(response: response, authorizationStatus: authStatus)
346
+ * ```
347
+ *
348
+ * @param response The UNNotificationResponse from the notification center delegate
349
+ * @param authorizationStatus Current push authorization status
350
+ */
351
+ @objc(handlePushOpen:authorizationStatus:)
352
+ public func handlePushOpen(response: UNNotificationResponse, authorizationStatus: UNAuthorizationStatus) {
353
+ sdk.handlePushOpen(response: response, authorizationStatus: authorizationStatus)
354
+
355
+ if debuggingEnabled {
356
+ let userInfo = response.notification.request.content.userInfo
357
+ showDebugInfo(event: "Push Open", data: [
358
+ "authorizationStatus": authorizationStatusToString(authorizationStatus),
359
+ "userInfo": userInfo as? [String: Any] ?? [:],
360
+ "actionIdentifier": response.actionIdentifier
361
+ ])
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Handle a push notification when the app is in the foreground (active state) - React Native version.
367
+ * This method accepts userInfo dictionary instead of UNNotificationResponse, making it callable from React Native.
368
+ *
369
+ * Note: This is a limited version since we don't have access to the full UNNotificationResponse.
370
+ * For full functionality, use the native AppDelegate implementation.
371
+ *
372
+ * @param userInfo The notification payload dictionary
373
+ * @param authorizationStatus Current push authorization status string
374
+ */
375
+ @objc(handleForegroundPushFromRN:authorizationStatus:)
376
+ public func handleForegroundPushFromRN(_ userInfo: [String: Any], authorizationStatus: String) {
377
+ _ = mapAuthorizationStatus(authorizationStatus)
378
+
379
+ // Note: SDK 2.0.8 requires UNNotificationResponse, but we only have userInfo from React Native
380
+ // This is a workaround that logs the limitation
381
+ print("[AttentiveSDK] handleForegroundPush called from React Native (limited functionality)")
382
+ print("[AttentiveSDK] For full functionality, implement in native AppDelegate")
383
+
384
+ if debuggingEnabled {
385
+ showDebugInfo(event: "Foreground Push (React Native)", data: [
386
+ "authorizationStatus": authorizationStatus,
387
+ "userInfo": userInfo,
388
+ "note": "Limited functionality - UNNotificationResponse not available from React Native"
389
+ ])
390
+ }
391
+ }
392
+
393
+ /**
394
+ * Handle when a push notification is opened by the user (app in background/inactive state) - React Native version.
395
+ * This method accepts userInfo dictionary instead of UNNotificationResponse, making it callable from React Native.
396
+ *
397
+ * Note: This is a limited version since we don't have access to the full UNNotificationResponse.
398
+ * For full functionality, use the native AppDelegate implementation.
399
+ *
400
+ * @param userInfo The notification payload dictionary
401
+ * @param authorizationStatus Current push authorization status string
402
+ */
403
+ @objc(handlePushOpenFromRN:authorizationStatus:)
404
+ public func handlePushOpenFromRN(_ userInfo: [String: Any], authorizationStatus: String) {
405
+ _ = mapAuthorizationStatus(authorizationStatus)
406
+
407
+ // Note: SDK 2.0.8 requires UNNotificationResponse, but we only have userInfo from React Native
408
+ // This is a workaround that logs the limitation
409
+ print("[AttentiveSDK] handlePushOpen called from React Native (limited functionality)")
410
+ print("[AttentiveSDK] For full functionality, implement in native AppDelegate")
411
+
412
+ if debuggingEnabled {
413
+ showDebugInfo(event: "Push Open (React Native)", data: [
414
+ "authorizationStatus": authorizationStatus,
415
+ "userInfo": userInfo,
416
+ "note": "Limited functionality - UNNotificationResponse not available from React Native"
417
+ ])
418
+ }
419
+ }
420
+
421
+ // MARK: - Push Notification Helpers
422
+
423
+ private func hexStringToData(_ hexString: String) -> Data? {
424
+ var data = Data()
425
+ var hex = hexString
426
+
427
+ // Remove any non-hex characters
428
+ hex = hex.replacingOccurrences(of: " ", with: "")
429
+ hex = hex.replacingOccurrences(of: "<", with: "")
430
+ hex = hex.replacingOccurrences(of: ">", with: "")
431
+
432
+ var index = hex.startIndex
433
+ while index < hex.endIndex {
434
+ let nextIndex = hex.index(index, offsetBy: 2, limitedBy: hex.endIndex) ?? hex.endIndex
435
+ if nextIndex > hex.endIndex { break }
436
+
437
+ let byteString = String(hex[index..<nextIndex])
438
+ if let byte = UInt8(byteString, radix: 16) {
439
+ data.append(byte)
440
+ } else {
441
+ return nil
442
+ }
443
+ index = nextIndex
444
+ }
445
+
446
+ return data.isEmpty ? nil : data
447
+ }
448
+
449
+ private func mapAuthorizationStatus(_ status: String) -> UNAuthorizationStatus {
450
+ switch status.lowercased() {
451
+ case "authorized":
452
+ return .authorized
453
+ case "denied":
454
+ return .denied
455
+ case "notdetermined":
456
+ return .notDetermined
457
+ case "provisional":
458
+ return .provisional
459
+ case "ephemeral":
460
+ if #available(iOS 14.0, *) {
461
+ return .ephemeral
462
+ }
463
+ return .authorized
464
+ default:
465
+ return .notDetermined
466
+ }
467
+ }
468
+
469
+ private func authorizationStatusToString(_ status: UNAuthorizationStatus) -> String {
470
+ switch status {
471
+ case .authorized:
472
+ return "authorized"
473
+ case .denied:
474
+ return "denied"
475
+ case .notDetermined:
476
+ return "notDetermined"
477
+ case .provisional:
478
+ return "provisional"
479
+ case .ephemeral:
480
+ if #available(iOS 14.0, *) {
481
+ return "ephemeral"
482
+ }
483
+ return "authorized"
484
+ @unknown default:
485
+ return "notDetermined"
486
+ }
487
+ }
488
+
142
489
  @objc
143
490
  public func invokeAttentiveDebugHelper() {
144
491
  if debuggingEnabled {
@@ -157,7 +504,7 @@ struct DebugEvent {
157
504
  }
158
505
  }
159
506
  }
160
-
507
+
161
508
  }
162
509
 
163
510
  public extension ATTNNativeSDK {
@@ -170,36 +517,36 @@ public extension ATTNNativeSDK {
170
517
  guard debuggingEnabled else {
171
518
  return "Debug logging is not enabled. Please enable debugging to export logs."
172
519
  }
173
-
520
+
174
521
  if debugHistory.isEmpty {
175
522
  return "No debug events recorded in this session."
176
523
  }
177
-
524
+
178
525
  let formatter = DateFormatter()
179
526
  formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
180
527
  let exportDate = formatter.string(from: Date())
181
-
528
+
182
529
  var exportContent = """
183
530
  Attentive React Native SDK - Debug Session Export
184
531
  Generated: \(exportDate)
185
532
  Total Events: \(debugHistory.count)
186
-
533
+
187
534
  \(String(repeating: "=", count: 60))
188
-
535
+
189
536
  """
190
-
537
+
191
538
  // Add all events in chronological order (oldest first for better readability)
192
539
  for (index, event) in debugHistory.enumerated() {
193
540
  exportContent += "Event #\(index + 1)\n"
194
541
  exportContent += event.formatForExport()
195
542
  exportContent += "\n"
196
543
  }
197
-
544
+
198
545
  exportContent += """
199
546
  \(String(repeating: "=", count: 60))
200
547
  End of Debug Session Export
201
548
  """
202
-
549
+
203
550
  return exportContent
204
551
  }
205
552
 
@@ -380,7 +727,7 @@ class DebugOverlayViewController: UIViewController {
380
727
  shareButton.addTarget(self, action: #selector(shareButtonTapped), for: .touchUpInside)
381
728
  shareButton.translatesAutoresizingMaskIntoConstraints = false
382
729
  containerView.addSubview(shareButton)
383
-
730
+
384
731
  // X Close button in top-right corner
385
732
  let closeButton = UIButton(type: .system)
386
733
  closeButton.setTitle("✕", for: .normal)
@@ -538,10 +885,10 @@ class DebugOverlayViewController: UIViewController {
538
885
  @objc private func shareButtonTapped() {
539
886
  // Generate export content for the current history
540
887
  let exportContent = generateExportContent()
541
-
888
+
542
889
  // Create activity view controller for sharing
543
890
  let activityVC = UIActivityViewController(activityItems: [exportContent], applicationActivities: nil)
544
-
891
+
545
892
  // For iPad - prevent crash by setting popover presentation controller
546
893
  if let popover = activityVC.popoverPresentationController {
547
894
  // Find the share button view to anchor the popover
@@ -553,10 +900,10 @@ class DebugOverlayViewController: UIViewController {
553
900
  popover.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
554
901
  }
555
902
  }
556
-
903
+
557
904
  present(activityVC, animated: true)
558
905
  }
559
-
906
+
560
907
  /**
561
908
  * Generates formatted export content for sharing
562
909
  * @return Formatted string containing all debug events
@@ -565,32 +912,32 @@ class DebugOverlayViewController: UIViewController {
565
912
  if history.isEmpty {
566
913
  return "No debug events recorded in this session."
567
914
  }
568
-
915
+
569
916
  let formatter = DateFormatter()
570
917
  formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
571
918
  let exportDate = formatter.string(from: Date())
572
-
919
+
573
920
  var exportContent = """
574
921
  Attentive React Native SDK - Debug Session Export
575
922
  Generated: \(exportDate)
576
923
  Total Events: \(history.count)
577
-
924
+
578
925
  \(String(repeating: "=", count: 60))
579
-
926
+
580
927
  """
581
-
928
+
582
929
  // Add all events in chronological order (oldest first for better readability)
583
930
  for (index, event) in history.enumerated() {
584
931
  exportContent += "Event #\(index + 1)\n"
585
932
  exportContent += event.formatForExport()
586
933
  exportContent += "\n"
587
934
  }
588
-
935
+
589
936
  exportContent += """
590
937
  \(String(repeating: "=", count: 60))
591
938
  End of Debug Session Export
592
939
  """
593
-
940
+
594
941
  return exportContent
595
942
  }
596
943
 
@@ -772,7 +1119,7 @@ class EventDetailViewController: UIViewController {
772
1119
  shareButton.addTarget(self, action: #selector(shareEventButtonTapped), for: .touchUpInside)
773
1120
  shareButton.translatesAutoresizingMaskIntoConstraints = false
774
1121
  containerView.addSubview(shareButton)
775
-
1122
+
776
1123
  let closeButton = UIButton(type: .system)
777
1124
  closeButton.setTitle("✕", for: .normal)
778
1125
  closeButton.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .medium)
@@ -834,19 +1181,19 @@ class EventDetailViewController: UIViewController {
834
1181
  */
835
1182
  @objc private func shareEventButtonTapped() {
836
1183
  let exportContent = generateSingleEventExport()
837
-
1184
+
838
1185
  // Create activity view controller for sharing
839
1186
  let activityVC = UIActivityViewController(activityItems: [exportContent], applicationActivities: nil)
840
-
1187
+
841
1188
  // For iPad - prevent crash by setting popover presentation controller
842
1189
  if let popover = activityVC.popoverPresentationController {
843
1190
  popover.sourceView = view
844
1191
  popover.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
845
1192
  }
846
-
1193
+
847
1194
  present(activityVC, animated: true)
848
1195
  }
849
-
1196
+
850
1197
  /**
851
1198
  * Generates formatted export content for a single event
852
1199
  * @return Formatted string containing the single debug event
@@ -855,18 +1202,18 @@ class EventDetailViewController: UIViewController {
855
1202
  let formatter = DateFormatter()
856
1203
  formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
857
1204
  let exportDate = formatter.string(from: Date())
858
-
1205
+
859
1206
  let eventContent = """
860
1207
  Attentive React Native SDK - Single Event Export
861
1208
  Generated: \(exportDate)
862
-
1209
+
863
1210
  \(String(repeating: "=", count: 60))
864
-
1211
+
865
1212
  \(event.formatForExport())
866
1213
  \(String(repeating: "=", count: 60))
867
1214
  End of Single Event Export
868
1215
  """
869
-
1216
+
870
1217
  return eventContent
871
1218
  }
872
1219
 
@@ -5,3 +5,6 @@
5
5
  #import <React/RCTBridgeModule.h>
6
6
  #import <React/RCTViewManager.h>
7
7
  #import <React/RCTLog.h>
8
+
9
+ // Import UserNotifications for push notification support
10
+ @import UserNotifications;