@dashx/react-native 1.1.1 → 1.1.3

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 (132) hide show
  1. package/.DS_Store +0 -0
  2. package/.claude/settings.local.json +14 -0
  3. package/.github/workflows/ci.yml +28 -0
  4. package/CONTRIBUTING.md +153 -0
  5. package/DashXReactNative.podspec +6 -2
  6. package/README.md +259 -97
  7. package/__tests__/index.test.js +363 -0
  8. package/android/.gitignore +10 -0
  9. package/android/.gradle/7.2/dependencies-accessors/dependencies-accessors.lock +0 -0
  10. package/android/.gradle/7.2/dependencies-accessors/gc.properties +0 -0
  11. package/android/.gradle/7.2/fileChanges/last-build.bin +0 -0
  12. package/android/.gradle/7.2/fileHashes/fileHashes.lock +0 -0
  13. package/android/.gradle/7.2/gc.properties +0 -0
  14. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  15. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  16. package/android/.gradle/checksums/checksums.lock +0 -0
  17. package/android/.gradle/checksums/md5-checksums.bin +0 -0
  18. package/android/.gradle/checksums/sha1-checksums.bin +0 -0
  19. package/android/.gradle/vcs-1/gc.properties +0 -0
  20. package/android/build/.transforms/9dceace6c3787533d2dbb1a387e9284c/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/dashx/rn/sdk/DashXReactNativeFirebaseMessagingService.dex +0 -0
  21. package/android/build/.transforms/9dceace6c3787533d2dbb1a387e9284c/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/dashx/rn/sdk/DashXReactNativeModule.dex +0 -0
  22. package/android/build/.transforms/9dceace6c3787533d2dbb1a387e9284c/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/dashx/rn/sdk/MessageReceivedEventHolder.dex +0 -0
  23. package/android/build/.transforms/9dceace6c3787533d2dbb1a387e9284c/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/dashx/rn/sdk/util/Utils$WhenMappings.dex +0 -0
  24. package/android/build/.transforms/9dceace6c3787533d2dbb1a387e9284c/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/dashx/rn/sdk/util/Utils.dex +0 -0
  25. package/android/build/.transforms/9dceace6c3787533d2dbb1a387e9284c/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
  26. package/android/build/.transforms/f3c6049e7ab4bb564c79f3a246b8575e/transformed/classes/classes_dex/classes.dex +0 -0
  27. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +16 -0
  28. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  29. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +31 -5
  30. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +16 -0
  31. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/dashx/rn/sdk/DashXReactNativeFirebaseMessagingService.class +0 -0
  32. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/dashx/rn/sdk/DashXReactNativeModule.class +0 -0
  33. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/dashx/rn/sdk/MessageReceivedEventHolder.class +0 -0
  34. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/dashx/rn/sdk/util/Utils$WhenMappings.class +0 -0
  35. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/dashx/rn/sdk/util/Utils.class +0 -0
  36. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  37. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  38. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream +0 -0
  39. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream.len +0 -0
  40. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.len +0 -0
  41. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  42. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i +0 -0
  43. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  44. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  45. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  46. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
  47. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
  48. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  49. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  50. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  51. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  52. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
  53. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
  54. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  55. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  56. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  57. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  58. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
  59. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
  60. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  61. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab +0 -0
  62. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.values.at +0 -0
  63. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  64. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  65. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  66. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
  67. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  68. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  69. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  70. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream +0 -0
  71. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len +0 -0
  72. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.len +0 -0
  73. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  74. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i +0 -0
  75. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  76. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream +0 -0
  77. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream.len +0 -0
  78. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.len +0 -0
  79. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  80. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i +0 -0
  81. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  82. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
  83. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
  84. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.len +0 -0
  85. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
  86. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
  87. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +1 -1
  88. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
  89. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream +0 -0
  90. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream.len +0 -0
  91. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.len +0 -0
  92. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
  93. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i +0 -0
  94. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
  95. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
  96. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
  97. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
  98. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
  99. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
  100. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  101. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  102. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  103. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
  104. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  105. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  106. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  107. package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
  108. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  109. package/android/build/outputs/logs/manifest-merger-debug-report.txt +25 -3
  110. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  111. package/android/build/tmp/kotlin-classes/debug/com/dashx/rn/sdk/DashXReactNativeFirebaseMessagingService.class +0 -0
  112. package/android/build/tmp/kotlin-classes/debug/com/dashx/rn/sdk/DashXReactNativeModule.class +0 -0
  113. package/android/build/tmp/kotlin-classes/debug/com/dashx/rn/sdk/MessageReceivedEventHolder.class +0 -0
  114. package/android/build/tmp/kotlin-classes/debug/com/dashx/rn/sdk/util/Utils$WhenMappings.class +0 -0
  115. package/android/build/tmp/kotlin-classes/debug/com/dashx/rn/sdk/util/Utils.class +0 -0
  116. package/android/build.gradle +4 -1
  117. package/android/src/main/AndroidManifest.xml +15 -3
  118. package/android/src/main/java/com/dashx/rn/sdk/DashXReactNativeFirebaseMessagingService.kt +72 -0
  119. package/android/src/main/java/com/dashx/rn/sdk/DashXReactNativeModule.kt +84 -15
  120. package/android/src/main/java/com/dashx/rn/sdk/MessageReceivedEventHolder.kt +14 -0
  121. package/android/src/main/java/com/dashx/rn/sdk/util/Utils.kt +14 -0
  122. package/babel.config.js +1 -1
  123. package/index.d.ts +107 -0
  124. package/index.js +81 -4
  125. package/ios/.DS_Store +0 -0
  126. package/ios/DashXEventEmitter.swift +3 -2
  127. package/ios/DashXRCTAppDelegate.swift +110 -44
  128. package/ios/DashXReactNative-Bridging-Header.h +2 -0
  129. package/ios/DashXReactNative.h +1 -0
  130. package/ios/DashXReactNative.mm +16 -0
  131. package/ios/DashXReactNative.swift +118 -28
  132. package/package.json +18 -5
