@attentive-mobile/attentive-react-native-sdk 2.0.0-beta.7 → 2.0.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.
- package/README.md +77 -112
- package/android/src/main/kotlin/com/attentivereactnativesdk/AttentiveReactNativeSdkModule.kt +35 -45
- package/package.json +6 -2
- package/ios/AttentiveReactNativeSdk.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/ios/AttentiveReactNativeSdk.xcodeproj/project.xcworkspace/xcuserdata/zheref.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/AttentiveReactNativeSdk.xcodeproj/xcuserdata/zheref.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
package/README.md
CHANGED
|
@@ -81,7 +81,12 @@ Attentive.initialize(config);
|
|
|
81
81
|
|
|
82
82
|
#### Android — Initialize from Native Code
|
|
83
83
|
|
|
84
|
-
On Android, `AttentiveSdk.initialize()` **must** be called from your `Application.onCreate()` in native Kotlin/Java code.
|
|
84
|
+
On Android, `AttentiveSdk.initialize()` **must** be called from your `Application.onCreate()` in native Kotlin/Java code. There are two reasons for this:
|
|
85
|
+
|
|
86
|
+
1. **Lifecycle observers must be registered before the React Native bridge is ready.** Internally, the SDK creates an `AppLaunchTracker` that calls `lifecycle.addObserver()` on the `ProcessLifecycleOwner`. If initialization happens after the bridge starts, early app-launch events can be missed.
|
|
87
|
+
2. **`lifecycle.addObserver()` requires the main thread.** AndroidX enforces this with an `IllegalStateException` if called from a background thread. `Application.onCreate()` is guaranteed by the Android system to run on the main thread, so calling `initialize` there satisfies this requirement automatically — no extra threading machinery needed.
|
|
88
|
+
|
|
89
|
+
> **Do not** call `AttentiveSdk.initialize()` from a background thread or a coroutine dispatcher other than `Dispatchers.Main`. Doing so will throw an `IllegalStateException` from inside the AndroidX Lifecycle library.
|
|
85
90
|
|
|
86
91
|
Add the following to your `MainApplication.kt` (or `MainApplication.java`):
|
|
87
92
|
|
|
@@ -90,7 +95,6 @@ import android.app.Application
|
|
|
90
95
|
import com.attentive.androidsdk.AttentiveConfig
|
|
91
96
|
import com.attentive.androidsdk.AttentiveSdk
|
|
92
97
|
import com.attentive.androidsdk.AttentiveLogLevel
|
|
93
|
-
import com.facebook.react.bridge.UiThreadUtil
|
|
94
98
|
|
|
95
99
|
class MainApplication : Application(), ReactApplication {
|
|
96
100
|
|
|
@@ -105,19 +109,43 @@ class MainApplication : Application(), ReactApplication {
|
|
|
105
109
|
.applicationContext(this)
|
|
106
110
|
.domain("YOUR_ATTENTIVE_DOMAIN")
|
|
107
111
|
.mode(AttentiveConfig.Mode.PRODUCTION) // or Mode.DEBUG for testing
|
|
112
|
+
.notificationIconId(R.drawable.ic_stat_notification)
|
|
108
113
|
.skipFatigueOnCreatives(false)
|
|
109
114
|
.logLevel(AttentiveLogLevel.VERBOSE)
|
|
110
115
|
.build()
|
|
111
116
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
117
|
+
// Application.onCreate() is always called on the main thread by the Android system,
|
|
118
|
+
// so no thread-switching wrapper is needed here.
|
|
119
|
+
AttentiveSdk.initialize(config)
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
122
|
```
|
|
119
123
|
|
|
120
|
-
|
|
124
|
+
##### Android notification small icon
|
|
125
|
+
|
|
126
|
+
Android requires push notifications to use a small status-bar icon. If `notificationIconId` is not set, the Attentive Android SDK uses its default fallback icon. To use your app's branding, add a notification small icon resource to your host app and pass its resource ID during native initialization.
|
|
127
|
+
|
|
128
|
+
Add the icon to your Android app's drawable resources:
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
android/app/src/main/res/drawable/ic_stat_notification.png
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Use a white-only transparent PNG designed for Android notification status bars. Do not use the full-color launcher icon from `mipmap-*`, because Android masks small notification icons and it can render poorly.
|
|
135
|
+
|
|
136
|
+
Then add the icon to your existing `AttentiveConfig.Builder()` chain in `MainApplication.kt` before `build()`:
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
android/app/src/main/java/<your-package>/MainApplication.kt
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```kotlin
|
|
143
|
+
.notificationIconId(R.drawable.ic_stat_notification)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`MainApplication.kt` is host-app code, not generated by this SDK at runtime. If `R.drawable.ic_stat_notification` does not resolve, confirm the drawable file name matches exactly and that you are referencing your app module's `R` class.
|
|
147
|
+
|
|
148
|
+
After the native initialization, all other SDK operations (`identify`, `recordAddToCartEvent`, `recordPurchaseEvent`, etc.) are called from TypeScript as normal on both platforms.
|
|
121
149
|
|
|
122
150
|
> **Tip:** If you see `[AttentiveSDK] recordAddToCartEvent failed — SDK may not be initialized` in your Android logcat, it means `AttentiveSdk.initialize()` was not called from native code before the event was recorded. Check your `Application.onCreate()` setup.
|
|
123
151
|
|
|
@@ -218,7 +246,7 @@ Attentive.identify({phone: '+15556667777'};)
|
|
|
218
246
|
|
|
219
247
|
### Push Notifications (iOS and Android)
|
|
220
248
|
|
|
221
|
-
The SDK supports push notification integration on both iOS (APNs) and Android (
|
|
249
|
+
The SDK supports push notification integration on both iOS (APNs) and Android (FCM). The following sections cover iOS-specific setup flows. On Android, push notification integration is handled entirely in native Kotlin/Java code — see [App Events on Android](#app-events-on-android) for details.
|
|
222
250
|
|
|
223
251
|
> **iOS — required setup:** Your AppDelegate **must** forward notification
|
|
224
252
|
> responses to the SDK for push tracking to work. Add this single line to your
|
|
@@ -243,22 +271,9 @@ The SDK supports push notification integration on both iOS (APNs) and Android (r
|
|
|
243
271
|
|
|
244
272
|
### App Events on Android
|
|
245
273
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
| SDK method | Purpose on Android |
|
|
249
|
-
|------------|--------------------|
|
|
250
|
-
| `getPushAuthorizationStatus()` | Returns `authorized`, `denied`, or `notDetermined` (uses `POST_NOTIFICATIONS` on API 33+). Use before `handleRegularOpen` so tracking uses the correct status. |
|
|
251
|
-
| `registerForPushNotifications()` | Requests `POST_NOTIFICATIONS` on Android 13+; no-op on older versions. |
|
|
252
|
-
| `handleRegularOpen(authStatus)` | Tracks a regular app open (launch or return to foreground). Call after `identify()` and pass the result of `getPushAuthorizationStatus()`. |
|
|
253
|
-
| `registerDeviceToken` / `registerDeviceTokenWithCallback` | Optional. Register your FCM token when using Firebase Cloud Messaging. |
|
|
254
|
-
| `handlePushOpen` / `handleForegroundPush` | Optional. Call when the user opens a notification or receives one in the foreground. |
|
|
274
|
+
On Android, **regular app open and foreground events are handled automatically** by the native Android SDK once `AttentiveSdk.initialize()` is called from `Application.onCreate()` (see [Android Native Initialization](#android--initialize-from-native-code)). The lifecycle observers registered during initialization (e.g. `AppLaunchTracker`) take care of this transparently — there is no need to manually call `handleRegularOpen` or subscribe to `AppState` changes.
|
|
255
275
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
- **Regular app open** – Call `handleRegularOpen(authorizationStatus)` when the app is opened (launch or returning to foreground). The SDK uses this for tracking and the `/mtctrl` endpoint.
|
|
259
|
-
- **Permission status** – On Android 13+ (API 33+), notification permission is `POST_NOTIFICATIONS`. The SDK exposes `getPushAuthorizationStatus()` so you can pass the correct status into `handleRegularOpen`.
|
|
260
|
-
- **Requesting permission** – Call `registerForPushNotifications()` to trigger the system permission dialog on Android 13+; it is a no-op on older versions.
|
|
261
|
-
- **Order of operations** – Always call `identify()` before any `handleRegularOpen()` so the SDK has user context for network requests.
|
|
276
|
+
The only TypeScript-side step required on Android is calling `identify()` with any available user identifiers as early as possible in your app’s lifecycle (e.g. in the root component `useEffect`).
|
|
262
277
|
|
|
263
278
|
#### Prerequisites
|
|
264
279
|
|
|
@@ -271,115 +286,66 @@ This section describes how to implement Attentive app events on Android so they
|
|
|
271
286
|
</manifest>
|
|
272
287
|
```
|
|
273
288
|
|
|
274
|
-
2. **
|
|
275
|
-
|
|
276
|
-
#### 1. On app launch (Android)
|
|
289
|
+
2. **Native initialization** – The SDK must be initialized from `Application.onCreate()` on Android (see [Android Native Initialization](#android--initialize-from-native-code) above). App open and lifecycle events are then tracked automatically.
|
|
277
290
|
|
|
278
|
-
|
|
291
|
+
#### TypeScript setup (Android)
|
|
279
292
|
|
|
280
|
-
|
|
281
|
-
2. Call `handleRegularOpen(authStatus)` with that status.
|
|
282
|
-
3. Optionally call `registerForPushNotifications()` to prompt for permission (Android 13+).
|
|
293
|
+
After native initialization, the only required TypeScript call is `identify()`:
|
|
283
294
|
|
|
284
295
|
```typescript
|
|
285
296
|
import { Platform } from 'react-native';
|
|
286
|
-
import {
|
|
287
|
-
initialize,
|
|
288
|
-
identify,
|
|
289
|
-
getPushAuthorizationStatus,
|
|
290
|
-
registerForPushNotifications,
|
|
291
|
-
handleRegularOpen,
|
|
292
|
-
type AttentiveSdkConfiguration,
|
|
293
|
-
type PushAuthorizationStatus,
|
|
294
|
-
} from 'attentive-react-native-sdk';
|
|
297
|
+
import { initialize, identify } from 'attentive-react-native-sdk';
|
|
295
298
|
|
|
296
299
|
// Inside your root component (e.g. App.tsx useEffect):
|
|
297
|
-
|
|
298
|
-
// iOS only: initialize from TypeScript.
|
|
299
|
-
// Android: initialization must be done natively from Application.onCreate() — see README.
|
|
300
300
|
if (Platform.OS === 'ios') {
|
|
301
301
|
initialize(config);
|
|
302
302
|
}
|
|
303
303
|
|
|
304
304
|
identify({ email: 'user@example.com', clientUserId: 'id-123' });
|
|
305
|
-
|
|
306
|
-
if (Platform.OS === 'android') {
|
|
307
|
-
getPushAuthorizationStatus()
|
|
308
|
-
.then((authStatus: PushAuthorizationStatus) => {
|
|
309
|
-
handleRegularOpen(authStatus);
|
|
310
|
-
})
|
|
311
|
-
.catch(() => {
|
|
312
|
-
handleRegularOpen('authorized'); // fallback
|
|
313
|
-
});
|
|
314
|
-
registerForPushNotifications(); // Shows permission dialog on Android 13+
|
|
315
|
-
}
|
|
316
305
|
```
|
|
317
306
|
|
|
318
|
-
####
|
|
307
|
+
#### Push notifications on Android (FCM)
|
|
319
308
|
|
|
320
|
-
|
|
309
|
+
The Attentive Android SDK registers its own `FirebaseMessagingService` automatically — **you do not need to create a subclass**. As long as your app is registered with Firebase and includes a valid `google-services.json`, the SDK handles FCM token registration and foreground push delivery for you. Follow the [Firebase Android setup guide](https://firebase.google.com/docs/cloud-messaging/android/client) to add FCM to your project.
|
|
321
310
|
|
|
322
|
-
|
|
323
|
-
import { AppState } from 'react-native';
|
|
324
|
-
import { getPushAuthorizationStatus, handleRegularOpen } from 'attentive-react-native-sdk';
|
|
325
|
-
import type { PushAuthorizationStatus } from 'attentive-react-native-sdk';
|
|
326
|
-
|
|
327
|
-
const subscription = AppState.addEventListener('change', (nextAppState) => {
|
|
328
|
-
if (nextAppState === 'active' && Platform.OS === 'android') {
|
|
329
|
-
getPushAuthorizationStatus()
|
|
330
|
-
.then((authStatus: PushAuthorizationStatus) => {
|
|
331
|
-
handleRegularOpen(authStatus);
|
|
332
|
-
})
|
|
333
|
-
.catch(() => {
|
|
334
|
-
handleRegularOpen('authorized');
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// Cleanup on unmount:
|
|
340
|
-
return () => subscription.remove();
|
|
341
|
-
```
|
|
311
|
+
##### If you already have a FirebaseMessagingService subclass
|
|
342
312
|
|
|
343
|
-
|
|
313
|
+
If your app has an existing `FirebaseMessagingService` subclass for other purposes, route Attentive messages through to the SDK:
|
|
344
314
|
|
|
345
|
-
|
|
315
|
+
```kotlin
|
|
316
|
+
import com.attentive.androidsdk.AttentiveSdk
|
|
317
|
+
import com.google.firebase.messaging.FirebaseMessagingService
|
|
318
|
+
import com.google.firebase.messaging.RemoteMessage
|
|
346
319
|
|
|
347
|
-
|
|
320
|
+
class YourFirebaseMessagingService : FirebaseMessagingService() {
|
|
348
321
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
authStatus,
|
|
356
|
-
(data, url, response, error) => {
|
|
357
|
-
if (error) {
|
|
358
|
-
console.error('Attentive token registration failed', error);
|
|
359
|
-
}
|
|
360
|
-
handleRegularOpen(authStatus);
|
|
322
|
+
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
|
323
|
+
super.onMessageReceived(remoteMessage)
|
|
324
|
+
if (AttentiveSdk.isAttentiveFirebaseMessage(remoteMessage)) {
|
|
325
|
+
AttentiveSdk.sendNotification(remoteMessage)
|
|
326
|
+
}
|
|
327
|
+
// Handle your own messages below...
|
|
361
328
|
}
|
|
362
|
-
|
|
363
|
-
});
|
|
329
|
+
}
|
|
364
330
|
```
|
|
365
331
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
If you handle FCM messages (e.g. with `@react-native-firebase/messaging`), you can report notification opens and foreground receives the same way as on iOS:
|
|
332
|
+
##### Notification opens (singleTask apps)
|
|
369
333
|
|
|
370
|
-
|
|
371
|
-
- **Notification received while app in foreground:** `handleForegroundPush(payload, authorizationStatus)`
|
|
334
|
+
React Native apps use `singleTask` launch mode by default. When a push notification is tapped while the app is in the background, Android delivers the intent via `onNewIntent()` rather than recreating the activity. Override `onNewIntent` in your `MainActivity` so the SDK can detect the notification tap:
|
|
372
335
|
|
|
373
|
-
|
|
336
|
+
```kotlin
|
|
337
|
+
import android.content.Intent
|
|
374
338
|
|
|
375
|
-
|
|
339
|
+
class MainActivity : ReactActivity() {
|
|
376
340
|
|
|
377
|
-
|
|
341
|
+
override fun onNewIntent(intent: Intent?) {
|
|
342
|
+
super.onNewIntent(intent)
|
|
343
|
+
intent?.let { setIntent(it) }
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
```
|
|
378
347
|
|
|
379
|
-
|
|
380
|
-
2. **Foreground:** `AppState.addEventListener('change', …)` → when `active` and Android → `getPushAuthorizationStatus()` → `handleRegularOpen(authStatus)`.
|
|
381
|
-
3. **Optional:** When FCM token is available → `registerDeviceTokenWithCallback(token, authStatus, callback)` → in callback call `handleRegularOpen(authStatus)`.
|
|
382
|
-
4. **Optional:** When user opens a notification or receives one in foreground → `handlePushOpen` / `handleForegroundPush` with payload and status from `getPushAuthorizationStatus()`.
|
|
348
|
+
Refer to the [Attentive Android SDK documentation](https://github.com/attentive-mobile/attentive-android-sdk) for the full list of native APIs available for push notification integration.
|
|
383
349
|
|
|
384
350
|
---
|
|
385
351
|
|
|
@@ -393,9 +359,9 @@ import { registerForPushNotifications } from 'attentive-react-native-sdk';
|
|
|
393
359
|
registerForPushNotifications();
|
|
394
360
|
```
|
|
395
361
|
|
|
396
|
-
#### Register Device Token (iOS
|
|
362
|
+
#### Register Device Token (iOS)
|
|
397
363
|
|
|
398
|
-
When your app receives
|
|
364
|
+
When your iOS app receives an APNs device token, register it with the Attentive backend:
|
|
399
365
|
|
|
400
366
|
```typescript
|
|
401
367
|
import { registerDeviceToken } from 'attentive-react-native-sdk';
|
|
@@ -412,7 +378,7 @@ The `authorizationStatus` parameter should be one of:
|
|
|
412
378
|
- `'provisional'` - Provisional authorization (quiet notifications)
|
|
413
379
|
- `'ephemeral'` - App Clip notifications
|
|
414
380
|
|
|
415
|
-
#### Handle Push Notification Opens (iOS
|
|
381
|
+
#### Handle Push Notification Opens (iOS)
|
|
416
382
|
|
|
417
383
|
When a user taps on a push notification, track the event:
|
|
418
384
|
|
|
@@ -428,7 +394,7 @@ handlePushOpened(
|
|
|
428
394
|
);
|
|
429
395
|
```
|
|
430
396
|
|
|
431
|
-
#### Handle Foreground Notifications (iOS
|
|
397
|
+
#### Handle Foreground Notifications (iOS)
|
|
432
398
|
|
|
433
399
|
When a notification arrives while the app is in the foreground:
|
|
434
400
|
|
|
@@ -523,9 +489,8 @@ func application(
|
|
|
523
489
|
```
|
|
524
490
|
|
|
525
491
|
**Documentation:**
|
|
526
|
-
- [Push
|
|
527
|
-
- [
|
|
528
|
-
- [Push Notifications Setup](./PUSH_NOTIFICATIONS_SETUP.md) - General push notification setup
|
|
492
|
+
- [Push Notifications Integration Guide](./docs/PUSH_NOTIFICATIONS_INTEGRATION.md) - Callback-based registration, complete AppDelegate implementation, Android and iOS token flow
|
|
493
|
+
- [Push Notifications Setup](./docs/PUSH_NOTIFICATIONS_SETUP.md) - Apple Developer Portal, APNs certificates, and TestFlight configuration
|
|
529
494
|
- [iOS Native SDK documentation](https://github.com/attentive-mobile/attentive-ios-sdk) - Native SDK reference
|
|
530
495
|
|
|
531
|
-
For
|
|
496
|
+
For Android push notification integration, see the **[App Events on Android](#app-events-on-android)** section above.
|
package/android/src/main/kotlin/com/attentivereactnativesdk/AttentiveReactNativeSdkModule.kt
CHANGED
|
@@ -88,7 +88,7 @@ class AttentiveReactNativeSdkModule(reactContext: ReactApplicationContext) :
|
|
|
88
88
|
) {
|
|
89
89
|
debugHelper.initialize(enableDebugger)
|
|
90
90
|
|
|
91
|
-
Log.
|
|
91
|
+
Log.d(
|
|
92
92
|
TAG,
|
|
93
93
|
"[AttentiveSDK] initialize() called from TypeScript is a NO-OP on Android. " +
|
|
94
94
|
"You must call AttentiveSdk.initialize(config) from your Application.onCreate() " +
|
|
@@ -187,17 +187,7 @@ class AttentiveReactNativeSdkModule(reactContext: ReactApplicationContext) :
|
|
|
187
187
|
val itemsList = buildItems(items)
|
|
188
188
|
val productViewEvent = ProductViewEvent.Builder().items(itemsList).deeplink(deeplink).build()
|
|
189
189
|
|
|
190
|
-
|
|
191
|
-
AttentiveSdk.recordEvent(productViewEvent)
|
|
192
|
-
} catch (e: Exception) {
|
|
193
|
-
Log.e(
|
|
194
|
-
TAG,
|
|
195
|
-
"[AttentiveSDK] recordProductViewEvent failed — SDK may not be initialized. " +
|
|
196
|
-
"On Android, call AttentiveSdk.initialize(config) from Application.onCreate() " +
|
|
197
|
-
"before recording events. Error: ${e.message}"
|
|
198
|
-
)
|
|
199
|
-
return
|
|
200
|
-
}
|
|
190
|
+
if (!recordEventSafely("recordProductViewEvent") { AttentiveSdk.recordEvent(productViewEvent) }) return
|
|
201
191
|
|
|
202
192
|
if (debugHelper.isDebuggingEnabled()) {
|
|
203
193
|
val debugData = mutableMapOf<String, Any>()
|
|
@@ -227,17 +217,7 @@ class AttentiveReactNativeSdkModule(reactContext: ReactApplicationContext) :
|
|
|
227
217
|
}
|
|
228
218
|
val purchaseEvent = purchaseBuilder.build()
|
|
229
219
|
|
|
230
|
-
|
|
231
|
-
AttentiveSdk.recordEvent(purchaseEvent)
|
|
232
|
-
} catch (e: Exception) {
|
|
233
|
-
Log.e(
|
|
234
|
-
TAG,
|
|
235
|
-
"[AttentiveSDK] recordPurchaseEvent failed — SDK may not be initialized. " +
|
|
236
|
-
"On Android, call AttentiveSdk.initialize(config) from Application.onCreate() " +
|
|
237
|
-
"before recording events. Error: ${e.message}"
|
|
238
|
-
)
|
|
239
|
-
return
|
|
240
|
-
}
|
|
220
|
+
if (!recordEventSafely("recordPurchaseEvent") { AttentiveSdk.recordEvent(purchaseEvent) }) return
|
|
241
221
|
|
|
242
222
|
if (debugHelper.isDebuggingEnabled()) {
|
|
243
223
|
val debugData = mutableMapOf<String, Any>()
|
|
@@ -260,17 +240,7 @@ class AttentiveReactNativeSdkModule(reactContext: ReactApplicationContext) :
|
|
|
260
240
|
val itemsList = buildItems(items)
|
|
261
241
|
val addToCartEvent = AddToCartEvent.Builder().items(itemsList).deeplink(deeplink).build()
|
|
262
242
|
|
|
263
|
-
|
|
264
|
-
AttentiveSdk.recordEvent(addToCartEvent)
|
|
265
|
-
} catch (e: Exception) {
|
|
266
|
-
Log.e(
|
|
267
|
-
TAG,
|
|
268
|
-
"[AttentiveSDK] recordAddToCartEvent failed — SDK may not be initialized. " +
|
|
269
|
-
"On Android, call AttentiveSdk.initialize(config) from Application.onCreate() " +
|
|
270
|
-
"before recording events. Error: ${e.message}"
|
|
271
|
-
)
|
|
272
|
-
return
|
|
273
|
-
}
|
|
243
|
+
if (!recordEventSafely("recordAddToCartEvent") { AttentiveSdk.recordEvent(addToCartEvent) }) return
|
|
274
244
|
|
|
275
245
|
if (debugHelper.isDebuggingEnabled()) {
|
|
276
246
|
val debugData = mutableMapOf<String, Any>()
|
|
@@ -290,17 +260,7 @@ class AttentiveReactNativeSdkModule(reactContext: ReactApplicationContext) :
|
|
|
290
260
|
val propertiesMap = convertToStringMap(properties.toHashMap())
|
|
291
261
|
val customEvent = CustomEvent.Builder().type(type).properties(propertiesMap).build()
|
|
292
262
|
|
|
293
|
-
|
|
294
|
-
AttentiveSdk.recordEvent(customEvent)
|
|
295
|
-
} catch (e: Exception) {
|
|
296
|
-
Log.e(
|
|
297
|
-
TAG,
|
|
298
|
-
"[AttentiveSDK] recordCustomEvent failed — SDK may not be initialized. " +
|
|
299
|
-
"On Android, call AttentiveSdk.initialize(config) from Application.onCreate() " +
|
|
300
|
-
"before recording events. Error: ${e.message}"
|
|
301
|
-
)
|
|
302
|
-
return
|
|
303
|
-
}
|
|
263
|
+
if (!recordEventSafely("recordCustomEvent") { AttentiveSdk.recordEvent(customEvent) }) return
|
|
304
264
|
|
|
305
265
|
if (debugHelper.isDebuggingEnabled()) {
|
|
306
266
|
val debugData = mutableMapOf<String, Any>()
|
|
@@ -844,6 +804,36 @@ class AttentiveReactNativeSdkModule(reactContext: ReactApplicationContext) :
|
|
|
844
804
|
* @param rawItems The raw item array as received from the React Native bridge.
|
|
845
805
|
* @return A list of maps, one per item, containing all present fields.
|
|
846
806
|
*/
|
|
807
|
+
/**
|
|
808
|
+
* Executes [block] (which calls [AttentiveSdk.recordEvent]) and catches any [Exception].
|
|
809
|
+
*
|
|
810
|
+
* When an exception is caught the log always includes the exception's simple class name so
|
|
811
|
+
* callers can distinguish an uninitialized-SDK error (typically [IllegalStateException]) from
|
|
812
|
+
* a programming mistake such as [NullPointerException] or [IllegalArgumentException] in event
|
|
813
|
+
* construction — both of which would have been silently misattributed to initialization
|
|
814
|
+
* failure under the previous broad catch-and-blame pattern.
|
|
815
|
+
*
|
|
816
|
+
* @param callerName Method name to include in the log for quick triage (e.g. "recordPurchaseEvent").
|
|
817
|
+
* @param block Lambda that performs the [AttentiveSdk.recordEvent] call.
|
|
818
|
+
* @return `true` if [block] completed without throwing, `false` if an exception was caught.
|
|
819
|
+
*/
|
|
820
|
+
private fun recordEventSafely(callerName: String, block: () -> Unit): Boolean {
|
|
821
|
+
return try {
|
|
822
|
+
block()
|
|
823
|
+
true
|
|
824
|
+
} catch (e: Exception) {
|
|
825
|
+
// Include the exception class so the developer can tell apart an uninitialized SDK
|
|
826
|
+
// (IllegalStateException) from a malformed-event bug (NullPointerException, etc.)
|
|
827
|
+
Log.e(
|
|
828
|
+
TAG,
|
|
829
|
+
"[AttentiveSDK] $callerName failed with ${e.javaClass.simpleName}: ${e.message}. " +
|
|
830
|
+
"If the SDK was not initialized via AttentiveSdk.initialize() in Application.onCreate(), " +
|
|
831
|
+
"that is the most likely cause. Otherwise, inspect the exception type above."
|
|
832
|
+
)
|
|
833
|
+
false
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
847
837
|
private fun extractItemsDebugData(rawItems: ReadableArray): List<Map<String, Any>> {
|
|
848
838
|
val result = mutableListOf<Map<String, Any>>()
|
|
849
839
|
for (i in 0 until rawItems.size()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@attentive-mobile/attentive-react-native-sdk",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "React Native Module for the Attentive SDK",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"*.podspec",
|
|
17
17
|
"!lib/typescript/example",
|
|
18
18
|
"!ios/build",
|
|
19
|
+
"!ios/Pods",
|
|
20
|
+
"!ios/Podfile.lock",
|
|
21
|
+
"!ios/*.xcworkspace",
|
|
22
|
+
"!**/xcuserdata",
|
|
19
23
|
"!android/build",
|
|
20
24
|
"!android/gradle",
|
|
21
25
|
"!android/gradlew",
|
|
@@ -111,7 +115,7 @@
|
|
|
111
115
|
"release-it": {
|
|
112
116
|
"git": {
|
|
113
117
|
"commitMessage": "chore: release ${version}",
|
|
114
|
-
"tagName": "
|
|
118
|
+
"tagName": "${version}"
|
|
115
119
|
},
|
|
116
120
|
"npm": {
|
|
117
121
|
"publish": true
|
|
Binary file
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
-
<plist version="1.0">
|
|
4
|
-
<dict>
|
|
5
|
-
<key>SchemeUserState</key>
|
|
6
|
-
<dict>
|
|
7
|
-
<key>AttentiveReactNativeSdk.xcscheme_^#shared#^_</key>
|
|
8
|
-
<dict>
|
|
9
|
-
<key>orderHint</key>
|
|
10
|
-
<integer>0</integer>
|
|
11
|
-
</dict>
|
|
12
|
-
</dict>
|
|
13
|
-
</dict>
|
|
14
|
-
</plist>
|