package/README.md CHANGED
@@ -30,7 +30,7 @@ _DashX SDK for React Native_
30
30
 
31
31
  | Platform | Minimum Version |
32
32
  |----------|----------------|
33
- | iOS | 12.0+ |
33
+ | iOS | 13.0+ |
34
34
  | Android | SDK 26 (Android 8.0)+ |
35
35
  | React Native | 0.71+ |
36
36
  | Node.js | 16.0+ |
@@ -76,33 +76,67 @@ apply plugin: 'com.google.gms.google-services'
76
76
 
77
77
  ### Setup for iOS
78
78
 
79
- 1. Add Firebase pods to your `Podfile`:
79
+ #### Step 1: Add pods to your Podfile
80
+
81
+ The DashX iOS SDK is not published to CocoaPods trunk. Add it directly from GitHub along with the Firebase pods required for FCM push notifications.
82
+
83
+ Add `use_modular_headers!` at the top of your `ios/Podfile`, then add the pods inside the target block:
80
84
 
81
85
  ```ruby
82
- pod 'FirebaseMessaging', :modular_headers => true
86
+ use_modular_headers!
87
+
88
+ target 'YourApp' do
89
+ # ... other pods
90
+
91
+ pod 'DashX', :git => 'https://github.com/dashxhq/dashx-ios.git', :tag => '1.1.4'
92
+ pod 'FirebaseCore'
93
+ pod 'FirebaseMessaging'
94
+
95
+ use_react_native!(...)
96
+ end
83
97
  ```
84
98
 
85
- 2. Run pod install:
99
+ Then run:
86
100
 
87
101
  ```sh
88
102
  cd ios && pod install
89
103
  ```
90
104
 
91
- 3. Add your iOS app on [Firebase Console](https://console.firebase.google.com/): **Project Overview > Add App > iOS**
105
+ #### Step 2: Add GoogleService-Info.plist
106
+
107
+ 1. Add your iOS app on [Firebase Console](https://console.firebase.google.com/): **Project Overview > Add App > iOS**
108
+ 2. Download `GoogleService-Info.plist` and add it to your Xcode project (right-click project root > **Add Files to "YourApp"**, ensure **Copy items if needed** is checked)
109
+
110
+ #### Step 3: Enable Push Notifications capability
92
111
 
93
- 4. Download `GoogleService-Info.plist` and add it to your Xcode project (right-click project > **Add Files**, ensure **Copy items if needed** is checked).
112
+ In Xcode, select your app target > **Signing & Capabilities** > **+ Capability** > add **Push Notifications**.
94
113
 
95
- #### Option A: Using `DashXRCTAppDelegate` (Recommended)
114
+ #### Step 4: Configure your AppDelegate
96
115
 
97
- The SDK ships a `DashXRCTAppDelegate` class that handles Firebase configuration, FCM token management, and push notification display out of the box. Subclass it in your `AppDelegate`:
116
+ The SDK ships `DashXRCTAppDelegate` which handles permission requests, notification display, and universal link forwarding. Subclass it in your `AppDelegate.swift` and configure Firebase directly:
98
117
 
99
118
  ```swift
100
119
  import DashXReactNative
120
+ import ReactAppDependencyProvider
121
+ import FirebaseCore
122
+ import FirebaseMessaging
123
+ import DashX
124
+ import UserNotifications
101
125
 
102
126
  @main
103
127
  class AppDelegate: DashXRCTAppDelegate {
104
- override func sourceURL(for bridge: RCTBridge) -> URL? {
105
- bundleURL()
128
+ override var moduleName: String { "YourAppName" }
129
+
130
+ override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
131
+ self.dependencyProvider = RCTAppDependencyProvider()
132
+ FirebaseApp.configure()
133
+ Messaging.messaging().delegate = self
134
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
135
+ }
136
+
137
+ // Forward the APNS token to Firebase so it can exchange it for an FCM token
138
+ override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
139
+ Messaging.messaging().setAPNSToken(deviceToken, type: .unknown)
106
140
  }
107
141
 
108
142
  override func bundleURL() -> URL? {
@@ -112,47 +146,63 @@ class AppDelegate: DashXRCTAppDelegate {
112
146
  Bundle.main.url(forResource: "main", withExtension: "jsbundle")
113
147
  #endif
114
148
  }
149
+ }
115
150
 
116
- // Called when a notification is tapped
117
- override func notificationClicked(message: [AnyHashable: Any], actionIdentifier: String) {
118
- // Handle notification tap
119
- }
120
-
121
- // Called when a notification arrives while the app is in the foreground
122
- override func notificationDeliveredInForeground(message: [AnyHashable: Any]) -> UNNotificationPresentationOptions {
123
- return [.banner, .sound]
151
+ // Receive the FCM token and pass it to DashX
152
+ extension AppDelegate: MessagingDelegate {
153
+ func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
154
+ guard let token = fcmToken else { return }
155
+ DashX.setFCMToken(to: token)
124
156
  }
125
157
  }
126
158
  ```
127
159
 
128
- #### Option B: Manual setup in AppDelegate
160
+ `DashXRCTAppDelegate` handles the rest automatically:
161
+ - Requests notification permission on launch
162
+ - Displays notifications (banner + sound by default, even while app is in the foreground)
163
+ - Tracks delivery, clicks, and dismissals
164
+ - Forwards universal links to the JS `onLinkReceived` listener
129
165
 
130
- If you prefer manual control, import Firebase at the top of `/ios/{projectName}/AppDelegate.m`:
166
+ **Override points** (all optional):
131
167
 
132
- ```objective-c
133
- #import <FirebaseCore/FirebaseCore.h>
134
- #import <FirebaseMessaging/FirebaseMessaging.h>
168
+ ```swift
169
+ /// Control foreground notification presentation (default: [.banner, .sound])
170
+ override func notificationDeliveredInForeground(message: [AnyHashable: Any]) -> UNNotificationPresentationOptions {
171
+ return [.banner, .sound, .badge]
172
+ }
173
+
174
+ // Called when the user taps a notification or one of its action buttons
175
+ override func notificationClicked(message: [AnyHashable: Any], actionIdentifier: String) {
176
+ // custom tap handling
177
+ }
178
+
179
+ // Called when a universal/deep link arrives
180
+ override func handleLink(url: URL) {
181
+ // custom link handling
182
+ }
135
183
  ```
136
184
 
137
- Then inside `didFinishLaunchingWithOptions`:
185
+ #### Step 5: Subscribe in JavaScript
138
186
 
139
- ```objective-c
140
- [UNUserNotificationCenter currentNotificationCenter].delegate = self;
141
- UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
142
- [[UNUserNotificationCenter currentNotificationCenter]
143
- requestAuthorizationWithOptions:authOptions
144
- completionHandler:^(BOOL granted, NSError * _Nullable error) {
145
- }];
187
+ Call `DashX.subscribe()` at app startup so the SDK registers the device as soon as the FCM token is available:
146
188
 
147
- [application registerForRemoteNotifications];
189
+ ```js
190
+ import { useEffect } from 'react';
191
+ import DashX from '@dashx/react-native';
148
192
 
149
- if ([FIRApp defaultApp] == nil) {
150
- [FIRApp configure];
151
- }
193
+ DashX.configure({ publicKey: 'your-public-key' });
152
194
 
153
- [FIRMessaging messaging].delegate = self;
195
+ export default function App() {
196
+ useEffect(() => {
197
+ DashX.subscribe();
198
+ }, []);
199
+
200
+ // ...
201
+ }
154
202
  ```
155
203
 
204
+ > **Without Firebase:** If you are not using FCM, omit the Firebase pods and the Firebase-related `AppDelegate` code. The SDK will still handle APNS permission requests and notification display via `DashXRCTAppDelegate`.
205
+
156
206
  ## Usage
157
207
 
158
208
  ```js
@@ -161,16 +211,25 @@ import DashX from '@dashx/react-native';
161
211
 
162
212
  ### Configure
163
213
 
164
- Initialize the SDK as early as possible (e.g. in your root component or `App.js`):
214
+ Initialize the SDK once at app startup. Call `configure` at the top level of your entry file (e.g. `App.js`) so it runs before any component mounts:
165
215
 
166
216
  ```js
217
+ // App.js — call at module level, before the component
218
+ import DashX from '@dashx/react-native';
219
+
167
220
  DashX.configure({
168
221
  publicKey: 'your-public-key',
169
222
  // baseURI: 'https://api.dashx.com', // optional, custom API endpoint
170
223
  // targetEnvironment: 'production', // optional
171
224
  });
225
+
226
+ export default function App() {
227
+ // ...
228
+ }
172
229
  ```
173
230
 
231
+ Calling from `useEffect` in your root component also works, but module-level init is preferred so the SDK is ready as early as possible.
232
+
174
233
  | Option | Type | Required | Description |
175
234
  |--------|------|----------|-------------|
176
235
  | `publicKey` | `string` | Yes | Your DashX public key |
@@ -218,6 +277,51 @@ DashX.screen('HomeScreen', {
218
277
  });
219
278
  ```
220
279
 
280
+ ### Fetch a record
281
+
282
+ Fetch a single record by URN (e.g. `"article/uuid"`). The URN must use the full UUID form of the record ID.
283
+
284
+ ```js
285
+ const record = await DashX.fetchRecord('article/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', {
286
+ preview: false,
287
+ language: 'en',
288
+ // fields, include, exclude as arrays of objects if needed
289
+ });
290
+ ```
291
+
292
+ | Option | Type | Description |
293
+ |--------|------|-------------|
294
+ | `preview` | `boolean` | Include draft/preview content |
295
+ | `language` | `string` | Content language |
296
+ | `fields` | `object[]` | Fields to return |
297
+ | `include` | `object[]` | Related data to include |
298
+ | `exclude` | `object[]` | Fields to exclude |
299
+
300
+ ### Search records
301
+
302
+ ```js
303
+ const records = await DashX.searchRecords('article', {
304
+ filter: { status: 'published' },
305
+ order: [{ field: 'createdAt', direction: 'desc' }],
306
+ limit: 10,
307
+ page: 1,
308
+ preview: false,
309
+ language: 'en',
310
+ });
311
+ ```
312
+
313
+ | Option | Type | Description |
314
+ |--------|------|-------------|
315
+ | `filter` | `object` | Filter criteria |
316
+ | `order` | `object[]` | Sort order (e.g. `[{ field, direction }]`) |
317
+ | `limit` | `number` | Max records to return |
318
+ | `page` | `number` | Page number for pagination |
319
+ | `preview` | `boolean` | Preview/draft content |
320
+ | `language` | `string` | Content language |
321
+ | `fields` | `object[]` | Fields to return |
322
+ | `include` | `object[]` | Related data to include |
323
+ | `exclude` | `object[]` | Fields to exclude |
324
+
221
325
  ### Push notifications
222
326
 
223
327
  Subscribe the current device to push notifications:
@@ -232,6 +336,31 @@ Unsubscribe:
232
336
  DashX.unsubscribe();
233
337
  ```
234
338
 
339
+ ### Request notification permission (iOS)
340
+
341
+ Prompt the user for push notification permission. Returns a `NotificationPermissionStatus` integer matching `UNAuthorizationStatus`:
342
+
343
+ | Value | Meaning |
344
+ |-------|---------|
345
+ | `0` | Not determined |
346
+ | `1` | Denied |
347
+ | `2` | Authorized |
348
+ | `3` | Provisional |
349
+ | `4` | Ephemeral |
350
+
351
+ ```js
352
+ const status = await DashX.requestNotificationPermission();
353
+ if (status === 2) {
354
+ DashX.subscribe();
355
+ }
356
+ ```
357
+
358
+ To check the current status without prompting the user:
359
+
360
+ ```js
361
+ const status = await DashX.getNotificationPermissionStatus();
362
+ ```
363
+
235
364
  ### Listen for incoming messages
236
365
 
237
366
  Register a listener to handle push notifications when they arrive:
@@ -248,13 +377,26 @@ useEffect(() => {
248
377
  }, []);
249
378
  ```
250
379
 
380
+ ### Listen for deep links / universal links (iOS)
381
+
382
+ Register a listener that fires when the SDK receives a universal link. On the native side, `DashXRCTAppDelegate` forwards links via `DashX.handleUserActivity` automatically.
383
+
384
+ ```js
385
+ useEffect(() => {
386
+ const subscription = DashX.onLinkReceived((url) => {
387
+ console.log('Link received:', url);
388
+ });
389
+
390
+ return () => subscription.remove();
391
+ }, []);
392
+ ```
393
+
251
394
  ### Notification preferences
252
395
 
253
396
  Fetch the user's stored notification preferences:
254
397
 
255
398
  ```js
256
399
  const preferences = await DashX.fetchStoredPreferences();
257
- console.log(preferences);
258
400
  ```
259
401
 
260
402
  Save updated preferences:
@@ -266,83 +408,103 @@ await DashX.saveStoredPreferences({
266
408
  });
267
409
  ```
268
410
 
269
- ### Logging
411
+ ### Upload an asset (iOS)
270
412
 
271
- Set the SDK log level for debugging:
413
+ Upload a local file to DashX and associate it with a resource and attribute:
272
414
 
273
415
  ```js
274
- DashX.setLogLevel(1); // 0 = off, 1 = errors, 2 = debug
416
+ const response = await DashX.uploadAsset(
417
+ '/path/to/local/file.jpg',
418
+ 'user', // resource name
419
+ 'avatar' // attribute name
420
+ );
421
+ // response: { status, data: { asset: { status, url, playbackIds } } }
275
422
  ```
276
423
 
277
- ### Android example
424
+ ### Fetch an asset (iOS)
278
425
 
279
- Below is a full example of integrating DashX in a React Native Android app:
426
+ Retrieve the status and URL of a previously uploaded asset by its ID:
280
427
 
281
428
  ```js
282
- // App.js
283
- import React, { useEffect } from 'react';
284
- import { View, Text, Button, StyleSheet } from 'react-native';
285
- import DashX from '@dashx/react-native';
429
+ const asset = await DashX.fetchAsset('asset-id');
430
+ // asset: { status, data: { asset: { status, url, playbackIds } } }
431
+ ```
286
432
 
287
- export default function App() {
288
- useEffect(() => {
289
- DashX.configure({ publicKey: 'your-public-key' });
290
- DashX.subscribe();
291
- }, []);
433
+ ### Lifecycle tracking (iOS)
292
434
 
293
- const handleLogin = () => {
294
- DashX.setIdentity('user-123', 'auth-token');
295
- DashX.identify({
296
- firstName: 'Jane',
297
- lastName: 'Doe',
298
- email: 'jane@example.com',
299
- });
300
- };
301
-
302
- const handleTrack = () => {
303
- DashX.track('Button Pressed', { screen: 'Home' });
304
- };
305
-
306
- const handleLogout = () => {
307
- DashX.unsubscribe();
308
- DashX.reset();
309
- };
310
-
311
- return (
312
- <View style={styles.container}>
313
- <Text style={styles.title}>DashX React Native Demo</Text>
314
- <Button title="Login" onPress={handleLogin} />
315
- <Button title="Track Event" onPress={handleTrack} />
316
- <Button title="Logout" onPress={handleLogout} />
317
- </View>
318
- );
319
- }
435
+ Enable automatic app lifecycle event tracking (app opened, backgrounded, etc.):
320
436
 
321
- const styles = StyleSheet.create({
322
- container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
323
- title: { fontSize: 24, marginBottom: 20 },
324
- });
437
+ ```js
438
+ DashX.enableLifecycleTracking(); // no-op on Android
439
+ ```
440
+
441
+ ### Ad tracking / ATT (iOS)
442
+
443
+ Request App Tracking Transparency permission and enable IDFA tracking:
444
+
445
+ ```js
446
+ DashX.enableAdTracking(); // no-op on Android
447
+ ```
448
+
449
+ Make sure `NSUserTrackingUsageDescription` is set in your `Info.plist`.
450
+
451
+ ### Logging
452
+
453
+ Set the SDK log level for debugging:
454
+
455
+ ```js
456
+ DashX.setLogLevel(1); // 0 = off, 1 = errors, 2 = debug
325
457
  ```
326
458
 
327
459
  ## API Reference
328
460
 
329
- | Method | Signature | Returns | Description |
330
- |--------|-----------|---------|-------------|
331
- | `configure` | `(options: object)` | `void` | Initialize the SDK with your public key |
332
- | `identify` | `(options: object)` | `void` | Identify the current user with traits |
333
- | `setIdentity` | `(uid: string, token: string)` | `void` | Set user identity with UID and auth token |
334
- | `reset` | `()` | `void` | Clear the current user identity |
335
- | `track` | `(event: string, data?: object)` | `void` | Track a named event with optional data |
336
- | `screen` | `(screenName: string, data?: object)` | `void` | Track a screen view with optional data |
337
- | `subscribe` | `()` | `void` | Subscribe device for push notifications |
338
- | `unsubscribe` | `()` | `void` | Unsubscribe device from push notifications |
339
- | `fetchStoredPreferences` | `()` | `Promise<object>` | Fetch stored notification preferences |
340
- | `saveStoredPreferences` | `(data: object)` | `Promise<object>` | Save notification preferences |
341
- | `setLogLevel` | `(level: number)` | `void` | Set SDK log verbosity |
342
- | `onMessageReceived` | `(callback: function)` | `EmitterSubscription` | Listen for incoming push notifications |
461
+ | Method | Signature | Platforms | Returns | Description |
462
+ |--------|-----------|-----------|---------|-------------|
463
+ | `configure` | `(options: object)` | iOS, Android | `void` | Initialize the SDK with your public key |
464
+ | `identify` | `(options: object)` | iOS, Android | `void` | Identify the current user with traits |
465
+ | `setIdentity` | `(uid: string, token: string)` | iOS, Android | `void` | Set user identity with UID and auth token |
466
+ | `reset` | `()` | iOS, Android | `void` | Clear the current user identity |
467
+ | `track` | `(event: string, data?: object)` | iOS, Android | `void` | Track a named event with optional data |
468
+ | `screen` | `(screenName: string, data?: object)` | iOS, Android | `void` | Track a screen view |
469
+ | `fetchRecord` | `(urn: string, options?: object)` | iOS, Android | `Promise<object>` | Fetch a single record by URN (`"resource/uuid"`) |
470
+ | `searchRecords` | `(resource: string, options?: object)` | iOS, Android | `Promise<object[]>` | Search records with filter, order, limit, page, etc. |
471
+ | `subscribe` | `()` | iOS, Android | `void` | Subscribe device for push notifications |
472
+ | `unsubscribe` | `()` | iOS, Android | `void` | Unsubscribe device from push notifications |
473
+ | `fetchStoredPreferences` | `()` | iOS, Android | `Promise<object>` | Fetch stored notification preferences |
474
+ | `saveStoredPreferences` | `(data: object)` | iOS, Android | `Promise<object>` | Save notification preferences |
475
+ | `setLogLevel` | `(level: number)` | iOS, Android | `void` | Set SDK log verbosity (0=off, 1=errors, 2=debug) |
476
+ | `onMessageReceived` | `(callback: function)` | iOS, Android | `EmitterSubscription` | Listen for incoming push notifications |
477
+ | `requestNotificationPermission` | `()` | iOS | `Promise<number>` | Prompt for push permission; resolves with `UNAuthorizationStatus` (0-4) |
478
+ | `getNotificationPermissionStatus` | `()` | iOS | `Promise<number>` | Get current push permission status without prompting |
479
+ | `onLinkReceived` | `(callback: function)` | iOS | `EmitterSubscription` | Listen for universal/deep link events |
480
+ | `uploadAsset` | `(filePath: string, resource: string, attribute: string)` | iOS | `Promise<AssetResponse>` | Upload a local file and associate it with a resource attribute |
481
+ | `fetchAsset` | `(assetId: string)` | iOS | `Promise<AssetResponse>` | Fetch status and URL of an uploaded asset |
482
+ | `enableLifecycleTracking` | `()` | iOS | `void` | Enable automatic app lifecycle event tracking |
483
+ | `enableAdTracking` | `()` | iOS | `void` | Request ATT permission and enable IDFA tracking |
343
484
 
344
485
  For detailed usage and advanced features, refer to the [DashX documentation](https://docs.dashx.com/developer).
345
486
 
487
+ ### Error Codes
488
+
489
+ Promise rejections from native methods use a consistent error code across both platforms:
490
+
491
+ | Code | Description |
492
+ |------|-------------|
493
+ | `EUNSPECIFIED` | An unspecified error occurred |
494
+
495
+ The error code is available on the `code` property of the rejected error:
496
+
497
+ ```js
498
+ try {
499
+ await DashX.fetchRecord('article/123');
500
+ } catch (error) {
501
+ console.log(error.code); // "EUNSPECIFIED"
502
+ console.log(error.message); // human-readable description
503
+ }
504
+ ```
505
+
506
+ A `DashXErrorCode` enum is exported from the TypeScript definitions for type-safe comparisons.
507
+
346
508
  ## Contributing
347
509
 
348
510
  Please follow [these steps](https://github.com/dashxhq/dashx-js/tree/master/examples/react-native#setting-up-development-environment) to set up the development environment.