zixflow-reactnative 1.1.2 → 1.1.4
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 +2224 -322
- package/android/build.gradle +18 -1
- package/android/consumer-rules.pro +3 -2
- package/android/gradle.properties +1 -1
- package/android/src/main/java/com/zixflow/reactnative/sdk/NativeZixflowModule.kt +69 -24
- package/android/src/main/java/com/zixflow/reactnative/sdk/ZixflowReactNativePackage.kt +25 -1
- package/android/src/main/java/com/zixflow/reactnative/sdk/constant/Keys.kt +19 -3
- package/android/src/main/java/com/zixflow/reactnative/sdk/geofence/NativeGeofenceModule.kt +65 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/liveactivities/NativeLiveActivitiesModule.kt +310 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/logging/NativeZixflowLoggingModule.kt +2 -2
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt +149 -10
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginapp/ReactInboxEventListener.kt +99 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/NotificationInboxBellViewManager.kt +38 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/NotificationInboxViewManager.kt +30 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactComposeHostView.kt +96 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactNotificationInboxBellView.kt +48 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactNotificationInboxView.kt +17 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt +11 -1
- package/android/src/main/res/values/zixflow_config.xml +1 -1
- package/android/zixflow-core.gradle +17 -7
- package/docs/cocoapods-deployment-target-normalization.md +139 -0
- package/ios/cocoapods_deployment_target.rb +432 -0
- package/ios/wrappers/NativeZixflow.mm +37 -0
- package/ios/wrappers/NativeZixflow.swift +135 -1
- package/ios/wrappers/ZixflowReactNativeDeepLinkRequestStore.swift +123 -0
- package/ios/wrappers/ZixflowReactNativeDeepLinkRouter.swift +312 -0
- package/ios/wrappers/geofence/NativeGeofence.mm +46 -0
- package/ios/wrappers/geofence/NativeGeofence.swift +27 -0
- package/ios/wrappers/inapp/NativeMessagingInApp.mm +22 -0
- package/ios/wrappers/inapp/NativeMessagingInApp.swift +90 -0
- package/ios/wrappers/inbox/InboxHostContainment.swift +40 -0
- package/ios/wrappers/inbox/RCTNotificationInboxBellNative.h +12 -0
- package/ios/wrappers/inbox/RCTNotificationInboxBellNative.mm +98 -0
- package/ios/wrappers/inbox/RCTNotificationInboxViewNative.h +12 -0
- package/ios/wrappers/inbox/RCTNotificationInboxViewNative.mm +78 -0
- package/ios/wrappers/inbox/ReactInboxEventListener.swift +76 -0
- package/ios/wrappers/inbox/ReactNotificationInboxBellView.h +22 -0
- package/ios/wrappers/inbox/ReactNotificationInboxBellView.swift +109 -0
- package/ios/wrappers/inbox/ReactNotificationInboxView.h +22 -0
- package/ios/wrappers/inbox/ReactNotificationInboxView.swift +69 -0
- package/ios/wrappers/liveactivities/NativeLiveActivities.mm +83 -0
- package/ios/wrappers/liveactivities/NativeLiveActivities.swift +390 -0
- package/ios/wrappers/location/NativeLocation.swift +7 -5
- package/ios/wrappers/utils/ZixflowConfigUtils.swift +5 -9
- package/ios/wrappers/utils/ZixflowConstants.swift +4 -0
- package/ios/wrappers/zixflow_reactnative.h +6 -0
- package/lib/commonjs/components/NotificationInboxBellView.js +49 -0
- package/lib/commonjs/components/NotificationInboxBellView.js.map +1 -0
- package/lib/commonjs/components/NotificationInboxView.js +36 -0
- package/lib/commonjs/components/NotificationInboxView.js.map +1 -0
- package/lib/commonjs/components/index.js +14 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/index.js +81 -4
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/notification-inbox.js +1 -0
- package/lib/commonjs/notification-inbox.js.map +1 -1
- package/lib/commonjs/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
- package/lib/commonjs/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
- package/lib/commonjs/specs/modules/NativeZixflow.js.map +1 -1
- package/lib/commonjs/specs/modules/NativeZixflowGeofence.js +15 -0
- package/lib/commonjs/specs/modules/NativeZixflowGeofence.js.map +1 -0
- package/lib/commonjs/specs/modules/NativeZixflowLiveActivities.js +23 -0
- package/lib/commonjs/specs/modules/NativeZixflowLiveActivities.js.map +1 -0
- package/lib/commonjs/specs/modules/NativeZixflowMessagingInApp.js +3 -1
- package/lib/commonjs/specs/modules/NativeZixflowMessagingInApp.js.map +1 -1
- package/lib/commonjs/specs/modules/NativeZixflowMessagingPush.js +3 -1
- package/lib/commonjs/specs/modules/NativeZixflowMessagingPush.js.map +1 -1
- package/lib/commonjs/types/data-pipelines.js +15 -15
- package/lib/commonjs/types/data-pipelines.js.map +1 -1
- package/lib/commonjs/types/in-app.js +24 -1
- package/lib/commonjs/types/in-app.js.map +1 -1
- package/lib/commonjs/types/inbox.js +79 -0
- package/lib/commonjs/types/inbox.js.map +1 -0
- package/lib/commonjs/types/index.js +22 -0
- package/lib/commonjs/types/index.js.map +1 -1
- package/lib/commonjs/types/live-activities.js +85 -0
- package/lib/commonjs/types/live-activities.js.map +1 -0
- package/lib/commonjs/utils/param-validation.js +77 -2
- package/lib/commonjs/utils/param-validation.js.map +1 -1
- package/lib/commonjs/zixflow-cdp.js +101 -11
- package/lib/commonjs/zixflow-cdp.js.map +1 -1
- package/lib/commonjs/zixflow-geofence.js +55 -0
- package/lib/commonjs/zixflow-geofence.js.map +1 -0
- package/lib/commonjs/zixflow-inapp.js +120 -0
- package/lib/commonjs/zixflow-inapp.js.map +1 -1
- package/lib/commonjs/zixflow-liveactivities.js +85 -0
- package/lib/commonjs/zixflow-liveactivities.js.map +1 -0
- package/lib/commonjs/zixflow.js.map +1 -1
- package/lib/module/components/NotificationInboxBellView.js +44 -0
- package/lib/module/components/NotificationInboxBellView.js.map +1 -0
- package/lib/module/components/NotificationInboxView.js +31 -0
- package/lib/module/components/NotificationInboxView.js.map +1 -0
- package/lib/module/components/index.js +5 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/index.js +8 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/notification-inbox.js +1 -1
- package/lib/module/notification-inbox.js.map +1 -1
- package/lib/module/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
- package/lib/module/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
- package/lib/module/specs/modules/NativeZixflow.js.map +1 -1
- package/lib/module/specs/modules/NativeZixflowGeofence.js +13 -0
- package/lib/module/specs/modules/NativeZixflowGeofence.js.map +1 -0
- package/lib/module/specs/modules/NativeZixflowLiveActivities.js +21 -0
- package/lib/module/specs/modules/NativeZixflowLiveActivities.js.map +1 -0
- package/lib/module/specs/modules/NativeZixflowMessagingInApp.js +3 -1
- package/lib/module/specs/modules/NativeZixflowMessagingInApp.js.map +1 -1
- package/lib/module/specs/modules/NativeZixflowMessagingPush.js +3 -1
- package/lib/module/specs/modules/NativeZixflowMessagingPush.js.map +1 -1
- package/lib/module/types/data-pipelines.js +15 -15
- package/lib/module/types/data-pipelines.js.map +1 -1
- package/lib/module/types/in-app.js +24 -0
- package/lib/module/types/in-app.js.map +1 -1
- package/lib/module/types/inbox.js +75 -0
- package/lib/module/types/inbox.js.map +1 -0
- package/lib/module/types/index.js +2 -0
- package/lib/module/types/index.js.map +1 -1
- package/lib/module/types/live-activities.js +88 -0
- package/lib/module/types/live-activities.js.map +1 -0
- package/lib/module/utils/param-validation.js +77 -2
- package/lib/module/utils/param-validation.js.map +1 -1
- package/lib/module/zixflow-cdp.js +101 -11
- package/lib/module/zixflow-cdp.js.map +1 -1
- package/lib/module/zixflow-geofence.js +50 -0
- package/lib/module/zixflow-geofence.js.map +1 -0
- package/lib/module/zixflow-inapp.js +99 -1
- package/lib/module/zixflow-inapp.js.map +1 -1
- package/lib/module/zixflow-liveactivities.js +80 -0
- package/lib/module/zixflow-liveactivities.js.map +1 -0
- package/lib/module/zixflow.js +0 -6
- package/lib/module/zixflow.js.map +1 -1
- package/lib/typescript/commonjs/components/NotificationInboxBellView.d.ts +39 -0
- package/lib/typescript/commonjs/components/NotificationInboxBellView.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/NotificationInboxView.d.ts +25 -0
- package/lib/typescript/commonjs/components/NotificationInboxView.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/index.d.ts +4 -0
- package/lib/typescript/commonjs/components/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +8 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/notification-inbox.d.ts +5 -0
- package/lib/typescript/commonjs/notification-inbox.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/components/NotificationInboxBellNativeComponent.d.ts +13 -0
- package/lib/typescript/commonjs/specs/components/NotificationInboxBellNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/specs/components/NotificationInboxViewNativeComponent.d.ts +12 -0
- package/lib/typescript/commonjs/specs/components/NotificationInboxViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/specs/modules/NativeZixflow.d.ts +6 -1
- package/lib/typescript/commonjs/specs/modules/NativeZixflow.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/modules/NativeZixflowGeofence.d.ts +13 -0
- package/lib/typescript/commonjs/specs/modules/NativeZixflowGeofence.d.ts.map +1 -0
- package/lib/typescript/commonjs/specs/modules/NativeZixflowLiveActivities.d.ts +31 -0
- package/lib/typescript/commonjs/specs/modules/NativeZixflowLiveActivities.d.ts.map +1 -0
- package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingInApp.d.ts +5 -1
- package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingInApp.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingPush.d.ts +1 -1
- package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingPush.d.ts.map +1 -1
- package/lib/typescript/commonjs/types/data-pipelines.d.ts +54 -18
- package/lib/typescript/commonjs/types/data-pipelines.d.ts.map +1 -1
- package/lib/typescript/commonjs/types/in-app.d.ts +22 -0
- package/lib/typescript/commonjs/types/in-app.d.ts.map +1 -1
- package/lib/typescript/commonjs/types/inbox.d.ts +97 -0
- package/lib/typescript/commonjs/types/inbox.d.ts.map +1 -0
- package/lib/typescript/commonjs/types/index.d.ts +2 -0
- package/lib/typescript/commonjs/types/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/types/live-activities.d.ts +161 -0
- package/lib/typescript/commonjs/types/live-activities.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/param-validation.d.ts +7 -0
- package/lib/typescript/commonjs/utils/param-validation.d.ts.map +1 -1
- package/lib/typescript/commonjs/zixflow-cdp.d.ts +38 -0
- package/lib/typescript/commonjs/zixflow-cdp.d.ts.map +1 -1
- package/lib/typescript/commonjs/zixflow-geofence.d.ts +36 -0
- package/lib/typescript/commonjs/zixflow-geofence.d.ts.map +1 -0
- package/lib/typescript/commonjs/zixflow-inapp.d.ts +36 -2
- package/lib/typescript/commonjs/zixflow-inapp.d.ts.map +1 -1
- package/lib/typescript/commonjs/zixflow-liveactivities.d.ts +66 -0
- package/lib/typescript/commonjs/zixflow-liveactivities.d.ts.map +1 -0
- package/lib/typescript/commonjs/zixflow.d.ts +0 -5
- package/lib/typescript/commonjs/zixflow.d.ts.map +1 -1
- package/lib/typescript/module/components/NotificationInboxBellView.d.ts +39 -0
- package/lib/typescript/module/components/NotificationInboxBellView.d.ts.map +1 -0
- package/lib/typescript/module/components/NotificationInboxView.d.ts +25 -0
- package/lib/typescript/module/components/NotificationInboxView.d.ts.map +1 -0
- package/lib/typescript/module/components/index.d.ts +4 -0
- package/lib/typescript/module/components/index.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +8 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/notification-inbox.d.ts +5 -0
- package/lib/typescript/module/notification-inbox.d.ts.map +1 -1
- package/lib/typescript/module/specs/components/NotificationInboxBellNativeComponent.d.ts +13 -0
- package/lib/typescript/module/specs/components/NotificationInboxBellNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/specs/components/NotificationInboxViewNativeComponent.d.ts +12 -0
- package/lib/typescript/module/specs/components/NotificationInboxViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/specs/modules/NativeZixflow.d.ts +6 -1
- package/lib/typescript/module/specs/modules/NativeZixflow.d.ts.map +1 -1
- package/lib/typescript/module/specs/modules/NativeZixflowGeofence.d.ts +13 -0
- package/lib/typescript/module/specs/modules/NativeZixflowGeofence.d.ts.map +1 -0
- package/lib/typescript/module/specs/modules/NativeZixflowLiveActivities.d.ts +31 -0
- package/lib/typescript/module/specs/modules/NativeZixflowLiveActivities.d.ts.map +1 -0
- package/lib/typescript/module/specs/modules/NativeZixflowMessagingInApp.d.ts +5 -1
- package/lib/typescript/module/specs/modules/NativeZixflowMessagingInApp.d.ts.map +1 -1
- package/lib/typescript/module/specs/modules/NativeZixflowMessagingPush.d.ts +1 -1
- package/lib/typescript/module/specs/modules/NativeZixflowMessagingPush.d.ts.map +1 -1
- package/lib/typescript/module/types/data-pipelines.d.ts +54 -18
- package/lib/typescript/module/types/data-pipelines.d.ts.map +1 -1
- package/lib/typescript/module/types/in-app.d.ts +22 -0
- package/lib/typescript/module/types/in-app.d.ts.map +1 -1
- package/lib/typescript/module/types/inbox.d.ts +97 -0
- package/lib/typescript/module/types/inbox.d.ts.map +1 -0
- package/lib/typescript/module/types/index.d.ts +2 -0
- package/lib/typescript/module/types/index.d.ts.map +1 -1
- package/lib/typescript/module/types/live-activities.d.ts +161 -0
- package/lib/typescript/module/types/live-activities.d.ts.map +1 -0
- package/lib/typescript/module/utils/param-validation.d.ts +7 -0
- package/lib/typescript/module/utils/param-validation.d.ts.map +1 -1
- package/lib/typescript/module/zixflow-cdp.d.ts +38 -0
- package/lib/typescript/module/zixflow-cdp.d.ts.map +1 -1
- package/lib/typescript/module/zixflow-geofence.d.ts +36 -0
- package/lib/typescript/module/zixflow-geofence.d.ts.map +1 -0
- package/lib/typescript/module/zixflow-inapp.d.ts +36 -2
- package/lib/typescript/module/zixflow-inapp.d.ts.map +1 -1
- package/lib/typescript/module/zixflow-liveactivities.d.ts +66 -0
- package/lib/typescript/module/zixflow-liveactivities.d.ts.map +1 -0
- package/lib/typescript/module/zixflow.d.ts +0 -5
- package/lib/typescript/module/zixflow.d.ts.map +1 -1
- package/package.json +14 -5
- package/src/components/NotificationInboxBellView.tsx +55 -0
- package/src/components/NotificationInboxView.tsx +30 -0
- package/src/components/index.ts +6 -0
- package/src/index.ts +8 -3
- package/src/notification-inbox.ts +1 -1
- package/src/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
- package/src/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
- package/src/specs/modules/NativeZixflow.ts +9 -1
- package/src/specs/modules/NativeZixflowGeofence.ts +14 -0
- package/src/specs/modules/NativeZixflowLiveActivities.ts +39 -0
- package/src/specs/modules/NativeZixflowMessagingInApp.ts +12 -1
- package/src/specs/modules/NativeZixflowMessagingPush.ts +3 -1
- package/src/types/data-pipelines.ts +55 -19
- package/src/types/in-app.ts +23 -0
- package/src/types/inbox.ts +110 -0
- package/src/types/index.ts +2 -0
- package/src/types/live-activities.ts +170 -0
- package/src/utils/param-validation.ts +104 -1
- package/src/zixflow-cdp.ts +119 -13
- package/src/zixflow-geofence.ts +56 -0
- package/src/zixflow-inapp.ts +125 -2
- package/src/zixflow-liveactivities.ts +86 -0
- package/src/zixflow.ts +0 -6
- package/zixflow-reactnative-richpush.podspec +6 -3
- package/zixflow-reactnative.podspec +28 -2
package/README.md
CHANGED
|
@@ -1,446 +1,2238 @@
|
|
|
1
|
-
|
|
2
|
-
<a href="https://zixflow.com">
|
|
3
|
-
<img src="https://zixflow.com/logo.svg" height="60">
|
|
4
|
-
</a>
|
|
5
|
-
</p>
|
|
1
|
+
# Zixflow React Native SDK - Integration Guide
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
[](https://www.npmjs.com/package/zixflow-reactnative)
|
|
9
|
-

|
|
10
|
-

|
|
11
|
-

|
|
12
|
-
[](CODE_OF_CONDUCT.md)
|
|
3
|
+
Welcome to the **Zixflow React Native SDK** integration guide. This comprehensive document will help you install, configure, and use the Zixflow SDK in your React Native application to track user behavior, send push notifications, display in-app messages, and more.
|
|
13
4
|
|
|
14
|
-
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Introduction](#introduction)
|
|
10
|
+
2. [Requirements](#requirements)
|
|
11
|
+
3. [Installation](#installation)
|
|
12
|
+
- [npm Installation](#npm-installation)
|
|
13
|
+
- [Yarn Installation](#yarn-installation)
|
|
14
|
+
4. [iOS Setup](#ios-setup)
|
|
15
|
+
- [CocoaPods Integration](#cocoapods-integration)
|
|
16
|
+
- [Swift Package Manager Integration](#swift-package-manager-integration)
|
|
17
|
+
- [Push Notification Setup (APNs)](#push-notification-setup-apns)
|
|
18
|
+
- [Notification Service Extension](#notification-service-extension)
|
|
19
|
+
5. [Android Setup](#android-setup)
|
|
20
|
+
- [Gradle Configuration](#gradle-configuration)
|
|
21
|
+
- [Firebase Cloud Messaging Setup](#firebase-cloud-messaging-setup)
|
|
22
|
+
6. [Quick Start](#quick-start)
|
|
23
|
+
- [Basic Configuration](#basic-configuration)
|
|
24
|
+
- [SDK Initialization](#sdk-initialization)
|
|
25
|
+
7. [Core Features](#core-features)
|
|
26
|
+
- [User Identification](#user-identification)
|
|
27
|
+
- [Event Tracking](#event-tracking)
|
|
28
|
+
- [Screen Tracking](#screen-tracking)
|
|
29
|
+
- [User Profile Attributes](#user-profile-attributes)
|
|
30
|
+
- [Device Attributes](#device-attributes)
|
|
31
|
+
- [Clearing User Identity](#clearing-user-identity)
|
|
32
|
+
8. [Push Notifications](#push-notifications)
|
|
33
|
+
- [Requesting Permission](#requesting-permission)
|
|
34
|
+
- [Handling Push Notifications](#handling-push-notifications)
|
|
35
|
+
- [Device Token Registration](#device-token-registration)
|
|
36
|
+
9. [Push Notification Tracking](#push-notification-tracking)
|
|
37
|
+
10. [Location Tracking](#location-tracking)
|
|
38
|
+
11. [Geofence Tracking](#geofence-tracking)
|
|
39
|
+
12. [Live Activities](#-live-activities)
|
|
40
|
+
13. [Notification Inbox](#notification-inbox)
|
|
41
|
+
14. [Advanced Configuration](#advanced-configuration)
|
|
42
|
+
- [Log Levels](#log-levels)
|
|
43
|
+
- [Auto-Tracking Options](#auto-tracking-options)
|
|
44
|
+
- [Custom API Hosts](#custom-api-hosts)
|
|
45
|
+
15. [Example Applications](#example-applications)
|
|
46
|
+
16. [API Reference](#api-reference)
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Introduction
|
|
51
|
+
|
|
52
|
+
The **Zixflow React Native SDK** enables you to:
|
|
53
|
+
|
|
54
|
+
- **Identify users** and track their behavior across your app
|
|
55
|
+
- **Send targeted push notifications** via Apple Push Notification service (APNs) or Firebase Cloud Messaging (FCM)
|
|
56
|
+
- **Display in-app messages** to engage users at the right time
|
|
57
|
+
- **Track user location** to enable location-based messaging
|
|
58
|
+
- **Manage user profiles** with custom attributes
|
|
59
|
+
- **Track events and screen views** automatically or manually
|
|
60
|
+
|
|
61
|
+
The SDK is built on top of the native Zixflow iOS and Android SDKs, providing a unified TypeScript API for React Native applications.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Requirements
|
|
66
|
+
|
|
67
|
+
### React Native
|
|
68
|
+
|
|
69
|
+
- **React Native**: 0.60.0 or higher
|
|
70
|
+
- **React**: 16.8.0 or higher (for hooks support)
|
|
71
|
+
|
|
72
|
+
### iOS
|
|
73
|
+
|
|
74
|
+
- **iOS**: 13.0 or higher
|
|
75
|
+
- **Xcode**: 14.0 or higher
|
|
76
|
+
- **Swift**: 5.3 or higher
|
|
77
|
+
- **CocoaPods**: 1.10.0 or higher (if using CocoaPods)
|
|
78
|
+
|
|
79
|
+
### Android
|
|
80
|
+
|
|
81
|
+
- **Android**: API level 24 (Android 7.0) or higher
|
|
82
|
+
- **Kotlin**: 1.6.0 or higher
|
|
83
|
+
- **Gradle**: 7.0 or higher
|
|
84
|
+
|
|
85
|
+
### TypeScript (Recommended)
|
|
86
|
+
|
|
87
|
+
- **TypeScript**: 4.0 or higher
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Installation
|
|
92
|
+
|
|
93
|
+
The Zixflow React Native SDK is available on **npm**. You can browse the package here:
|
|
94
|
+
|
|
95
|
+
**[npm - zixflow-reactnative](https://www.npmjs.com/package/zixflow-reactnative)**
|
|
96
|
+
|
|
97
|
+
### npm Installation
|
|
98
|
+
|
|
99
|
+
Install the SDK using npm:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npm install zixflow-reactnative
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Yarn Installation
|
|
106
|
+
|
|
107
|
+
Or install using Yarn:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
yarn add zixflow-reactnative
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
After installation, proceed with platform-specific setup for iOS and Android.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## iOS Setup
|
|
118
|
+
|
|
119
|
+
### CocoaPods Integration
|
|
120
|
+
|
|
121
|
+
The Zixflow React Native SDK automatically integrates with the native iOS SDK via CocoaPods or Swift Package Manager. If you're using **React Native 0.60 or higher**, dependencies are auto-linked.
|
|
122
|
+
|
|
123
|
+
1. **Navigate to the iOS directory**:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
cd ios
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
2. **Install CocoaPods dependencies**:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pod install
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
3. **Open the workspace in Xcode**:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
open YourApp.xcworkspace
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The SDK will automatically include the necessary Zixflow iOS modules:
|
|
142
|
+
- `ZixflowCommon`
|
|
143
|
+
- `ZixflowDataPipelines`
|
|
144
|
+
- `ZixflowMessagingPush`
|
|
145
|
+
- `ZixflowMessagingPushAPN` (for APNs)
|
|
146
|
+
- `ZixflowMessagingInApp`
|
|
147
|
+
|
|
148
|
+
### Swift Package Manager Integration
|
|
149
|
+
|
|
150
|
+
If you prefer **Swift Package Manager (SPM)**, you can configure your project to use the Zixflow iOS SDK directly:
|
|
151
|
+
|
|
152
|
+
1. **Open your project in Xcode**
|
|
153
|
+
2. **Go to File > Add Package Dependencies**
|
|
154
|
+
3. **Enter the repository URL**:
|
|
155
|
+
```
|
|
156
|
+
https://github.com/zixflow/zixflow-ios.git
|
|
157
|
+
```
|
|
158
|
+
4. **Select the version**: Use `1.0.0` or higher
|
|
159
|
+
5. **Add the following modules** to your app target:
|
|
160
|
+
- `Zixflow` (umbrella module)
|
|
161
|
+
- `ZixflowDataPipelines`
|
|
162
|
+
- `ZixflowMessagingPushAPN`
|
|
163
|
+
- `ZixflowMessagingInApp`
|
|
164
|
+
|
|
165
|
+
> **Note**: The example app uses SPM for the native iOS SDK integration. See `example/ios/Podfile` for reference.
|
|
166
|
+
|
|
167
|
+
### Push Notification Setup (APNs)
|
|
168
|
+
|
|
169
|
+
To enable **Apple Push Notifications (APNs)**:
|
|
170
|
+
|
|
171
|
+
1. **Enable Push Notifications capability** in Xcode:
|
|
172
|
+
- Select your app target
|
|
173
|
+
- Go to **Signing & Capabilities**
|
|
174
|
+
- Click **+ Capability** and add **Push Notifications**
|
|
175
|
+
|
|
176
|
+
2. **Enable Background Modes**:
|
|
177
|
+
- Add **Background Modes** capability
|
|
178
|
+
- Enable **Remote notifications**
|
|
179
|
+
|
|
180
|
+
3. **Configure APNs certificates** in your Apple Developer account and upload them to Zixflow dashboard.
|
|
181
|
+
|
|
182
|
+
### Notification Service Extension
|
|
183
|
+
|
|
184
|
+
To support **rich push notifications** (images, videos, action buttons), you need to add a **Notification Service Extension**:
|
|
185
|
+
|
|
186
|
+
1. **Create a Notification Service Extension** in Xcode:
|
|
187
|
+
- File > New > Target > Notification Service Extension
|
|
188
|
+
- Name it `NotificationServiceExtension`
|
|
189
|
+
|
|
190
|
+
2. **Add Zixflow modules to the extension** in your `Podfile`:
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
target 'NotificationServiceExtension' do
|
|
194
|
+
pod "ZixflowMessagingPushAPN", "~> 1.0"
|
|
195
|
+
pod "ZixflowMessagingPush", "~> 1.0"
|
|
196
|
+
pod "ZixflowDataPipelines", "~> 1.0"
|
|
197
|
+
pod "ZixflowCommon", "~> 1.0"
|
|
198
|
+
pod "AnalyticsSwiftZixflow", "1.7.3+zixflow.1"
|
|
199
|
+
end
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
3. **Update `NotificationService.swift`**:
|
|
203
|
+
|
|
204
|
+
```swift
|
|
205
|
+
import UserNotifications
|
|
206
|
+
import ZixflowMessagingPushAPN
|
|
207
|
+
|
|
208
|
+
class NotificationService: UNNotificationServiceExtension {
|
|
209
|
+
var contentHandler: ((UNNotificationContent) -> Void)?
|
|
210
|
+
var bestAttemptContent: UNMutableNotificationContent?
|
|
211
|
+
|
|
212
|
+
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
|
213
|
+
self.contentHandler = contentHandler
|
|
214
|
+
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
|
|
215
|
+
|
|
216
|
+
// Use Zixflow to handle rich push
|
|
217
|
+
MessagingPushAPN.didReceive(request, withContentHandler: contentHandler)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
override func serviceExtensionTimeWillExpire() {
|
|
221
|
+
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
|
|
222
|
+
contentHandler(bestAttemptContent)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
4. **Run `pod install`** again to install extension dependencies.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Android Setup
|
|
233
|
+
|
|
234
|
+
### Gradle Configuration
|
|
235
|
+
|
|
236
|
+
The React Native auto-linking will handle most of the Android setup. However, you need to ensure the following:
|
|
237
|
+
|
|
238
|
+
1. **Set minimum SDK version** in `android/build.gradle`:
|
|
239
|
+
|
|
240
|
+
```gradle
|
|
241
|
+
buildscript {
|
|
242
|
+
ext {
|
|
243
|
+
minSdkVersion = 24
|
|
244
|
+
compileSdkVersion = 34
|
|
245
|
+
targetSdkVersion = 34
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
2. **Add Maven repositories** in `android/build.gradle`:
|
|
251
|
+
|
|
252
|
+
```gradle
|
|
253
|
+
allprojects {
|
|
254
|
+
repositories {
|
|
255
|
+
google()
|
|
256
|
+
mavenCentral()
|
|
257
|
+
maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The Zixflow Android SDK will be automatically included via the React Native module.
|
|
263
|
+
|
|
264
|
+
### Firebase Cloud Messaging Setup
|
|
265
|
+
|
|
266
|
+
To enable **Firebase Cloud Messaging (FCM)** for push notifications on Android:
|
|
267
|
+
|
|
268
|
+
1. **Add Firebase to your project**:
|
|
269
|
+
- Go to [Firebase Console](https://console.firebase.google.com/)
|
|
270
|
+
- Add your Android app
|
|
271
|
+
- Download `google-services.json` and place it in `android/app/`
|
|
272
|
+
|
|
273
|
+
2. **Add Google Services plugin** in `android/build.gradle`:
|
|
274
|
+
|
|
275
|
+
```gradle
|
|
276
|
+
buildscript {
|
|
277
|
+
dependencies {
|
|
278
|
+
classpath 'com.google.gms:google-services:4.4.2'
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
3. **Apply the plugin** in `android/app/build.gradle`:
|
|
284
|
+
|
|
285
|
+
```gradle
|
|
286
|
+
apply plugin: 'com.google.gms.google-services'
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
4. **Configure FCM in the SDK** (shown in the initialization section below).
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Quick Start
|
|
294
|
+
|
|
295
|
+
### Basic Configuration
|
|
296
|
+
|
|
297
|
+
The SDK is configured using a `ZixflowConfig` object with several options:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
import { Zixflow, ZixflowConfig, ZixflowLogLevel } from 'zixflow-reactnative';
|
|
301
|
+
|
|
302
|
+
const config: ZixflowConfig = {
|
|
303
|
+
apiKey: 'YOUR_API_KEY',
|
|
304
|
+
logLevel: ZixflowLogLevel.debug,
|
|
305
|
+
inApp: {
|
|
306
|
+
},
|
|
307
|
+
autoTrackDeviceAttributes: true,
|
|
308
|
+
trackApplicationLifecycleEvents: true,
|
|
309
|
+
};
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Configuration Options**:
|
|
313
|
+
|
|
314
|
+
| Option | Type | Required | Description |
|
|
315
|
+
|--------|------|----------|-------------|
|
|
316
|
+
| `apiKey` | string | Yes | Your Zixflow API key |
|
|
317
|
+
| `logLevel` | `ZixflowLogLevel` | No | Logging level (debug, info, error, none) |
|
|
318
|
+
| `inApp` | object | No | Enable in-app messaging (use empty object: {}) |
|
|
319
|
+
| `autoTrackDeviceAttributes` | boolean | No | Automatically track device info |
|
|
320
|
+
| `trackApplicationLifecycleEvents` | boolean | No | Automatically track app lifecycle |
|
|
321
|
+
|
|
322
|
+
### SDK Initialization
|
|
323
|
+
|
|
324
|
+
Initialize the SDK in your `App.tsx` or main component using `useEffect`:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import React, { useEffect } from 'react';
|
|
328
|
+
import { Zixflow, ZixflowConfig, ZixflowLogLevel } from 'zixflow-reactnative';
|
|
329
|
+
|
|
330
|
+
export default function App() {
|
|
331
|
+
useEffect(() => {
|
|
332
|
+
// Configure the SDK
|
|
333
|
+
const config: ZixflowConfig = {
|
|
334
|
+
apiKey: 'YOUR_API_KEY',
|
|
335
|
+
logLevel: ZixflowLogLevel.debug,
|
|
336
|
+
inApp: {
|
|
337
|
+
},
|
|
338
|
+
autoTrackDeviceAttributes: true,
|
|
339
|
+
trackApplicationLifecycleEvents: true,
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// Initialize the SDK
|
|
343
|
+
Zixflow.initialize(config);
|
|
344
|
+
|
|
345
|
+
console.log('Zixflow SDK initialized');
|
|
346
|
+
}, []);
|
|
347
|
+
|
|
348
|
+
return (
|
|
349
|
+
// Your app UI
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
> **Important**: Initialize the SDK as early as possible in your app's lifecycle, ideally in the root component's `useEffect`.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Core Features
|
|
359
|
+
|
|
360
|
+
### User Identification
|
|
361
|
+
|
|
362
|
+
Identify users to track their behavior across sessions and devices:
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
366
|
+
|
|
367
|
+
// Identify user with ID only
|
|
368
|
+
Zixflow.identify({
|
|
369
|
+
userId: 'user-123'
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Identify user with traits
|
|
373
|
+
Zixflow.identify({
|
|
374
|
+
userId: 'user-123',
|
|
375
|
+
traits: {
|
|
376
|
+
email: 'user@example.com',
|
|
377
|
+
firstName: 'John',
|
|
378
|
+
lastName: 'Doe',
|
|
379
|
+
plan: 'premium'
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**When to identify users**:
|
|
385
|
+
- After successful login
|
|
386
|
+
- When user information becomes available
|
|
387
|
+
- After account creation
|
|
388
|
+
|
|
389
|
+
### Event Tracking
|
|
390
|
+
|
|
391
|
+
Track custom events to understand user behavior:
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
395
|
+
|
|
396
|
+
// Track event without properties
|
|
397
|
+
Zixflow.track('button_clicked');
|
|
398
|
+
|
|
399
|
+
// Track event with properties
|
|
400
|
+
Zixflow.track('product_viewed', {
|
|
401
|
+
product_id: '12345',
|
|
402
|
+
product_name: 'Premium Plan',
|
|
403
|
+
category: 'subscription',
|
|
404
|
+
price: 29.99
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// Track with Map (alternative syntax)
|
|
408
|
+
const properties = new Map<string, any>();
|
|
409
|
+
properties.set('item_count', 3);
|
|
410
|
+
properties.set('total_amount', 89.97);
|
|
411
|
+
Zixflow.track('checkout_completed', properties);
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**Common events to track**:
|
|
415
|
+
- User actions (button clicks, form submissions)
|
|
416
|
+
- E-commerce events (product views, purchases)
|
|
417
|
+
- Content interactions (article reads, video plays)
|
|
418
|
+
- Feature usage
|
|
419
|
+
|
|
420
|
+
### Screen Tracking
|
|
421
|
+
|
|
422
|
+
Track screen views to understand user navigation:
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
426
|
+
|
|
427
|
+
// Track screen view
|
|
428
|
+
Zixflow.screen('HomeScreen');
|
|
429
|
+
|
|
430
|
+
// Track screen with properties
|
|
431
|
+
Zixflow.screen('ProductDetailScreen', {
|
|
432
|
+
product_id: '12345',
|
|
433
|
+
category: 'electronics'
|
|
434
|
+
});
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
**Automatic screen tracking** with React Navigation:
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
import { NavigationContainer, NavigationContainerRef } from '@react-navigation/native';
|
|
441
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
442
|
+
import React, { useRef } from 'react';
|
|
443
|
+
|
|
444
|
+
export default function App() {
|
|
445
|
+
const navigationRef = useRef<NavigationContainerRef>(null);
|
|
446
|
+
const routeNameRef = useRef<string>();
|
|
447
|
+
|
|
448
|
+
return (
|
|
449
|
+
<NavigationContainer
|
|
450
|
+
ref={navigationRef}
|
|
451
|
+
onReady={() => {
|
|
452
|
+
routeNameRef.current = navigationRef.current?.getCurrentRoute()?.name;
|
|
453
|
+
}}
|
|
454
|
+
onStateChange={async () => {
|
|
455
|
+
const previousRouteName = routeNameRef.current;
|
|
456
|
+
const currentRouteName = navigationRef.current?.getCurrentRoute()?.name;
|
|
457
|
+
|
|
458
|
+
if (previousRouteName !== currentRouteName && currentRouteName) {
|
|
459
|
+
// Track screen change
|
|
460
|
+
Zixflow.screen(currentRouteName);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Save current route name
|
|
464
|
+
routeNameRef.current = currentRouteName;
|
|
465
|
+
}}
|
|
466
|
+
>
|
|
467
|
+
{/* Your navigation setup */}
|
|
468
|
+
</NavigationContainer>
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### User Profile Attributes
|
|
474
|
+
|
|
475
|
+
Set custom attributes on user profiles:
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
479
|
+
|
|
480
|
+
// Set profile attributes
|
|
481
|
+
Zixflow.setProfileAttributes({
|
|
482
|
+
plan: 'premium',
|
|
483
|
+
subscription_status: 'active',
|
|
484
|
+
last_login: new Date().toISOString(),
|
|
485
|
+
preferences: {
|
|
486
|
+
newsletter: true,
|
|
487
|
+
notifications: true
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Device Attributes
|
|
493
|
+
|
|
494
|
+
Set custom attributes on the current device:
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
498
|
+
|
|
499
|
+
// Set device attributes
|
|
500
|
+
Zixflow.setDeviceAttributes({
|
|
501
|
+
app_version: '2.1.0',
|
|
502
|
+
build_number: '421',
|
|
503
|
+
theme: 'dark',
|
|
504
|
+
language_preference: 'en-US'
|
|
505
|
+
});
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Clearing User Identity
|
|
509
|
+
|
|
510
|
+
Clear user identity when users log out:
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
514
|
+
|
|
515
|
+
// Clear identify on logout
|
|
516
|
+
const handleLogout = async () => {
|
|
517
|
+
// Clear Zixflow identity
|
|
518
|
+
Zixflow.clearIdentify();
|
|
519
|
+
|
|
520
|
+
// Perform other logout actions
|
|
521
|
+
// ...
|
|
522
|
+
};
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## Push Notifications
|
|
528
|
+
|
|
529
|
+
Complete guide to implementing push notifications in React Native with Zixflow using Apple Push Notifications (APNs) or Firebase Cloud Messaging (FCM).
|
|
530
|
+
|
|
531
|
+
> ** Track your campaigns:** The SDK automatically tracks delivery, opens, and clicks. For advanced tracking customization and implementation details, see [Push Notification Tracking](#push-notification-tracking).
|
|
532
|
+
|
|
533
|
+
### Prerequisites
|
|
534
|
+
|
|
535
|
+
Before implementing push notifications:
|
|
536
|
+
|
|
537
|
+
1. **Configure push credentials in Zixflow dashboard** - Add APNs certificates or FCM Server Key
|
|
538
|
+
2. **Complete iOS/Android native setup** - Ensure native dependencies are installed
|
|
539
|
+
3. **Device tokens must be associated with users** - Call `identify()` before sending notifications
|
|
540
|
+
|
|
541
|
+
**Important Limitation**: The Zixflow React Native SDK currently supports deep links and images in push notifications. Custom action buttons require additional native code.
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
### Platform-Specific Requirements
|
|
546
|
+
|
|
547
|
+
#### iOS Requirements
|
|
548
|
+
|
|
549
|
+
- **APNs or FCM**: Choose either Apple Push Notification service or Firebase Cloud Messaging
|
|
550
|
+
- **Xcode Capabilities**: Push Notifications and Background Modes
|
|
551
|
+
- **Notification Service Extension**: Required for rich push (images, videos)
|
|
552
|
+
- **Physical Device**: iOS Simulator cannot receive push notifications
|
|
553
|
+
|
|
554
|
+
#### Android Requirements
|
|
555
|
+
|
|
556
|
+
- **Firebase Project**: Required for FCM integration
|
|
557
|
+
- **google-services.json**: Must be in `android/app/` directory
|
|
558
|
+
- **Minimum API Level 24**: Android 7.0 or higher
|
|
559
|
+
- **POST_NOTIFICATIONS Permission**: Required for Android 13+ (API 33+)
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
### iOS Setup
|
|
564
|
+
|
|
565
|
+
You can use either **APNs (Apple Push Notification service)** or **FCM (Firebase Cloud Messaging)** on iOS.
|
|
566
|
+
|
|
567
|
+
#### Step 1: Enable Push Capabilities in Xcode
|
|
568
|
+
|
|
569
|
+
1. Open your project's `.xcworkspace` file in Xcode
|
|
570
|
+
2. Select your app target
|
|
571
|
+
3. Go to **Signing & Capabilities** tab
|
|
572
|
+
4. Click **+ Capability** → Add **Push Notifications**
|
|
573
|
+
5. Click **+ Capability** → Add **Background Modes**
|
|
574
|
+
6. Enable **Remote notifications** under Background Modes
|
|
575
|
+
|
|
576
|
+
#### Step 2: Choose APNs or FCM
|
|
577
|
+
|
|
578
|
+
**Option A: APNs (Recommended for iOS-only apps)**
|
|
579
|
+
|
|
580
|
+
Update your `ios/Podfile`:
|
|
581
|
+
|
|
582
|
+
```ruby
|
|
583
|
+
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
|
584
|
+
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
|
585
|
+
|
|
586
|
+
platform :ios, '13.0'
|
|
587
|
+
|
|
588
|
+
target 'YourApp' do
|
|
589
|
+
config = use_native_modules!
|
|
590
|
+
|
|
591
|
+
use_react_native!(:path => config[:reactNativePath])
|
|
592
|
+
|
|
593
|
+
# Add Zixflow with APNs support
|
|
594
|
+
pod 'zixflow-reactnative/apn', :path => '../node_modules/zixflow-reactnative'
|
|
595
|
+
|
|
596
|
+
# Your other dependencies...
|
|
597
|
+
end
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
**Option B: FCM (For cross-platform Firebase apps)**
|
|
601
|
+
|
|
602
|
+
Update your `ios/Podfile` with static linkage:
|
|
603
|
+
|
|
604
|
+
```ruby
|
|
605
|
+
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
|
606
|
+
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
|
607
|
+
|
|
608
|
+
platform :ios, '13.0'
|
|
609
|
+
|
|
610
|
+
# Required for FCM
|
|
611
|
+
use_frameworks! :linkage => :static
|
|
612
|
+
|
|
613
|
+
target 'YourApp' do
|
|
614
|
+
config = use_native_modules!
|
|
615
|
+
|
|
616
|
+
use_react_native!(:path => config[:reactNativePath])
|
|
617
|
+
|
|
618
|
+
# Add Zixflow with FCM support
|
|
619
|
+
pod 'zixflow-reactnative/fcm', :path => '../node_modules/zixflow-reactnative'
|
|
620
|
+
|
|
621
|
+
# Your other dependencies...
|
|
622
|
+
end
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
**Install dependencies:**
|
|
626
|
+
|
|
627
|
+
```bash
|
|
628
|
+
cd ios
|
|
629
|
+
pod install
|
|
630
|
+
cd ..
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
#### Step 3: Configure AppDelegate (Swift - Recommended)
|
|
634
|
+
|
|
635
|
+
For modern Swift-based React Native apps with APNs:
|
|
636
|
+
|
|
637
|
+
**AppDelegate.swift:**
|
|
638
|
+
|
|
639
|
+
```swift
|
|
640
|
+
import UIKit
|
|
641
|
+
import ZixflowMessagingPushAPN
|
|
642
|
+
|
|
643
|
+
@main
|
|
644
|
+
class AppDelegateWithZixflowIntegration: ZixflowAppDelegateWrapper<AppDelegate> {}
|
|
645
|
+
|
|
646
|
+
class AppDelegate: NSObject, UIApplicationDelegate {
|
|
647
|
+
|
|
648
|
+
func application(
|
|
649
|
+
_ application: UIApplication,
|
|
650
|
+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
|
651
|
+
) -> Bool {
|
|
652
|
+
// Set notification delegate
|
|
653
|
+
UNUserNotificationCenter.current().delegate = self
|
|
654
|
+
|
|
655
|
+
// Initialize Zixflow push messaging (APNs)
|
|
656
|
+
MessagingPushAPN.initialize(
|
|
657
|
+
withConfig: MessagingPushConfigBuilder()
|
|
658
|
+
.build()
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
return true
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
extension AppDelegate: UNUserNotificationCenterDelegate {
|
|
666
|
+
// Show notifications when app is in foreground
|
|
667
|
+
func userNotificationCenter(
|
|
668
|
+
_ center: UNUserNotificationCenter,
|
|
669
|
+
willPresent notification: UNNotification,
|
|
670
|
+
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
|
|
671
|
+
) {
|
|
672
|
+
completionHandler([.banner, .list, .badge, .sound])
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
**For FCM**, import `ZixflowMessagingPushFCM` instead:
|
|
678
|
+
|
|
679
|
+
```swift
|
|
680
|
+
import ZixflowMessagingPushFCM
|
|
681
|
+
|
|
682
|
+
// Initialize with FCM
|
|
683
|
+
MessagingPushFCM.initialize(
|
|
684
|
+
withConfig: MessagingPushConfigBuilder()
|
|
685
|
+
.build()
|
|
686
|
+
)
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
#### Step 4: Configure AppDelegate (Objective-C)
|
|
690
|
+
|
|
691
|
+
If your React Native app uses Objective-C, create a Swift bridge:
|
|
692
|
+
|
|
693
|
+
**Create `MyAppPushNotificationsHandler.swift`:**
|
|
694
|
+
|
|
695
|
+
```swift
|
|
696
|
+
import Foundation
|
|
697
|
+
import ZixflowMessagingPushAPN
|
|
698
|
+
|
|
699
|
+
@objc
|
|
700
|
+
public class MyAppPushNotificationsHandler: NSObject {
|
|
701
|
+
|
|
702
|
+
@objc(setupZixflowClickHandling)
|
|
703
|
+
public func setupZixflowClickHandling() {
|
|
704
|
+
MessagingPushAPN.initialize(
|
|
705
|
+
withConfig: MessagingPushConfigBuilder().build()
|
|
706
|
+
)
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
@objc(application:deviceToken:)
|
|
710
|
+
public func application(
|
|
711
|
+
_ application: UIApplication,
|
|
712
|
+
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
|
|
713
|
+
) {
|
|
714
|
+
MessagingPush.shared.application(
|
|
715
|
+
application,
|
|
716
|
+
didRegisterForRemoteNotificationsWithDeviceToken: deviceToken
|
|
717
|
+
)
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
@objc(application:error:)
|
|
721
|
+
public func application(
|
|
722
|
+
_ application: UIApplication,
|
|
723
|
+
didFailToRegisterForRemoteNotificationsWithError error: Error
|
|
724
|
+
) {
|
|
725
|
+
MessagingPush.shared.application(
|
|
726
|
+
application,
|
|
727
|
+
didFailToRegisterForRemoteNotificationsWithError: error
|
|
728
|
+
)
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
**Update `AppDelegate.mm`:**
|
|
734
|
+
|
|
735
|
+
```objc
|
|
736
|
+
#import "YourApp-Swift.h"
|
|
737
|
+
|
|
738
|
+
@implementation AppDelegate
|
|
739
|
+
|
|
740
|
+
MyAppPushNotificationsHandler* pnHandlerObj;
|
|
741
|
+
|
|
742
|
+
- (BOOL)application:(UIApplication *)application
|
|
743
|
+
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
|
744
|
+
// ... existing initialization code ...
|
|
745
|
+
|
|
746
|
+
// Register for remote notifications
|
|
747
|
+
[application registerForRemoteNotifications];
|
|
748
|
+
|
|
749
|
+
// Initialize push handling
|
|
750
|
+
pnHandlerObj = [[MyAppPushNotificationsHandler alloc] init];
|
|
751
|
+
[pnHandlerObj setupZixflowClickHandling];
|
|
752
|
+
|
|
753
|
+
return YES;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
- (void)application:(UIApplication *)application
|
|
757
|
+
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
|
|
758
|
+
[pnHandlerObj application:application deviceToken:deviceToken];
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
- (void)application:(UIApplication *)application
|
|
762
|
+
didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
|
|
763
|
+
[pnHandlerObj application:application error:error];
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
@end
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
#### Step 5: Rich Push with Notification Service Extension
|
|
770
|
+
|
|
771
|
+
To support rich push notifications (images, videos, GIFs), add a Notification Service Extension:
|
|
772
|
+
|
|
773
|
+
**1. Create Notification Service Extension:**
|
|
774
|
+
|
|
775
|
+
1. In Xcode: **File** → **New** → **Target**
|
|
776
|
+
2. Select **Notification Service Extension**
|
|
777
|
+
3. Name it `NotificationServiceExtension`
|
|
778
|
+
4. Click **Finish** (activate scheme when prompted)
|
|
779
|
+
|
|
780
|
+
**2. Add Zixflow to Extension:**
|
|
781
|
+
|
|
782
|
+
Update your `ios/Podfile` to include the extension:
|
|
783
|
+
|
|
784
|
+
```ruby
|
|
785
|
+
# Main app target
|
|
786
|
+
target 'YourApp' do
|
|
787
|
+
# ... existing configuration ...
|
|
788
|
+
pod 'zixflow-reactnative/apn', :path => '../node_modules/zixflow-reactnative'
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
# Notification Service Extension target
|
|
792
|
+
target 'NotificationServiceExtension' do
|
|
793
|
+
pod 'zixflow-reactnative-richpush/apn', :path => '../node_modules/zixflow-reactnative'
|
|
794
|
+
end
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
For FCM:
|
|
798
|
+
|
|
799
|
+
```ruby
|
|
800
|
+
target 'NotificationServiceExtension' do
|
|
801
|
+
pod 'zixflow-reactnative-richpush/fcm', :path => '../node_modules/zixflow-reactnative'
|
|
802
|
+
end
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
**3. Install dependencies:**
|
|
806
|
+
|
|
807
|
+
```bash
|
|
808
|
+
cd ios
|
|
809
|
+
pod install
|
|
810
|
+
cd ..
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
**4. Update `NotificationService.swift`:**
|
|
814
|
+
|
|
815
|
+
```swift
|
|
816
|
+
import UserNotifications
|
|
817
|
+
import ZixflowMessagingPushAPN
|
|
818
|
+
|
|
819
|
+
class NotificationService: UNNotificationServiceExtension {
|
|
820
|
+
var contentHandler: ((UNNotificationContent) -> Void)?
|
|
821
|
+
var bestAttemptContent: UNMutableNotificationContent?
|
|
822
|
+
|
|
823
|
+
override func didReceive(
|
|
824
|
+
_ request: UNNotificationRequest,
|
|
825
|
+
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
|
|
826
|
+
) {
|
|
827
|
+
self.contentHandler = contentHandler
|
|
828
|
+
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
|
|
829
|
+
|
|
830
|
+
// Initialize SDK for extension
|
|
831
|
+
MessagingPushAPN.initializeForExtension(
|
|
832
|
+
withConfig: MessagingPushConfigBuilder(apiKey: "YOUR_API_KEY")
|
|
833
|
+
.build()
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
if let bestAttemptContent = bestAttemptContent {
|
|
837
|
+
// Let Zixflow handle rich push and track delivery
|
|
838
|
+
MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
override func serviceExtensionTimeWillExpire() {
|
|
843
|
+
// Called when extension is about to be terminated
|
|
844
|
+
MessagingPush.shared.serviceExtensionTimeWillExpire()
|
|
845
|
+
|
|
846
|
+
if let contentHandler = contentHandler,
|
|
847
|
+
let bestAttemptContent = bestAttemptContent {
|
|
848
|
+
contentHandler(bestAttemptContent)
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
**Important:** Replace `"YOUR_API_KEY"` with your actual API key.
|
|
855
|
+
|
|
856
|
+
---
|
|
857
|
+
|
|
858
|
+
### Android Setup
|
|
859
|
+
|
|
860
|
+
Android push notifications use Firebase Cloud Messaging (FCM).
|
|
861
|
+
|
|
862
|
+
#### Step 1: Add Firebase to Your Project
|
|
863
|
+
|
|
864
|
+
1. Go to [Firebase Console](https://console.firebase.google.com/)
|
|
865
|
+
2. Add your Android app to Firebase project
|
|
866
|
+
3. Download `google-services.json`
|
|
867
|
+
4. Place `google-services.json` in `android/app/` directory
|
|
868
|
+
|
|
869
|
+
#### Step 2: Configure Gradle
|
|
870
|
+
|
|
871
|
+
**Project-level `android/build.gradle`:**
|
|
872
|
+
|
|
873
|
+
```gradle
|
|
874
|
+
buildscript {
|
|
875
|
+
ext {
|
|
876
|
+
minSdkVersion = 24
|
|
877
|
+
compileSdkVersion = 34
|
|
878
|
+
targetSdkVersion = 34
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
dependencies {
|
|
882
|
+
classpath 'com.google.gms:google-services:4.4.2'
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
allprojects {
|
|
887
|
+
repositories {
|
|
888
|
+
google()
|
|
889
|
+
mavenCentral()
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
**App-level `android/app/build.gradle`:**
|
|
895
|
+
|
|
896
|
+
```gradle
|
|
897
|
+
apply plugin: 'com.android.application'
|
|
898
|
+
apply plugin: 'com.google.gms.google-services' // Add this line
|
|
899
|
+
|
|
900
|
+
android {
|
|
901
|
+
// ... existing configuration ...
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
dependencies {
|
|
905
|
+
// React Native auto-linking handles Zixflow dependencies
|
|
906
|
+
// ... your other dependencies ...
|
|
907
|
+
}
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
#### Step 3: Add Permissions
|
|
911
|
+
|
|
912
|
+
Add to `android/app/src/main/AndroidManifest.xml`:
|
|
913
|
+
|
|
914
|
+
```xml
|
|
915
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
916
|
+
|
|
917
|
+
<!-- Required for push notifications -->
|
|
918
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
919
|
+
|
|
920
|
+
<!-- Required for Android 13+ (API 33+) -->
|
|
921
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
922
|
+
|
|
923
|
+
<application>
|
|
924
|
+
<!-- Your app configuration -->
|
|
925
|
+
</application>
|
|
926
|
+
</manifest>
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
#### Step 4: Configure Notification Icon and Color (Optional)
|
|
930
|
+
|
|
931
|
+
Add to `android/app/src/main/AndroidManifest.xml` inside `<application>` tag:
|
|
932
|
+
|
|
933
|
+
```xml
|
|
934
|
+
<application>
|
|
935
|
+
<!-- Default notification icon (white, transparent background) -->
|
|
936
|
+
<meta-data
|
|
937
|
+
android:name="com.google.firebase.messaging.default_notification_icon"
|
|
938
|
+
android:resource="@drawable/ic_notification" />
|
|
939
|
+
|
|
940
|
+
<!-- Default notification color (accent color) -->
|
|
941
|
+
<meta-data
|
|
942
|
+
android:name="com.google.firebase.messaging.default_notification_color"
|
|
943
|
+
android:resource="@color/notification_color" />
|
|
944
|
+
</application>
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
---
|
|
948
|
+
|
|
949
|
+
### React Native Integration
|
|
950
|
+
|
|
951
|
+
#### Step 1: Initialize SDK
|
|
952
|
+
|
|
953
|
+
Initialize the Zixflow SDK in your `App.tsx`:
|
|
954
|
+
|
|
955
|
+
```typescript
|
|
956
|
+
import React, { useEffect } from 'react';
|
|
957
|
+
import { Zixflow, ZixflowConfig, ZixflowLogLevel } from 'zixflow-reactnative';
|
|
958
|
+
|
|
959
|
+
export default function App() {
|
|
960
|
+
useEffect(() => {
|
|
961
|
+
// Configure the SDK
|
|
962
|
+
const config: ZixflowConfig = {
|
|
963
|
+
apiKey: 'YOUR_API_KEY',
|
|
964
|
+
logLevel: ZixflowLogLevel.debug,
|
|
965
|
+
inApp: {
|
|
966
|
+
},
|
|
967
|
+
autoTrackDeviceAttributes: true,
|
|
968
|
+
trackApplicationLifecycleEvents: true,
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
// Initialize the SDK
|
|
972
|
+
Zixflow.initialize(config);
|
|
973
|
+
|
|
974
|
+
console.log('Zixflow SDK initialized');
|
|
975
|
+
}, []);
|
|
976
|
+
|
|
977
|
+
return (
|
|
978
|
+
// Your app UI
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
#### Step 2: Request Push Permission
|
|
984
|
+
|
|
985
|
+
Request permission to send push notifications:
|
|
986
|
+
|
|
987
|
+
```typescript
|
|
988
|
+
import React, { useState } from 'react';
|
|
989
|
+
import { Button, Alert } from 'react-native';
|
|
990
|
+
import { Zixflow, ZixflowPushPermissionStatus } from 'zixflow-reactnative';
|
|
991
|
+
|
|
992
|
+
export default function PushPermissionButton() {
|
|
993
|
+
const [permissionStatus, setPermissionStatus] = useState<ZixflowPushPermissionStatus | null>(null);
|
|
994
|
+
|
|
995
|
+
const requestPushPermission = async () => {
|
|
996
|
+
try {
|
|
997
|
+
const permission = await Zixflow.pushMessaging.showPromptForPushNotifications({
|
|
998
|
+
ios: {
|
|
999
|
+
sound: true,
|
|
1000
|
+
badge: true,
|
|
1001
|
+
alert: true,
|
|
1002
|
+
},
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
setPermissionStatus(permission);
|
|
1006
|
+
|
|
1007
|
+
switch (permission) {
|
|
1008
|
+
case ZixflowPushPermissionStatus.Granted:
|
|
1009
|
+
Alert.alert('Success', 'Push notifications enabled!');
|
|
1010
|
+
console.log('Push notifications enabled');
|
|
1011
|
+
break;
|
|
1012
|
+
|
|
1013
|
+
case ZixflowPushPermissionStatus.Denied:
|
|
1014
|
+
Alert.alert('Denied', 'Push notification permission was denied');
|
|
1015
|
+
console.log('Push notifications denied');
|
|
1016
|
+
break;
|
|
1017
|
+
|
|
1018
|
+
case ZixflowPushPermissionStatus.NotDetermined:
|
|
1019
|
+
// iOS only: permission not yet asked
|
|
1020
|
+
console.log('Push notification permission not determined');
|
|
1021
|
+
break;
|
|
1022
|
+
}
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
console.error('Failed to request push permission:', error);
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
|
|
1028
|
+
return (
|
|
1029
|
+
<Button
|
|
1030
|
+
title="Enable Push Notifications"
|
|
1031
|
+
onPress={requestPushPermission}
|
|
1032
|
+
/>
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
**Permission Options (iOS)**:
|
|
1038
|
+
|
|
1039
|
+
| Option | Type | Description |
|
|
1040
|
+
|--------|------|-------------|
|
|
1041
|
+
| `sound` | boolean | Enable notification sounds |
|
|
1042
|
+
| `badge` | boolean | Enable app badge updates |
|
|
1043
|
+
| `alert` | boolean | Show notification alerts |
|
|
1044
|
+
|
|
1045
|
+
#### Step 3: Check Permission Status
|
|
1046
|
+
|
|
1047
|
+
Check current push permission status:
|
|
1048
|
+
|
|
1049
|
+
```typescript
|
|
1050
|
+
import { Zixflow, ZixflowPushPermissionStatus } from 'zixflow-reactnative';
|
|
1051
|
+
|
|
1052
|
+
const checkPushPermission = async () => {
|
|
1053
|
+
try {
|
|
1054
|
+
const status = await Zixflow.pushMessaging.getPushPermissionStatus();
|
|
1055
|
+
|
|
1056
|
+
console.log('Push permission status:', status);
|
|
1057
|
+
|
|
1058
|
+
if (status === ZixflowPushPermissionStatus.Granted) {
|
|
1059
|
+
console.log('Push notifications are enabled');
|
|
1060
|
+
} else if (status === ZixflowPushPermissionStatus.Denied) {
|
|
1061
|
+
console.log('Push notifications are disabled');
|
|
1062
|
+
} else {
|
|
1063
|
+
console.log('Push permission not determined');
|
|
1064
|
+
}
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
console.error('Failed to get push permission status:', error);
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
#### Step 4: Identify Users
|
|
1072
|
+
|
|
1073
|
+
**Critical**: Device tokens must be associated with a user before push notifications can be delivered.
|
|
1074
|
+
|
|
1075
|
+
```typescript
|
|
1076
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
1077
|
+
|
|
1078
|
+
// Identify user when they log in
|
|
1079
|
+
const handleLogin = async (userId: string, userEmail: string) => {
|
|
1080
|
+
try {
|
|
1081
|
+
// Identify user with Zixflow
|
|
1082
|
+
Zixflow.identify({
|
|
1083
|
+
userId: userId,
|
|
1084
|
+
traits: {
|
|
1085
|
+
email: userEmail,
|
|
1086
|
+
firstName: 'John',
|
|
1087
|
+
lastName: 'Doe',
|
|
1088
|
+
plan: 'premium',
|
|
1089
|
+
},
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
console.log('User identified:', userId);
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
console.error('Failed to identify user:', error);
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
**Important Notes:**
|
|
1100
|
+
- When users start the app, they automatically generate a device token
|
|
1101
|
+
- This token links to their Zixflow profile when you call `identify()`
|
|
1102
|
+
- This ensures notifications are delivered to the correct user
|
|
1103
|
+
|
|
1104
|
+
#### Step 5: Get Device Token
|
|
1105
|
+
|
|
1106
|
+
Retrieve the registered device token:
|
|
1107
|
+
|
|
1108
|
+
```typescript
|
|
1109
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
1110
|
+
|
|
1111
|
+
const getDeviceToken = async () => {
|
|
1112
|
+
try {
|
|
1113
|
+
const token = await Zixflow.pushMessaging.getRegisteredDeviceToken();
|
|
1114
|
+
console.log('Device token:', token);
|
|
1115
|
+
return token;
|
|
1116
|
+
} catch (error) {
|
|
1117
|
+
console.error('Failed to get device token:', error);
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
---
|
|
1124
|
+
|
|
1125
|
+
### Deep Links and Navigation
|
|
1126
|
+
|
|
1127
|
+
Handle deep links from push notifications:
|
|
1128
|
+
|
|
1129
|
+
#### Setup Deep Linking
|
|
1130
|
+
|
|
1131
|
+
**1. Configure Deep Links (iOS):**
|
|
1132
|
+
|
|
1133
|
+
Add to `ios/YourApp/Info.plist`:
|
|
1134
|
+
|
|
1135
|
+
```xml
|
|
1136
|
+
<key>CFBundleURLTypes</key>
|
|
1137
|
+
<array>
|
|
1138
|
+
<dict>
|
|
1139
|
+
<key>CFBundleURLSchemes</key>
|
|
1140
|
+
<array>
|
|
1141
|
+
<string>yourapp</string>
|
|
1142
|
+
</array>
|
|
1143
|
+
</dict>
|
|
1144
|
+
</array>
|
|
1145
|
+
```
|
|
1146
|
+
|
|
1147
|
+
**2. Configure Deep Links (Android):**
|
|
1148
|
+
|
|
1149
|
+
Add to `android/app/src/main/AndroidManifest.xml`:
|
|
1150
|
+
|
|
1151
|
+
```xml
|
|
1152
|
+
<activity android:name=".MainActivity">
|
|
1153
|
+
<!-- Existing intent filter -->
|
|
1154
|
+
|
|
1155
|
+
<!-- Deep link intent filter -->
|
|
1156
|
+
<intent-filter>
|
|
1157
|
+
<action android:name="android.intent.action.VIEW" />
|
|
1158
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
1159
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
1160
|
+
|
|
1161
|
+
<data
|
|
1162
|
+
android:scheme="yourapp"
|
|
1163
|
+
android:host="*" />
|
|
1164
|
+
</intent-filter>
|
|
1165
|
+
|
|
1166
|
+
<!-- Universal Links (optional) -->
|
|
1167
|
+
<intent-filter android:autoVerify="true">
|
|
1168
|
+
<action android:name="android.intent.action.VIEW" />
|
|
1169
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
1170
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
1171
|
+
|
|
1172
|
+
<data
|
|
1173
|
+
android:scheme="https"
|
|
1174
|
+
android:host="yourapp.com" />
|
|
1175
|
+
</intent-filter>
|
|
1176
|
+
</activity>
|
|
1177
|
+
```
|
|
1178
|
+
|
|
1179
|
+
#### Handle Deep Links in React Native
|
|
1180
|
+
|
|
1181
|
+
Use React Navigation's linking configuration:
|
|
1182
|
+
|
|
1183
|
+
```typescript
|
|
1184
|
+
import React from 'react';
|
|
1185
|
+
import { NavigationContainer } from '@react-navigation/native';
|
|
1186
|
+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
|
1187
|
+
import { Linking } from 'react-native';
|
|
1188
|
+
|
|
1189
|
+
const Stack = createNativeStackNavigator();
|
|
1190
|
+
|
|
1191
|
+
const linking = {
|
|
1192
|
+
prefixes: ['yourapp://', 'https://yourapp.com'],
|
|
1193
|
+
config: {
|
|
1194
|
+
screens: {
|
|
1195
|
+
Home: 'home',
|
|
1196
|
+
Product: 'product/:id',
|
|
1197
|
+
Profile: 'profile',
|
|
1198
|
+
},
|
|
1199
|
+
},
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
export default function App() {
|
|
1203
|
+
return (
|
|
1204
|
+
<NavigationContainer linking={linking}>
|
|
1205
|
+
<Stack.Navigator>
|
|
1206
|
+
<Stack.Screen name="Home" component={HomeScreen} />
|
|
1207
|
+
<Stack.Screen name="Product" component={ProductScreen} />
|
|
1208
|
+
<Stack.Screen name="Profile" component={ProfileScreen} />
|
|
1209
|
+
</Stack.Navigator>
|
|
1210
|
+
</NavigationContainer>
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
---
|
|
1216
|
+
|
|
1217
|
+
### Rich Push Notification Payloads
|
|
1218
|
+
|
|
1219
|
+
The Zixflow SDK automatically handles rich push notifications. Here's how to structure payloads:
|
|
1220
|
+
|
|
1221
|
+
#### iOS APNs Payload
|
|
1222
|
+
|
|
1223
|
+
```json
|
|
1224
|
+
{
|
|
1225
|
+
"aps": {
|
|
1226
|
+
"mutable-content": 1,
|
|
1227
|
+
"alert": {
|
|
1228
|
+
"title": "New Product Available",
|
|
1229
|
+
"body": "Check out our latest premium features"
|
|
1230
|
+
},
|
|
1231
|
+
"badge": 1,
|
|
1232
|
+
"sound": "default"
|
|
1233
|
+
},
|
|
1234
|
+
"Zixflow": {
|
|
1235
|
+
"push": {
|
|
1236
|
+
"link": "yourapp://product/123",
|
|
1237
|
+
"image": "https://example.com/product-image.jpg"
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
```
|
|
1242
|
+
|
|
1243
|
+
#### iOS FCM Payload
|
|
1244
|
+
|
|
1245
|
+
```json
|
|
1246
|
+
{
|
|
1247
|
+
"message": {
|
|
1248
|
+
"apns": {
|
|
1249
|
+
"payload": {
|
|
1250
|
+
"aps": {
|
|
1251
|
+
"mutable-content": 1,
|
|
1252
|
+
"alert": {
|
|
1253
|
+
"title": "Special Offer",
|
|
1254
|
+
"body": "Limited time discount!"
|
|
1255
|
+
}
|
|
1256
|
+
},
|
|
1257
|
+
"Zixflow": {
|
|
1258
|
+
"push": {
|
|
1259
|
+
"link": "https://yourapp.com/offers",
|
|
1260
|
+
"image": "https://example.com/offer.png"
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
},
|
|
1264
|
+
"headers": {
|
|
1265
|
+
"apns-priority": 10
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
```
|
|
1271
|
+
|
|
1272
|
+
#### Android FCM Payload
|
|
1273
|
+
|
|
1274
|
+
```json
|
|
1275
|
+
{
|
|
1276
|
+
"message": {
|
|
1277
|
+
"data": {
|
|
1278
|
+
"title": "Order Shipped",
|
|
1279
|
+
"body": "Your order is on the way!",
|
|
1280
|
+
"image": "https://example.com/shipping.jpg",
|
|
1281
|
+
"link": "yourapp://orders/456"
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
```
|
|
1286
|
+
|
|
1287
|
+
---
|
|
1288
|
+
|
|
1289
|
+
### Testing Push Notifications
|
|
1290
|
+
|
|
1291
|
+
#### 1. Verify Device Token Registration
|
|
1292
|
+
|
|
1293
|
+
Check if device token is registered:
|
|
1294
|
+
|
|
1295
|
+
```typescript
|
|
1296
|
+
const verifyPushSetup = async () => {
|
|
1297
|
+
try {
|
|
1298
|
+
const token = await Zixflow.pushMessaging.getRegisteredDeviceToken();
|
|
1299
|
+
const status = await Zixflow.pushMessaging.getPushPermissionStatus();
|
|
1300
|
+
|
|
1301
|
+
console.log('Device token:', token);
|
|
1302
|
+
console.log('Permission status:', status);
|
|
1303
|
+
|
|
1304
|
+
if (token && status === ZixflowPushPermissionStatus.Granted) {
|
|
1305
|
+
console.log('✓ Push notifications ready');
|
|
1306
|
+
} else {
|
|
1307
|
+
console.log('✗ Push setup incomplete');
|
|
1308
|
+
}
|
|
1309
|
+
} catch (error) {
|
|
1310
|
+
console.error('Push verification failed:', error);
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
#### 2. Enable Debug Logging
|
|
1316
|
+
|
|
1317
|
+
Set log level to debug during development:
|
|
1318
|
+
|
|
1319
|
+
```typescript
|
|
1320
|
+
const config: ZixflowConfig = {
|
|
1321
|
+
apiKey: 'YOUR_API_KEY',
|
|
1322
|
+
logLevel: ZixflowLogLevel.debug, // Verbose logging
|
|
1323
|
+
};
|
|
1324
|
+
|
|
1325
|
+
Zixflow.initialize(config);
|
|
1326
|
+
```
|
|
1327
|
+
|
|
1328
|
+
#### 3. Test on Physical Devices
|
|
1329
|
+
|
|
1330
|
+
**iOS:**
|
|
1331
|
+
- Push notifications **do not work** on iOS Simulator
|
|
1332
|
+
- Must use physical iOS device
|
|
1333
|
+
- Ensure APNs certificate is configured in Zixflow dashboard
|
|
1334
|
+
|
|
1335
|
+
**Android:**
|
|
1336
|
+
- Emulator works if Google Play Services installed
|
|
1337
|
+
- Physical device always recommended
|
|
1338
|
+
- Ensure FCM Server Key is configured in Zixflow dashboard
|
|
1339
|
+
|
|
1340
|
+
#### 4. Send Test Push from Zixflow
|
|
1341
|
+
|
|
1342
|
+
1. Log into Zixflow dashboard
|
|
1343
|
+
2. Navigate to **Messaging** → **Push Notifications**
|
|
1344
|
+
3. Create test push notification
|
|
1345
|
+
4. Send to specific user (using `userId` from `identify()`)
|
|
1346
|
+
|
|
1347
|
+
---
|
|
1348
|
+
|
|
1349
|
+
### Troubleshooting
|
|
1350
|
+
|
|
1351
|
+
#### iOS Push Not Received
|
|
1352
|
+
|
|
1353
|
+
**Symptoms:** Push sent but not delivered on iOS
|
|
1354
|
+
|
|
1355
|
+
**Solutions:**
|
|
1356
|
+
1. Verify Push Notifications capability enabled in Xcode
|
|
1357
|
+
2. Check APNs certificate uploaded to Zixflow dashboard
|
|
1358
|
+
3. Ensure testing on physical device (not simulator)
|
|
1359
|
+
4. Verify user identified: `Zixflow.identify({ userId: ... })`
|
|
1360
|
+
5. Check permission granted: `getPushPermissionStatus()`
|
|
1361
|
+
6. Review logs for errors
|
|
1362
|
+
|
|
1363
|
+
#### Android Push Not Received
|
|
1364
|
+
|
|
1365
|
+
**Symptoms:** Push sent but not delivered on Android
|
|
1366
|
+
|
|
1367
|
+
**Solutions:**
|
|
1368
|
+
1. Verify `google-services.json` in `android/app/` directory
|
|
1369
|
+
2. Check FCM Server Key in Zixflow dashboard
|
|
1370
|
+
3. Ensure POST_NOTIFICATIONS permission granted (Android 13+)
|
|
1371
|
+
4. Verify Google Services plugin applied
|
|
1372
|
+
5. Check device token registered
|
|
1373
|
+
6. Review logs for errors
|
|
1374
|
+
|
|
1375
|
+
#### Rich Push Not Working (Images)
|
|
1376
|
+
|
|
1377
|
+
**Symptoms:** Images not loading in notifications
|
|
1378
|
+
|
|
1379
|
+
**iOS Solutions:**
|
|
1380
|
+
1. Ensure Notification Service Extension properly configured
|
|
1381
|
+
2. Verify extension has correct dependencies in Podfile
|
|
1382
|
+
3. Check App Groups configuration (if using)
|
|
1383
|
+
4. Verify image URL is accessible
|
|
1384
|
+
5. Check extension logs for errors
|
|
1385
|
+
|
|
1386
|
+
**Android Solutions:**
|
|
1387
|
+
1. Verify image URL is accessible from device
|
|
1388
|
+
2. Check internet connectivity
|
|
1389
|
+
3. Ensure image format is supported (JPEG, PNG, GIF)
|
|
1390
|
+
|
|
1391
|
+
#### Permission Denied
|
|
1392
|
+
|
|
1393
|
+
**Symptoms:** User denied push permission
|
|
1394
|
+
|
|
1395
|
+
**Solutions:**
|
|
1396
|
+
1. Explain value before requesting permission
|
|
1397
|
+
2. Guide users to Settings to enable manually
|
|
1398
|
+
3. Show in-app message explaining benefits
|
|
1399
|
+
4. Request permission at appropriate time (after user engagement)
|
|
1400
|
+
|
|
1401
|
+
#### Device Token Not Registered
|
|
1402
|
+
|
|
1403
|
+
**Symptoms:** `getRegisteredDeviceToken()` returns empty or fails
|
|
1404
|
+
|
|
1405
|
+
**Solutions:**
|
|
1406
|
+
1. Verify SDK initialized before calling
|
|
1407
|
+
2. Check permission granted
|
|
1408
|
+
3. Ensure identify() called to associate token
|
|
1409
|
+
4. Review native logs for errors
|
|
1410
|
+
5. Rebuild app and retry
|
|
1411
|
+
|
|
1412
|
+
---
|
|
1413
|
+
|
|
1414
|
+
### Best Practices
|
|
1415
|
+
|
|
1416
|
+
1. **Request permission thoughtfully** - Explain value before asking
|
|
1417
|
+
2. **Identify users immediately** after authentication
|
|
1418
|
+
3. **Test on physical devices** - Simulators have limitations
|
|
1419
|
+
4. **Use rich push** - Images increase engagement
|
|
1420
|
+
5. **Handle deep links** - Provide seamless navigation
|
|
1421
|
+
6. **Monitor metrics** - Track delivery and open rates
|
|
1422
|
+
7. **Enable debug logging** - During development only
|
|
1423
|
+
8. **Test both platforms** - iOS and Android behave differently
|
|
1424
|
+
9. **Clear identity on logout** - Call `clearIdentify()`
|
|
1425
|
+
10. **Keep credentials secure** - Never commit API keys to git
|
|
1426
|
+
|
|
1427
|
+
---
|
|
1428
|
+
|
|
1429
|
+
---
|
|
15
1430
|
|
|
16
|
-
|
|
1431
|
+
## Push Notification Tracking
|
|
17
1432
|
|
|
18
|
-
|
|
19
|
-
> 🧪 Example apps: [see the `/example` directory](/example)
|
|
1433
|
+
### Overview
|
|
20
1434
|
|
|
21
|
-
|
|
1435
|
+
Zixflow automatically tracks push notification delivery and engagement metrics to help you measure campaign performance. The SDK tracks three key lifecycle events:
|
|
1436
|
+
|
|
1437
|
+
1. **Delivery Confirmed** - When the notification arrives on the device
|
|
1438
|
+
2. **Notification Opened** - When the user taps the notification
|
|
1439
|
+
3. **Action Button Clicked** - When the user taps an action button
|
|
22
1440
|
|
|
23
|
-
|
|
1441
|
+
### How Push Tracking Works
|
|
24
1442
|
|
|
25
|
-
|
|
1443
|
+
When Zixflow sends a push notification, it includes special tracking fields in the data payload:
|
|
26
1444
|
|
|
27
|
-
|
|
1445
|
+
| Field | Purpose |
|
|
1446
|
+
|-------|---------|
|
|
1447
|
+
| `Zixflow-Delivery-ID` | Unique ID for this delivery (links to campaign record) |
|
|
1448
|
+
| `Zixflow-Delivery-Token` | The FCM/APNs token the notification was sent to |
|
|
1449
|
+
|
|
1450
|
+
**Example push payload:**
|
|
1451
|
+
```json
|
|
1452
|
+
{
|
|
1453
|
+
"Zixflow-Delivery-ID": "626533406292836846",
|
|
1454
|
+
"Zixflow-Delivery-Token": "dcFRlDhiRbehM1vg...",
|
|
1455
|
+
"title": "Flash Sale! 70% OFF",
|
|
1456
|
+
"body": "Limited time only — grab your deal now!",
|
|
1457
|
+
"deeplink_url": "https://yourapp.com/sale",
|
|
1458
|
+
"image_url": "https://cdn.yourapp.com/banner.png",
|
|
1459
|
+
"action_buttons": "[{\"name\":\"Shop Now\",\"deeplink\":\"https://yourapp.com/sale\"},{\"name\":\"Remind Me\",\"deeplink\":\"\"}]"
|
|
1460
|
+
}
|
|
1461
|
+
```
|
|
28
1462
|
|
|
29
|
-
|
|
30
|
-
- 📲 Push notifications (iOS APN and Android FCM)
|
|
31
|
-
- 💬 In-app messaging
|
|
32
|
-
- 📍 Location tracking (optional)
|
|
33
|
-
- 🎯 Personalized customer experiences
|
|
34
|
-
- 📈 Analytics and customer data pipelines
|
|
35
|
-
- 🔧 TypeScript support
|
|
36
|
-
- ⚡ React Native New Architecture (TurboModules) support
|
|
1463
|
+
> **Note:** The SDK automatically handles most tracking for you when using the default push implementation. This section covers advanced customization scenarios.
|
|
37
1464
|
|
|
38
1465
|
---
|
|
39
1466
|
|
|
40
|
-
|
|
1467
|
+
### Tracking Events
|
|
41
1468
|
|
|
42
|
-
|
|
43
|
-
npm install zixflow-reactnative
|
|
44
|
-
```
|
|
1469
|
+
#### 1. Delivery Confirmed
|
|
45
1470
|
|
|
46
|
-
|
|
1471
|
+
**When to track:** The moment the push payload arrives on the device
|
|
47
1472
|
|
|
48
|
-
|
|
49
|
-
|
|
1473
|
+
**SDK method:** `Zixflow.trackMetric({ deliveryID, deviceToken, event: MetricEvent.delivered })`
|
|
1474
|
+
|
|
1475
|
+
**Implementation:**
|
|
1476
|
+
|
|
1477
|
+
```typescript
|
|
1478
|
+
import messaging from '@react-native-firebase/messaging';
|
|
1479
|
+
import { Zixflow, MetricEvent } from 'zixflow-reactnative';
|
|
1480
|
+
|
|
1481
|
+
// Listen for foreground messages
|
|
1482
|
+
useEffect(() => {
|
|
1483
|
+
const unsubscribe = messaging().onMessage(async (remoteMessage) => {
|
|
1484
|
+
const deliveryId = remoteMessage.data?.['Zixflow-Delivery-ID'] ?? '';
|
|
1485
|
+
const deliveryToken = remoteMessage.data?.['Zixflow-Delivery-Token'] ?? cachedToken ?? '';
|
|
1486
|
+
|
|
1487
|
+
if (deliveryId && deliveryToken) {
|
|
1488
|
+
await Zixflow.trackMetric({
|
|
1489
|
+
deliveryID: deliveryId,
|
|
1490
|
+
deviceToken: deliveryToken,
|
|
1491
|
+
event: MetricEvent.delivered,
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
return unsubscribe;
|
|
1497
|
+
}, []);
|
|
50
1498
|
```
|
|
51
1499
|
|
|
1500
|
+
> **Important:** The Zixflow SDK automatically tracks delivery. Manual tracking is only needed for custom implementations.
|
|
1501
|
+
|
|
52
1502
|
---
|
|
53
1503
|
|
|
54
|
-
|
|
1504
|
+
#### 2. Notification Opened
|
|
55
1505
|
|
|
56
|
-
|
|
1506
|
+
**When to track:** When the user taps the notification banner
|
|
1507
|
+
|
|
1508
|
+
**SDK method:** `Zixflow.trackMetric({ deliveryID, deviceToken, event: MetricEvent.opened })`
|
|
57
1509
|
|
|
58
|
-
|
|
1510
|
+
**Implementation:**
|
|
59
1511
|
|
|
60
|
-
```
|
|
61
|
-
import
|
|
62
|
-
|
|
63
|
-
ZixflowConfig,
|
|
64
|
-
ZixflowLogLevel,
|
|
65
|
-
} from 'zixflow-reactnative';
|
|
1512
|
+
```typescript
|
|
1513
|
+
import messaging from '@react-native-firebase/messaging';
|
|
1514
|
+
import { Zixflow, MetricEvent } from 'zixflow-reactnative';
|
|
66
1515
|
|
|
1516
|
+
// Background / quit tap
|
|
67
1517
|
useEffect(() => {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
},
|
|
76
|
-
push: {
|
|
77
|
-
android: {
|
|
78
|
-
pushClickBehavior: 'ActivityPreventRestart', // Optional
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
};
|
|
1518
|
+
const unsubscribe = messaging().onNotificationOpenedApp(async (remoteMessage) => {
|
|
1519
|
+
trackOpened(remoteMessage.data);
|
|
1520
|
+
handleDeeplink(remoteMessage.data?.deeplink_url);
|
|
1521
|
+
});
|
|
1522
|
+
|
|
1523
|
+
return unsubscribe;
|
|
1524
|
+
}, []);
|
|
82
1525
|
|
|
83
|
-
|
|
1526
|
+
// Terminated tap
|
|
1527
|
+
useEffect(() => {
|
|
1528
|
+
messaging()
|
|
1529
|
+
.getInitialNotification()
|
|
1530
|
+
.then((remoteMessage) => {
|
|
1531
|
+
if (remoteMessage) {
|
|
1532
|
+
trackOpened(remoteMessage.data);
|
|
1533
|
+
handleDeeplink(remoteMessage.data?.deeplink_url);
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
84
1536
|
}, []);
|
|
1537
|
+
|
|
1538
|
+
const trackOpened = async (data: any) => {
|
|
1539
|
+
const deliveryId = data?.['Zixflow-Delivery-ID'] ?? '';
|
|
1540
|
+
const deliveryToken = data?.['Zixflow-Delivery-Token'] ?? cachedToken ?? '';
|
|
1541
|
+
|
|
1542
|
+
if (deliveryId && deliveryToken) {
|
|
1543
|
+
await Zixflow.trackMetric({
|
|
1544
|
+
deliveryID: deliveryId,
|
|
1545
|
+
deviceToken: deliveryToken,
|
|
1546
|
+
event: MetricEvent.opened,
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
85
1550
|
```
|
|
86
1551
|
|
|
87
|
-
>
|
|
1552
|
+
> **Note:** The SDK automatically tracks opens when using the default push implementation.
|
|
88
1553
|
|
|
89
1554
|
---
|
|
90
1555
|
|
|
91
|
-
|
|
1556
|
+
#### 3. Action Button Clicked
|
|
1557
|
+
|
|
1558
|
+
**When to track:** When the user taps a named action button
|
|
1559
|
+
|
|
1560
|
+
**SDK method:** `Zixflow.track({ name: "Push Notification Action Clicked", properties: {...} })`
|
|
1561
|
+
|
|
1562
|
+
**Implementation:**
|
|
1563
|
+
|
|
1564
|
+
```typescript
|
|
1565
|
+
import { Zixflow, MetricEvent } from 'zixflow-reactnative';
|
|
1566
|
+
|
|
1567
|
+
// Handle action press from local notification
|
|
1568
|
+
const handleActionPress = async (data: any, actionId: string) => {
|
|
1569
|
+
const deliveryId = data['Zixflow-Delivery-ID'] ?? '';
|
|
1570
|
+
const token = data['Zixflow-Delivery-Token'] ?? '';
|
|
1571
|
+
const actionIndex = parseInt(actionId.replace(/\D/g, ''), 10);
|
|
1572
|
+
const buttons = parseButtons(data['action_buttons']);
|
|
1573
|
+
const actionName = buttons[actionIndex]?.name ?? `Action ${actionIndex + 1}`;
|
|
1574
|
+
const actionDeeplink = buttons[actionIndex]?.deeplink ?? '';
|
|
1575
|
+
|
|
1576
|
+
// Always fire opened first
|
|
1577
|
+
await Zixflow.trackMetric({
|
|
1578
|
+
deliveryID: deliveryId,
|
|
1579
|
+
deviceToken: token,
|
|
1580
|
+
event: MetricEvent.opened,
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
// Then fire action clicked
|
|
1584
|
+
await Zixflow.track({
|
|
1585
|
+
name: 'Push Notification Action Clicked',
|
|
1586
|
+
properties: {
|
|
1587
|
+
'Zixflow-Delivery-ID': deliveryId,
|
|
1588
|
+
'Zixflow-Delivery-Token': token,
|
|
1589
|
+
'action_index': actionIndex,
|
|
1590
|
+
'action_name': actionName,
|
|
1591
|
+
'action_deeplink': actionDeeplink,
|
|
1592
|
+
},
|
|
1593
|
+
});
|
|
1594
|
+
};
|
|
1595
|
+
|
|
1596
|
+
const parseButtons = (raw: any) => {
|
|
1597
|
+
if (!raw) return [];
|
|
1598
|
+
try {
|
|
1599
|
+
return typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
1600
|
+
} catch {
|
|
1601
|
+
return [];
|
|
1602
|
+
}
|
|
1603
|
+
};
|
|
1604
|
+
```
|
|
92
1605
|
|
|
93
|
-
|
|
1606
|
+
**Action button properties:**
|
|
94
1607
|
|
|
95
|
-
|
|
1608
|
+
| Property | Type | Required | Description |
|
|
1609
|
+
|----------|------|----------|-------------|
|
|
1610
|
+
| `Zixflow-Delivery-ID` | string | ✅ | Links event to campaign delivery |
|
|
1611
|
+
| `action_index` | integer | ✅ | 0-based index of button tapped |
|
|
1612
|
+
| `action_name` | string | ✅ | Button label (e.g., "Shop Now") |
|
|
1613
|
+
| `Zixflow-Delivery-Token` | string | Recommended | FCM/APNs token |
|
|
1614
|
+
| `action_deeplink` | string | Recommended | Button's URL |
|
|
96
1615
|
|
|
97
|
-
|
|
98
|
-
Zixflow.identify({
|
|
99
|
-
userId: 'user123',
|
|
100
|
-
traits: {
|
|
101
|
-
email: 'user@example.com',
|
|
102
|
-
firstName: 'John',
|
|
103
|
-
lastName: 'Doe',
|
|
104
|
-
plan: 'premium',
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
```
|
|
1616
|
+
---
|
|
108
1617
|
|
|
109
|
-
###
|
|
1618
|
+
### Action Buttons Format
|
|
110
1619
|
|
|
111
|
-
|
|
1620
|
+
The `action_buttons` field is a JSON-encoded string containing button definitions:
|
|
112
1621
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
1622
|
+
**Raw payload value:**
|
|
1623
|
+
```
|
|
1624
|
+
"[{\"name\":\"Shop Now\",\"deeplink\":\"https://yourapp.com/sale\"},{\"name\":\"Remind Me\",\"deeplink\":\"\"}]"
|
|
1625
|
+
```
|
|
1626
|
+
|
|
1627
|
+
**Parsed structure:**
|
|
1628
|
+
```json
|
|
1629
|
+
[
|
|
1630
|
+
{ "name": "Shop Now", "deeplink": "https://yourapp.com/sale" },
|
|
1631
|
+
{ "name": "Remind Me", "deeplink": "" }
|
|
1632
|
+
]
|
|
120
1633
|
```
|
|
121
1634
|
|
|
122
|
-
|
|
1635
|
+
**Parsing example:**
|
|
1636
|
+
|
|
1637
|
+
```typescript
|
|
1638
|
+
const parseActionButtons = (raw: any): Array<{ name: string; deeplink: string }> => {
|
|
1639
|
+
if (!raw) return [];
|
|
123
1640
|
|
|
124
|
-
|
|
1641
|
+
try {
|
|
1642
|
+
const decoded = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
1643
|
+
return Array.isArray(decoded) ? decoded : [];
|
|
1644
|
+
} catch (error) {
|
|
1645
|
+
console.error('Error parsing action buttons:', error);
|
|
1646
|
+
return [];
|
|
1647
|
+
}
|
|
1648
|
+
};
|
|
125
1649
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
1650
|
+
// Usage
|
|
1651
|
+
const buttons = parseActionButtons(remoteMessage.data?.action_buttons);
|
|
1652
|
+
buttons.forEach((btn, index) => {
|
|
1653
|
+
console.log(`Button ${index}: ${btn.name} -> ${btn.deeplink}`);
|
|
130
1654
|
});
|
|
131
1655
|
```
|
|
132
1656
|
|
|
133
|
-
|
|
1657
|
+
**Rules:**
|
|
1658
|
+
- Maximum 2 buttons per notification (recommended for consistency across platforms)
|
|
1659
|
+
- `deeplink` may be empty — handle gracefully
|
|
1660
|
+
- Button index is 0-based (first button = index 0)
|
|
134
1661
|
|
|
135
|
-
|
|
1662
|
+
---
|
|
136
1663
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
1664
|
+
### Complete Push Tracking Implementation
|
|
1665
|
+
|
|
1666
|
+
Here's a complete example integrating all tracking events:
|
|
1667
|
+
|
|
1668
|
+
```typescript
|
|
1669
|
+
import React, { useEffect, useState } from 'react';
|
|
1670
|
+
import messaging, { FirebaseMessagingTypes } from '@react-native-firebase/messaging';
|
|
1671
|
+
import { Zixflow, MetricEvent } from 'zixflow-reactnative';
|
|
1672
|
+
|
|
1673
|
+
const App = () => {
|
|
1674
|
+
const [fcmToken, setFcmToken] = useState<string | null>(null);
|
|
1675
|
+
|
|
1676
|
+
useEffect(() => {
|
|
1677
|
+
setupPushNotifications();
|
|
1678
|
+
}, []);
|
|
1679
|
+
|
|
1680
|
+
const setupPushNotifications = async () => {
|
|
1681
|
+
// Request permission
|
|
1682
|
+
const authStatus = await messaging().requestPermission();
|
|
1683
|
+
const enabled =
|
|
1684
|
+
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
|
|
1685
|
+
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
|
|
1686
|
+
|
|
1687
|
+
if (enabled) {
|
|
1688
|
+
// Get FCM token
|
|
1689
|
+
const token = await messaging().getToken();
|
|
1690
|
+
setFcmToken(token);
|
|
1691
|
+
|
|
1692
|
+
// Register with Zixflow
|
|
1693
|
+
await Zixflow.registerDeviceToken({ deviceToken: token });
|
|
1694
|
+
|
|
1695
|
+
// Listen for token refresh
|
|
1696
|
+
messaging().onTokenRefresh(async (newToken) => {
|
|
1697
|
+
setFcmToken(newToken);
|
|
1698
|
+
await Zixflow.registerDeviceToken({ deviceToken: newToken });
|
|
1699
|
+
});
|
|
1700
|
+
|
|
1701
|
+
// Handle foreground messages
|
|
1702
|
+
messaging().onMessage(async (remoteMessage) => {
|
|
1703
|
+
trackDelivery(remoteMessage);
|
|
1704
|
+
showLocalNotification(remoteMessage);
|
|
1705
|
+
});
|
|
1706
|
+
|
|
1707
|
+
// Handle background/quit taps
|
|
1708
|
+
messaging().onNotificationOpenedApp((remoteMessage) => {
|
|
1709
|
+
trackOpened(remoteMessage.data);
|
|
1710
|
+
handleDeeplink(remoteMessage.data?.deeplink_url);
|
|
1711
|
+
});
|
|
1712
|
+
|
|
1713
|
+
// Handle terminated tap
|
|
1714
|
+
const initialNotification = await messaging().getInitialNotification();
|
|
1715
|
+
if (initialNotification) {
|
|
1716
|
+
trackOpened(initialNotification.data);
|
|
1717
|
+
handleDeeplink(initialNotification.data?.deeplink_url);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
};
|
|
1721
|
+
|
|
1722
|
+
const trackDelivery = async (message: FirebaseMessagingTypes.RemoteMessage) => {
|
|
1723
|
+
const deliveryId = message.data?.['Zixflow-Delivery-ID'] ?? '';
|
|
1724
|
+
const deliveryToken = message.data?.['Zixflow-Delivery-Token'] ?? fcmToken ?? '';
|
|
1725
|
+
|
|
1726
|
+
if (deliveryId && deliveryToken) {
|
|
1727
|
+
await Zixflow.trackMetric({
|
|
1728
|
+
deliveryID: deliveryId,
|
|
1729
|
+
deviceToken: deliveryToken,
|
|
1730
|
+
event: MetricEvent.delivered,
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
};
|
|
1734
|
+
|
|
1735
|
+
const trackOpened = async (data: any) => {
|
|
1736
|
+
const deliveryId = data?.['Zixflow-Delivery-ID'] ?? '';
|
|
1737
|
+
const deliveryToken = data?.['Zixflow-Delivery-Token'] ?? fcmToken ?? '';
|
|
1738
|
+
|
|
1739
|
+
if (deliveryId && deliveryToken) {
|
|
1740
|
+
await Zixflow.trackMetric({
|
|
1741
|
+
deliveryID: deliveryId,
|
|
1742
|
+
deviceToken: deliveryToken,
|
|
1743
|
+
event: MetricEvent.opened,
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1746
|
+
};
|
|
1747
|
+
|
|
1748
|
+
const handleDeeplink = (url?: string) => {
|
|
1749
|
+
if (!url) return;
|
|
1750
|
+
// Implement your deep link navigation logic
|
|
1751
|
+
console.log('Navigating to:', url);
|
|
1752
|
+
};
|
|
1753
|
+
|
|
1754
|
+
const showLocalNotification = async (message: FirebaseMessagingTypes.RemoteMessage) => {
|
|
1755
|
+
// Use local notification library to show notification
|
|
1756
|
+
};
|
|
1757
|
+
|
|
1758
|
+
return (
|
|
1759
|
+
// Your app UI
|
|
1760
|
+
<></>
|
|
1761
|
+
);
|
|
1762
|
+
};
|
|
1763
|
+
|
|
1764
|
+
export default App;
|
|
146
1765
|
```
|
|
147
1766
|
|
|
148
1767
|
---
|
|
149
1768
|
|
|
150
|
-
|
|
1769
|
+
### Tracking Decision Flowchart
|
|
151
1770
|
|
|
152
|
-
|
|
1771
|
+
```
|
|
1772
|
+
Notification received?
|
|
1773
|
+
├── YES → trackMetric(delivered) [automatic with SDK]
|
|
1774
|
+
│
|
|
1775
|
+
User interacted?
|
|
1776
|
+
├── Tapped notification body
|
|
1777
|
+
│ └── trackMetric(opened) [automatic with SDK]
|
|
1778
|
+
│ └── Navigate to deeplink_url
|
|
1779
|
+
│
|
|
1780
|
+
└── Tapped action button
|
|
1781
|
+
├── trackMetric(opened) [automatic with SDK]
|
|
1782
|
+
└── track("Push Notification Action Clicked")
|
|
1783
|
+
└── Navigate to button's deeplink
|
|
1784
|
+
```
|
|
153
1785
|
|
|
154
|
-
|
|
1786
|
+
---
|
|
155
1787
|
|
|
156
|
-
|
|
157
|
-
const status = await Zixflow.pushMessaging.showPromptForPushNotifications();
|
|
158
|
-
console.log('Push permission status:', status);
|
|
159
|
-
```
|
|
1788
|
+
### Fallback for Non-Zixflow Push
|
|
160
1789
|
|
|
161
|
-
|
|
1790
|
+
If a push notification doesn't contain `Zixflow-Delivery-ID` (sent from another source), the SDK automatically skips tracking.
|
|
162
1791
|
|
|
163
|
-
|
|
164
|
-
// Track when user receives a notification
|
|
165
|
-
Zixflow.pushMessaging.onMessageReceived((message) => {
|
|
166
|
-
console.log('Notification received:', message);
|
|
167
|
-
});
|
|
1792
|
+
For custom analytics on non-Zixflow pushes, use a different event name:
|
|
168
1793
|
|
|
169
|
-
|
|
170
|
-
Zixflow
|
|
171
|
-
|
|
1794
|
+
```typescript
|
|
1795
|
+
// Don't use reserved push event names for non-Zixflow pushes
|
|
1796
|
+
await Zixflow.track({
|
|
1797
|
+
name: 'External Push Received',
|
|
1798
|
+
properties: {
|
|
1799
|
+
'notification_id': message.messageId ?? '',
|
|
1800
|
+
'title': message.notification?.title ?? '',
|
|
1801
|
+
'body': message.notification?.body ?? '',
|
|
1802
|
+
'source': 'external',
|
|
1803
|
+
},
|
|
172
1804
|
});
|
|
173
1805
|
```
|
|
174
1806
|
|
|
175
|
-
|
|
1807
|
+
> **Important:** Push notification event names (`Push Notification Delivered`, `Push Notification Opened`, `Push Notification Action Clicked`) are reserved for Zixflow's delivery pipeline.
|
|
176
1808
|
|
|
177
1809
|
---
|
|
178
1810
|
|
|
179
|
-
|
|
1811
|
+
### Testing Push Tracking
|
|
180
1812
|
|
|
181
|
-
|
|
1813
|
+
#### Verify Tracking Events
|
|
182
1814
|
|
|
183
|
-
|
|
184
|
-
import { InAppMessageEvent, InAppMessageEventType } from 'zixflow-reactnative';
|
|
1815
|
+
Enable debug logging to see tracking events:
|
|
185
1816
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
break;
|
|
192
|
-
case InAppMessageEventType.MessageClicked:
|
|
193
|
-
console.log('In-app message clicked:', event.message);
|
|
194
|
-
break;
|
|
195
|
-
case InAppMessageEventType.MessageDismissed:
|
|
196
|
-
console.log('In-app message dismissed:', event.message);
|
|
197
|
-
break;
|
|
198
|
-
}
|
|
199
|
-
});
|
|
1817
|
+
```typescript
|
|
1818
|
+
const config: ZixflowConfig = {
|
|
1819
|
+
apiKey: 'YOUR_API_KEY',
|
|
1820
|
+
logLevel: 'debug',
|
|
1821
|
+
};
|
|
200
1822
|
|
|
201
|
-
|
|
202
|
-
Zixflow.inAppMessaging.dismissMessage();
|
|
1823
|
+
await Zixflow.setup(config);
|
|
203
1824
|
```
|
|
204
1825
|
|
|
205
|
-
|
|
1826
|
+
Look for these log messages:
|
|
1827
|
+
- `[Zixflow] Push Notification Delivered tracked`
|
|
1828
|
+
- `[Zixflow] Push Notification Opened tracked`
|
|
1829
|
+
- `[Zixflow] Push Notification Action Clicked tracked`
|
|
206
1830
|
|
|
207
|
-
|
|
1831
|
+
#### Check Campaign Analytics
|
|
208
1832
|
|
|
209
|
-
|
|
210
|
-
|
|
1833
|
+
1. Log into Zixflow dashboard
|
|
1834
|
+
2. Navigate to **Messaging** → **Push Notifications**
|
|
1835
|
+
3. View campaign metrics:
|
|
1836
|
+
- **Sent** - Total notifications sent
|
|
1837
|
+
- **Delivered** - Arrived on device
|
|
1838
|
+
- **Opened** - User tapped notification
|
|
1839
|
+
- **Clicked** - User tapped action button
|
|
211
1840
|
|
|
212
|
-
|
|
213
|
-
const messages = await inbox.getMessages();
|
|
1841
|
+
---
|
|
214
1842
|
|
|
215
|
-
|
|
216
|
-
await inbox.markMessageOpened(messageId);
|
|
1843
|
+
## Location Tracking
|
|
217
1844
|
|
|
218
|
-
|
|
219
|
-
await inbox.markMessageDeleted(messageId);
|
|
1845
|
+
Enable location tracking to send location-based messages:
|
|
220
1846
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
1847
|
+
```typescript
|
|
1848
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
1849
|
+
|
|
1850
|
+
// Location tracking is configured via the native SDK
|
|
1851
|
+
// Make sure to request location permissions in your app
|
|
1852
|
+
|
|
1853
|
+
// iOS: Add location permission keys to Info.plist
|
|
1854
|
+
// - NSLocationWhenInUseUsageDescription
|
|
1855
|
+
// - NSLocationAlwaysAndWhenInUseUsageDescription
|
|
1856
|
+
|
|
1857
|
+
// Android: Add location permissions to AndroidManifest.xml
|
|
1858
|
+
// - ACCESS_FINE_LOCATION
|
|
1859
|
+
// - ACCESS_COARSE_LOCATION
|
|
225
1860
|
```
|
|
226
1861
|
|
|
227
|
-
|
|
1862
|
+
Request location permissions using a library like `react-native-permissions`:
|
|
228
1863
|
|
|
229
|
-
|
|
1864
|
+
```typescript
|
|
1865
|
+
import { request, PERMISSIONS, RESULTS } from 'react-native-permissions';
|
|
1866
|
+
import { Platform } from 'react-native';
|
|
230
1867
|
|
|
231
|
-
|
|
1868
|
+
const requestLocationPermission = async () => {
|
|
1869
|
+
const permission = Platform.select({
|
|
1870
|
+
ios: PERMISSIONS.IOS.LOCATION_WHEN_IN_USE,
|
|
1871
|
+
android: PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION,
|
|
1872
|
+
});
|
|
232
1873
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
1874
|
+
if (!permission) return;
|
|
1875
|
+
|
|
1876
|
+
const result = await request(permission);
|
|
236
1877
|
|
|
237
|
-
|
|
1878
|
+
if (result === RESULTS.GRANTED) {
|
|
1879
|
+
console.log('Location permission granted');
|
|
1880
|
+
}
|
|
1881
|
+
};
|
|
1882
|
+
```
|
|
238
1883
|
|
|
239
|
-
|
|
1884
|
+
On iOS, a React Native 0.88+ `UIScene` host using the acknowledged handler must declare that ownership before React Native starts. This API is not used on Android or by AppDelegate-only hosts. Call this first in `scene(_:willConnectTo:options:)` so cold destinations wait for the JavaScript handler instead of entering the legacy `Linking` path:
|
|
240
1885
|
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
<string>We need your location to provide personalized experiences</string>
|
|
244
|
-
<key>NSLocationAlwaysUsageDescription</key>
|
|
245
|
-
<string>We need your location to provide personalized experiences</string>
|
|
1886
|
+
```swift
|
|
1887
|
+
NativeZixflow.configureAcknowledgedSceneDeepLinkRouting()
|
|
246
1888
|
```
|
|
247
1889
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
1890
|
+
Register the app's Zixflow deep-link handler before calling `Zixflow.initialize`. Return
|
|
1891
|
+
`true` after routing a URL. Return `false` to let the native SDK try the host AppDelegate, then pass
|
|
1892
|
+
a host-owned custom scheme to React Native `Linking` or open other URLs through the system. If your
|
|
1893
|
+
handler might decline an app-owned custom scheme, keep a `Linking` URL listener registered for that
|
|
1894
|
+
fallback. A thrown error, rejected promise, missing handler, or handler timeout follows the same
|
|
1895
|
+
native fallback. Cold URLs wait up to ten seconds for registration and are replayed once the handler
|
|
1896
|
+
is ready. After delivery, the handler has ten seconds to settle. If it takes longer, native fallback
|
|
1897
|
+
runs; a handler that later routes the URL can cause a second navigation because its acknowledgement
|
|
1898
|
+
cannot cancel the fallback.
|
|
1899
|
+
|
|
1900
|
+
```typescript
|
|
1901
|
+
const subscription = Zixflow.setDeepLinkHandler(async (url) => {
|
|
1902
|
+
if (!canRouteInApp(url)) {
|
|
1903
|
+
return false;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
await routeInApp(url);
|
|
1907
|
+
return true;
|
|
1908
|
+
});
|
|
1909
|
+
|
|
1910
|
+
Zixflow.initialize(config);
|
|
251
1911
|
```
|
|
252
1912
|
|
|
253
|
-
|
|
1913
|
+
Remove the returned subscription when the routing owner is torn down. If no replacement handler
|
|
1914
|
+
registers, later destinations wait for the readiness timeout and then use the native fallback.
|
|
254
1915
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
1916
|
+
The existing `Linking` path remains available for backward compatibility. Keep
|
|
1917
|
+
`NativeZixflow.configureSceneDeepLinkRouting()` in your SceneDelegate, then signal readiness
|
|
1918
|
+
after registering the listener:
|
|
1919
|
+
|
|
1920
|
+
```typescript
|
|
1921
|
+
const subscription = Linking.addEventListener('url', ({ url }) => {
|
|
1922
|
+
// Route the URL in your app.
|
|
1923
|
+
});
|
|
1924
|
+
Zixflow.setDeepLinkRoutingReady();
|
|
258
1925
|
```
|
|
259
1926
|
|
|
1927
|
+
Cold destinations wait up to ten seconds for this readiness signal. If it does not arrive, the SDK
|
|
1928
|
+
tries the host AppDelegate, passes a host-owned custom scheme to React Native `Linking`, or opens
|
|
1929
|
+
other URLs through the system.
|
|
1930
|
+
|
|
1931
|
+
This older path cannot acknowledge whether JavaScript handled the URL, so the SDK cannot safely fall back after it publishes a `Linking` event. Prefer `setDeepLinkHandler` for new UIScene integrations. Older React Native versions and AppDelegate-only hosts keep their existing deep-link integration.
|
|
1932
|
+
|
|
1933
|
+
Expo apps keep the `Linking` path. With config-plugin auto-initialization, register the app's
|
|
1934
|
+
`Linking` listener, then call `Zixflow.setDeepLinkRoutingReady()` after the router is ready. The
|
|
1935
|
+
Expo plugin configures its native scene lifecycle automatically, so Expo app code does not call a
|
|
1936
|
+
native configuration method. Apps using JavaScript initialization register the router before
|
|
1937
|
+
`Zixflow.initialize`; initialization marks Linking ready automatically.
|
|
1938
|
+
|
|
1939
|
+
This integration applies after the host has adopted React Native's UIScene lifecycle; the plugin does not replace React Native's root application lifecycle.
|
|
1940
|
+
|
|
1941
|
+
This release's compatibility scope is one simultaneous window scene. Multiple simultaneous React Native window scenes are not supported. React Native's URL notification is process-wide, so SDK-published destinations may reach every connected React Native instance rather than one selected window.
|
|
1942
|
+
|
|
260
1943
|
---
|
|
261
1944
|
|
|
262
|
-
##
|
|
1945
|
+
## Geofence Tracking
|
|
263
1946
|
|
|
264
|
-
|
|
1947
|
+
Geofence is an optional module, off by default. Enabling it also enables the Location module, which geofence depends on.
|
|
265
1948
|
|
|
266
|
-
|
|
1949
|
+
Enable it at build time:
|
|
267
1950
|
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
appVersion: '2.1.0',
|
|
272
|
-
customAttribute: 'value',
|
|
273
|
-
});
|
|
1951
|
+
```properties
|
|
1952
|
+
# android/gradle.properties
|
|
1953
|
+
zixflow_geofence_enabled=true
|
|
274
1954
|
```
|
|
275
1955
|
|
|
276
|
-
|
|
1956
|
+
```ruby
|
|
1957
|
+
# ios/Podfile
|
|
1958
|
+
pod 'zixflow-reactnative', :subspecs => ['geofence']
|
|
1959
|
+
```
|
|
277
1960
|
|
|
278
|
-
|
|
1961
|
+
Then opt in by passing a `geofence` config to `initialize`:
|
|
279
1962
|
|
|
280
|
-
```
|
|
281
|
-
Zixflow
|
|
1963
|
+
```typescript
|
|
1964
|
+
import { Zixflow, ZixflowGeofenceLocationMode } from 'zixflow-reactnative';
|
|
1965
|
+
|
|
1966
|
+
Zixflow.initialize({
|
|
1967
|
+
apiKey: 'YOUR_API_KEY',
|
|
1968
|
+
geofence: {
|
|
1969
|
+
// AUTOMATIC (default): the SDK acquires location itself on identify and app launch.
|
|
1970
|
+
// MANUAL: drive refreshes yourself via Zixflow.geofence.refreshFromCurrentLocation().
|
|
1971
|
+
locationMode: ZixflowGeofenceLocationMode.Automatic,
|
|
1972
|
+
},
|
|
1973
|
+
ios: {
|
|
1974
|
+
// Whether geofence transitions are delivered in real time on a background cold-wake.
|
|
1975
|
+
// Defaults to on when the geofence module is enabled.
|
|
1976
|
+
allowBackgroundDelivery: true,
|
|
1977
|
+
},
|
|
1978
|
+
});
|
|
282
1979
|
```
|
|
283
1980
|
|
|
284
|
-
|
|
1981
|
+
Request location permissions the same way as for [Location Tracking](#location-tracking) above — geofence monitoring requires them and the SDK never requests permission itself.
|
|
285
1982
|
|
|
286
|
-
|
|
1983
|
+
With `MANUAL` location mode, trigger a refresh after permission is granted:
|
|
287
1984
|
|
|
288
|
-
```
|
|
289
|
-
Zixflow.
|
|
1985
|
+
```typescript
|
|
1986
|
+
Zixflow.geofence.refreshFromCurrentLocation();
|
|
290
1987
|
```
|
|
291
1988
|
|
|
1989
|
+
This requests a one-shot location fix and refreshes the nearby geofence set from it. It no-ops if the geofence module is not enabled or a user has not been identified.
|
|
1990
|
+
|
|
292
1991
|
---
|
|
293
1992
|
|
|
294
|
-
##
|
|
1993
|
+
## 🔴 Live Activities
|
|
295
1994
|
|
|
296
|
-
|
|
1995
|
+
Enable the activity types you use under the `liveNotifications` key of your SDK config. On iOS, also add `NSSupportsLiveActivities` to your app's `Info.plist`, the `liveactivities` pod subspec, and a Widget Extension that renders the SDK's built-in templates. Without the `Info.plist` key, iOS refuses to start any activity:
|
|
297
1996
|
|
|
298
|
-
|
|
1997
|
+
```xml
|
|
1998
|
+
<key>NSSupportsLiveActivities</key>
|
|
1999
|
+
<true/>
|
|
2000
|
+
```
|
|
299
2001
|
|
|
300
|
-
|
|
301
|
-
pod 'zixflow-reactnative', :path => '../node_modules/zixflow-reactnative'
|
|
2002
|
+
**One manual step is required on iOS.** Forward every opened URL to the SDK from the host's active lifecycle, or taps on a Live Activity are not attributed. `NativeLiveActivities` comes from the wrapper pod, so import it. This only compiles once the `liveactivities` subspec is installed.
|
|
302
2003
|
|
|
303
|
-
|
|
304
|
-
pod 'zixflow-reactnative/fcm', :path => '../node_modules/zixflow-reactnative'
|
|
2004
|
+
For an AppDelegate host:
|
|
305
2005
|
|
|
306
|
-
|
|
307
|
-
|
|
2006
|
+
```swift
|
|
2007
|
+
import zixflow_reactnative
|
|
2008
|
+
import React
|
|
308
2009
|
|
|
309
|
-
|
|
310
|
-
|
|
2010
|
+
extension AppDelegate {
|
|
2011
|
+
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
|
2012
|
+
// Reports an `opened` metric and returns the deep link to route to. A non-Zixflow URL comes
|
|
2013
|
+
// back unchanged; `nil` means the activity carried no deep link, so there is nothing to open.
|
|
2014
|
+
guard let routableUrl = NativeLiveActivities.handleWidgetUrl(url) else { return true }
|
|
2015
|
+
return RCTLinkingManager.application(app, open: routableUrl, options: options)
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
311
2018
|
```
|
|
312
2019
|
|
|
313
|
-
|
|
2020
|
+
A React Native `AppDelegate` conforms to `UIApplicationDelegate` directly rather than subclassing, so this method is not an `override`, and the URL is passed on to `RCTLinkingManager` instead of `super`. See [the sample app's `AppDelegate.swift`](/example/ios/SampleApp/AppDelegate.swift) for this in context.
|
|
314
2021
|
|
|
315
|
-
|
|
2022
|
+
For a `UIScene` host, handle both lifecycle paths. At scene connection, pass the connection options through the wrapper so a cold Live Activity tap is attributed and its destination enters the acknowledged Zixflow router. Ordinary app links remain in React Native launch options for `Linking`:
|
|
316
2023
|
|
|
317
|
-
|
|
2024
|
+
```swift
|
|
2025
|
+
import zixflow_reactnative
|
|
318
2026
|
|
|
319
|
-
|
|
2027
|
+
reactNativeFactory?.startReactNative(
|
|
2028
|
+
withModuleName: "YourApp",
|
|
2029
|
+
in: window,
|
|
2030
|
+
launchOptions: NativeLiveActivities.reactNativeLaunchOptions(from: connectionOptions)
|
|
2031
|
+
)
|
|
2032
|
+
```
|
|
320
2033
|
|
|
321
|
-
-
|
|
322
|
-
- **Android Gradle Plugin**: 8.9.1 or higher
|
|
323
|
-
- **Gradle**: 8.11.1 or higher
|
|
324
|
-
- **minSdkVersion**: 21 or higher
|
|
325
|
-
- **Kotlin**: 1.9.25 or higher
|
|
2034
|
+
Then replace the ordinary React Native URL-forwarding body in the existing `SceneDelegate` for warm opens. This reports Live Activity taps, sends Zixflow destinations through the configured Zixflow router, and forwards ordinary app links to React Native `Linking`:
|
|
326
2035
|
|
|
327
|
-
|
|
2036
|
+
```swift
|
|
2037
|
+
import zixflow_reactnative
|
|
328
2038
|
|
|
329
|
-
|
|
2039
|
+
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
|
|
2040
|
+
for context in URLContexts {
|
|
2041
|
+
NativeLiveActivities.handleAndRouteWidgetUrl(context.url)
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
```
|
|
330
2045
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
2046
|
+
Do not also pass those URLs to `RCTLinkingManager`. The helper already routes them through the
|
|
2047
|
+
Zixflow bridge, and forwarding them again can deliver the same URL twice.
|
|
2048
|
+
|
|
2049
|
+
Android needs no equivalent native step.
|
|
2050
|
+
|
|
2051
|
+
For Expo Router, unwrap the URL once in the app's top-level `app/+native-intent.tsx` file:
|
|
2052
|
+
|
|
2053
|
+
```typescript
|
|
2054
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
2055
|
+
|
|
2056
|
+
export async function redirectSystemPath({ path }: { path: string }) {
|
|
2057
|
+
return Zixflow.liveActivities.handleWidgetUrl(path);
|
|
342
2058
|
}
|
|
343
2059
|
```
|
|
344
2060
|
|
|
345
|
-
|
|
2061
|
+
This reports the opened event, returns the customer's destination, preserves ordinary URLs, and
|
|
2062
|
+
returns `null` for a Zixflow tracking URL without a destination. Do not also call the helper
|
|
2063
|
+
from a `Linking` listener because processing the same tracking URL twice reports two opened events.
|
|
2064
|
+
Expo apps without Expo Router can apply the helper once in their central `Linking` initial-URL and
|
|
2065
|
+
subscription pipeline.
|
|
346
2066
|
|
|
347
|
-
|
|
348
|
-
|
|
2067
|
+
---
|
|
2068
|
+
|
|
2069
|
+
## Notification Inbox
|
|
2070
|
+
|
|
2071
|
+
The Visual Notification Inbox renders received messages in a list the SDK owns the design of. Two components are available:
|
|
2072
|
+
|
|
2073
|
+
- `NotificationInboxBellView` — a bell icon with an unread badge. Tapping it opens the SDK's own inbox panel; the host presents nothing.
|
|
2074
|
+
- `NotificationInboxView` — the message list itself, embeddable directly in your own screen with no surrounding chrome (no bell, no scrim).
|
|
2075
|
+
|
|
2076
|
+
```tsx
|
|
2077
|
+
import { NotificationInboxBellView, NotificationInboxView } from 'zixflow-reactnative';
|
|
2078
|
+
|
|
2079
|
+
// Place the bell wherever your layout calls for it. Give it a fixed size for
|
|
2080
|
+
// predictable layout — at least 88 points/dp square.
|
|
2081
|
+
<NotificationInboxBellView
|
|
2082
|
+
style={{ width: 88, height: 88 }}
|
|
2083
|
+
onTap={() => analytics.track('inbox_bell_tapped')}
|
|
2084
|
+
/>
|
|
2085
|
+
|
|
2086
|
+
// Or embed the message list directly in a screen of your own.
|
|
2087
|
+
<NotificationInboxView style={{ flex: 1 }} />
|
|
349
2088
|
```
|
|
350
2089
|
|
|
351
|
-
|
|
2090
|
+
`onTap` on `NotificationInboxBellView` is observational only — the SDK opens the inbox panel itself. Message actions inside `NotificationInboxView` are reported through the global inbox event listener below, not a per-message callback.
|
|
352
2091
|
|
|
353
|
-
|
|
354
|
-
android {
|
|
355
|
-
// ... other config
|
|
356
|
-
}
|
|
2092
|
+
Remote branding styles the bell and list themselves (colors, icon); the host still owns layout and placement via `style`.
|
|
357
2093
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
2094
|
+
The SDK ships no text of its own in the visual inbox, so accessibility labels are the one place a string is still needed. Every label is optional — an omitted label leaves that element unlabeled rather than falling back to English:
|
|
2095
|
+
|
|
2096
|
+
```typescript
|
|
2097
|
+
Zixflow.initialize({
|
|
2098
|
+
apiKey: 'YOUR_API_KEY',
|
|
2099
|
+
inApp: {
|
|
2100
|
+
notificationInboxAccessibilityLabels: {
|
|
2101
|
+
bell: t('inbox.bell'),
|
|
2102
|
+
bellWithUnreadCount: t('inbox.unread'), // e.g. "{count} unread notifications"
|
|
2103
|
+
loadingIndicator: t('inbox.loading'),
|
|
2104
|
+
emptyState: t('inbox.empty'),
|
|
2105
|
+
},
|
|
2106
|
+
},
|
|
2107
|
+
});
|
|
363
2108
|
```
|
|
364
2109
|
|
|
365
|
-
|
|
2110
|
+
Register a listener to observe inbox events (shown, opened, dismissed, and non-dismiss actions taken on a message):
|
|
366
2111
|
|
|
367
|
-
|
|
2112
|
+
```typescript
|
|
2113
|
+
import { InboxEventType } from 'zixflow-reactnative';
|
|
368
2114
|
|
|
369
|
-
|
|
370
|
-
|
|
2115
|
+
const subscription = Zixflow.inAppMessaging.registerInboxEventListener((event) => {
|
|
2116
|
+
switch (event.eventType) {
|
|
2117
|
+
case InboxEventType.messageActionTaken:
|
|
2118
|
+
// event.actionName / event.actionValue are only present for this event
|
|
2119
|
+
console.log('Inbox action taken:', event.actionName, event.actionValue);
|
|
2120
|
+
break;
|
|
2121
|
+
case InboxEventType.messageShown:
|
|
2122
|
+
case InboxEventType.messageOpened:
|
|
2123
|
+
case InboxEventType.messageDismissed:
|
|
2124
|
+
console.log(event.eventType, event.message);
|
|
2125
|
+
break;
|
|
2126
|
+
}
|
|
2127
|
+
});
|
|
2128
|
+
|
|
2129
|
+
// Remove listener
|
|
2130
|
+
subscription.remove();
|
|
371
2131
|
```
|
|
372
2132
|
|
|
373
|
-
|
|
2133
|
+
You can also fetch and manage inbox messages directly through `Zixflow.inAppMessaging.inbox()`:
|
|
374
2134
|
|
|
375
|
-
|
|
2135
|
+
```typescript
|
|
2136
|
+
const inbox = Zixflow.inAppMessaging.inbox();
|
|
376
2137
|
|
|
377
|
-
|
|
2138
|
+
const messages = await inbox.getMessages();
|
|
378
2139
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
| `migrationSiteId` | `string` | ❌ | Site ID for migration from older SDKs |
|
|
383
|
-
| `logLevel` | `ZixflowLogLevel` | ❌ | Logging verbosity (default: error) |
|
|
384
|
-
| `trackApplicationLifecycleEvents` | `boolean` | ❌ | Auto-track app lifecycle events |
|
|
385
|
-
| `inApp` | `object` | ❌ | In-app messaging configuration |
|
|
386
|
-
| `push` | `object` | ❌ | Push notification configuration |
|
|
2140
|
+
inbox.markMessageOpened(message);
|
|
2141
|
+
inbox.markMessageDeleted(message);
|
|
2142
|
+
```
|
|
387
2143
|
|
|
388
2144
|
---
|
|
389
2145
|
|
|
390
|
-
##
|
|
2146
|
+
## Advanced Configuration
|
|
2147
|
+
|
|
2148
|
+
### Log Levels
|
|
391
2149
|
|
|
392
|
-
|
|
2150
|
+
Control SDK logging for debugging:
|
|
393
2151
|
|
|
394
|
-
```
|
|
395
|
-
import
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
InAppMessage,
|
|
404
|
-
InboxMessage,
|
|
405
|
-
} from 'zixflow-reactnative';
|
|
2152
|
+
```typescript
|
|
2153
|
+
import { Zixflow, ZixflowLogLevel } from 'zixflow-reactnative';
|
|
2154
|
+
|
|
2155
|
+
const config = {
|
|
2156
|
+
apiKey: 'YOUR_API_KEY',
|
|
2157
|
+
logLevel: ZixflowLogLevel.debug, // debug, info, error, none
|
|
2158
|
+
};
|
|
2159
|
+
|
|
2160
|
+
Zixflow.initialize(config);
|
|
406
2161
|
```
|
|
407
2162
|
|
|
408
|
-
|
|
2163
|
+
**Log levels**:
|
|
2164
|
+
- `ZixflowLogLevel.debug` - Verbose logging (development)
|
|
2165
|
+
- `ZixflowLogLevel.info` - Informational messages
|
|
2166
|
+
- `ZixflowLogLevel.error` - Errors only
|
|
2167
|
+
- `ZixflowLogLevel.none` - No logging (production)
|
|
2168
|
+
|
|
2169
|
+
### Auto-Tracking Options
|
|
409
2170
|
|
|
410
|
-
|
|
2171
|
+
Configure automatic tracking features:
|
|
411
2172
|
|
|
412
|
-
|
|
2173
|
+
```typescript
|
|
2174
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
413
2175
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
Zixflow.inAppMessaging.registerEventsListener(listener);
|
|
2176
|
+
const config = {
|
|
2177
|
+
apiKey: 'YOUR_API_KEY',
|
|
417
2178
|
|
|
418
|
-
//
|
|
419
|
-
|
|
2179
|
+
// Automatically track device attributes (OS, model, app version)
|
|
2180
|
+
autoTrackDeviceAttributes: true,
|
|
420
2181
|
|
|
421
|
-
//
|
|
422
|
-
|
|
2182
|
+
// Automatically track app lifecycle events (open, close, background)
|
|
2183
|
+
trackApplicationLifecycleEvents: true,
|
|
2184
|
+
|
|
2185
|
+
// Automatically track screens (requires integration)
|
|
2186
|
+
autoTrackScreenViews: true,
|
|
2187
|
+
};
|
|
2188
|
+
|
|
2189
|
+
Zixflow.initialize(config);
|
|
423
2190
|
```
|
|
424
2191
|
|
|
425
|
-
|
|
2192
|
+
### Custom API Hosts
|
|
2193
|
+
|
|
2194
|
+
Override API endpoints for testing or custom infrastructure:
|
|
2195
|
+
|
|
2196
|
+
```typescript
|
|
2197
|
+
import { Zixflow } from 'zixflow-reactnative';
|
|
2198
|
+
|
|
2199
|
+
const config = {
|
|
2200
|
+
apiKey: 'YOUR_API_KEY',
|
|
2201
|
+
|
|
2202
|
+
// Custom tracking API host
|
|
2203
|
+
trackingApiUrl: 'https://custom-track.zixflow.com',
|
|
2204
|
+
|
|
2205
|
+
// Custom in-app messaging API host
|
|
2206
|
+
inApp: {
|
|
2207
|
+
// Override in-app messaging API endpoint if needed
|
|
2208
|
+
},
|
|
2209
|
+
};
|
|
2210
|
+
|
|
2211
|
+
Zixflow.initialize(config);
|
|
2212
|
+
```
|
|
426
2213
|
|
|
427
2214
|
---
|
|
428
2215
|
|
|
429
|
-
## Example
|
|
2216
|
+
## Example Applications
|
|
2217
|
+
|
|
2218
|
+
The Zixflow React Native SDK includes example applications demonstrating best practices:
|
|
2219
|
+
|
|
2220
|
+
### Sample App (APNs)
|
|
430
2221
|
|
|
431
|
-
|
|
2222
|
+
Location: `example/`
|
|
432
2223
|
|
|
433
|
-
|
|
434
|
-
- User identification
|
|
2224
|
+
**Features**:
|
|
2225
|
+
- User identification and logout
|
|
435
2226
|
- Event tracking
|
|
436
|
-
-
|
|
437
|
-
-
|
|
438
|
-
-
|
|
439
|
-
-
|
|
2227
|
+
- Screen tracking with React Navigation
|
|
2228
|
+
- Push notifications with APNs
|
|
2229
|
+
- In-app messaging with event listeners
|
|
2230
|
+
- Profile and device attributes
|
|
440
2231
|
|
|
441
|
-
|
|
2232
|
+
**Running the example**:
|
|
442
2233
|
|
|
443
2234
|
```bash
|
|
2235
|
+
# Install dependencies
|
|
444
2236
|
cd example
|
|
445
2237
|
npm install
|
|
446
2238
|
|
|
@@ -454,64 +2246,174 @@ npx react-native run-android
|
|
|
454
2246
|
|
|
455
2247
|
---
|
|
456
2248
|
|
|
457
|
-
##
|
|
2249
|
+
## API Reference
|
|
458
2250
|
|
|
459
|
-
###
|
|
2251
|
+
### Core SDK
|
|
460
2252
|
|
|
461
|
-
|
|
462
|
-
- Run `pod install` in your iOS directory
|
|
463
|
-
- Make sure you're using the correct pod name: `zixflow-reactnative`
|
|
2253
|
+
#### `Zixflow.initialize(config: ZixflowConfig): void`
|
|
464
2254
|
|
|
465
|
-
|
|
466
|
-
- Verify your Firebase configuration (Android) or APNs certificates (iOS)
|
|
467
|
-
- Check that you've requested push permissions
|
|
468
|
-
- Ensure you've registered the device token
|
|
2255
|
+
Initialize the SDK with configuration.
|
|
469
2256
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
2257
|
+
```typescript
|
|
2258
|
+
Zixflow.initialize({
|
|
2259
|
+
apiKey: 'YOUR_API_KEY',
|
|
2260
|
+
});
|
|
2261
|
+
```
|
|
474
2262
|
|
|
475
|
-
|
|
2263
|
+
#### `Zixflow.identify(params: { userId: string; traits?: object }): void`
|
|
476
2264
|
|
|
477
|
-
|
|
2265
|
+
Identify a user with optional traits.
|
|
478
2266
|
|
|
479
|
-
|
|
2267
|
+
```typescript
|
|
2268
|
+
Zixflow.identify({
|
|
2269
|
+
userId: 'user-123',
|
|
2270
|
+
traits: { email: 'user@example.com' }
|
|
2271
|
+
});
|
|
2272
|
+
```
|
|
480
2273
|
|
|
481
|
-
|
|
482
|
-
- iOS 13+
|
|
483
|
-
- Android API 21+ (Android 5.0 Lollipop)
|
|
484
|
-
- Swift 5.3+
|
|
485
|
-
- Kotlin 1.8+
|
|
2274
|
+
#### `Zixflow.clearIdentify(): void`
|
|
486
2275
|
|
|
487
|
-
|
|
2276
|
+
Clear the current user's identity.
|
|
488
2277
|
|
|
489
|
-
|
|
2278
|
+
```typescript
|
|
2279
|
+
Zixflow.clearIdentify();
|
|
2280
|
+
```
|
|
490
2281
|
|
|
491
|
-
|
|
2282
|
+
#### `Zixflow.track(name: string, properties?: object | Map<string, any>): void`
|
|
492
2283
|
|
|
493
|
-
|
|
494
|
-
2. Follow our [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
495
|
-
3. Check out [DEVELOPMENT.md](DEVELOPMENT.md) for development guidelines
|
|
496
|
-
4. Open issues or pull requests on [GitHub](https://github.com/zixflow/zixflow-reactnative)
|
|
2284
|
+
Track a custom event.
|
|
497
2285
|
|
|
498
|
-
|
|
2286
|
+
```typescript
|
|
2287
|
+
Zixflow.track('button_clicked', { button_name: 'signup' });
|
|
2288
|
+
```
|
|
499
2289
|
|
|
500
|
-
|
|
2290
|
+
#### `Zixflow.screen(name: string, properties?: object): void`
|
|
501
2291
|
|
|
502
|
-
|
|
503
|
-
- 💬 [Community Forum](https://community.zixflow.com)
|
|
504
|
-
- 🐛 [Issue Tracker](https://github.com/zixflow/zixflow-reactnative/issues)
|
|
505
|
-
- 📧 Email: apps@zixflow.com
|
|
2292
|
+
Track a screen view.
|
|
506
2293
|
|
|
507
|
-
|
|
2294
|
+
```typescript
|
|
2295
|
+
Zixflow.screen('HomeScreen');
|
|
2296
|
+
```
|
|
508
2297
|
|
|
509
|
-
|
|
2298
|
+
#### `Zixflow.setProfileAttributes(attributes: object): void`
|
|
510
2299
|
|
|
511
|
-
|
|
2300
|
+
Set attributes on the user profile.
|
|
512
2301
|
|
|
513
|
-
|
|
2302
|
+
```typescript
|
|
2303
|
+
Zixflow.setProfileAttributes({ plan: 'premium' });
|
|
2304
|
+
```
|
|
2305
|
+
|
|
2306
|
+
#### `Zixflow.setDeviceAttributes(attributes: object): void`
|
|
2307
|
+
|
|
2308
|
+
Set attributes on the current device.
|
|
2309
|
+
|
|
2310
|
+
```typescript
|
|
2311
|
+
Zixflow.setDeviceAttributes({ app_version: '2.0.0' });
|
|
2312
|
+
```
|
|
2313
|
+
|
|
2314
|
+
### Push Messaging
|
|
2315
|
+
|
|
2316
|
+
#### `Zixflow.pushMessaging.showPromptForPushNotifications(options): Promise<ZixflowPushPermissionStatus>`
|
|
2317
|
+
|
|
2318
|
+
Request push notification permission.
|
|
2319
|
+
|
|
2320
|
+
```typescript
|
|
2321
|
+
const status = await Zixflow.pushMessaging.showPromptForPushNotifications({
|
|
2322
|
+
ios: { sound: true, badge: true }
|
|
2323
|
+
});
|
|
2324
|
+
```
|
|
2325
|
+
|
|
2326
|
+
### In-App Messaging
|
|
2327
|
+
|
|
2328
|
+
#### `Zixflow.inAppMessaging.registerEventsListener(callback): EventSubscription`
|
|
2329
|
+
|
|
2330
|
+
Register a listener for in-app message events.
|
|
2331
|
+
|
|
2332
|
+
```typescript
|
|
2333
|
+
const listener = Zixflow.inAppMessaging.registerEventsListener((event) => {
|
|
2334
|
+
console.log('In-app event:', event);
|
|
2335
|
+
});
|
|
2336
|
+
|
|
2337
|
+
// Remove listener
|
|
2338
|
+
listener.remove();
|
|
2339
|
+
```
|
|
2340
|
+
|
|
2341
|
+
#### `Zixflow.inAppMessaging.dismissMessage(): void`
|
|
2342
|
+
|
|
2343
|
+
Dismiss the currently displayed in-app message.
|
|
2344
|
+
|
|
2345
|
+
```typescript
|
|
2346
|
+
Zixflow.inAppMessaging.dismissMessage();
|
|
2347
|
+
```
|
|
2348
|
+
|
|
2349
|
+
### Types
|
|
2350
|
+
|
|
2351
|
+
#### `ZixflowConfig`
|
|
2352
|
+
|
|
2353
|
+
Configuration object for SDK initialization.
|
|
2354
|
+
|
|
2355
|
+
```typescript
|
|
2356
|
+
interface ZixflowConfig {
|
|
2357
|
+
apiKey: string;
|
|
2358
|
+
logLevel?: ZixflowLogLevel;
|
|
2359
|
+
inApp?: {
|
|
2360
|
+
};
|
|
2361
|
+
autoTrackDeviceAttributes?: boolean;
|
|
2362
|
+
trackApplicationLifecycleEvents?: boolean;
|
|
2363
|
+
autoTrackScreenViews?: boolean;
|
|
2364
|
+
trackingApiUrl?: string;
|
|
2365
|
+
}
|
|
2366
|
+
```
|
|
2367
|
+
|
|
2368
|
+
#### `ZixflowLogLevel`
|
|
2369
|
+
|
|
2370
|
+
Log level enum.
|
|
2371
|
+
|
|
2372
|
+
```typescript
|
|
2373
|
+
enum ZixflowLogLevel {
|
|
2374
|
+
none = 'none',
|
|
2375
|
+
error = 'error',
|
|
2376
|
+
info = 'info',
|
|
2377
|
+
debug = 'debug'
|
|
2378
|
+
}
|
|
2379
|
+
```
|
|
2380
|
+
|
|
2381
|
+
#### `ZixflowPushPermissionStatus`
|
|
2382
|
+
|
|
2383
|
+
Push notification permission status.
|
|
2384
|
+
|
|
2385
|
+
```typescript
|
|
2386
|
+
enum ZixflowPushPermissionStatus {
|
|
2387
|
+
Granted = 'granted',
|
|
2388
|
+
Denied = 'denied',
|
|
2389
|
+
NotDetermined = 'notDetermined'
|
|
2390
|
+
}
|
|
2391
|
+
```
|
|
2392
|
+
|
|
2393
|
+
#### `InAppMessageEventType`
|
|
514
2394
|
|
|
515
|
-
|
|
2395
|
+
In-app message event types.
|
|
2396
|
+
|
|
2397
|
+
```typescript
|
|
2398
|
+
enum InAppMessageEventType {
|
|
2399
|
+
messageShown = 'messageShown',
|
|
2400
|
+
messageDismissed = 'messageDismissed',
|
|
2401
|
+
messageActionTaken = 'messageActionTaken',
|
|
2402
|
+
errorWithMessage = 'errorWithMessage'
|
|
2403
|
+
}
|
|
2404
|
+
```
|
|
2405
|
+
|
|
2406
|
+
#### `InAppMessageEvent`
|
|
2407
|
+
|
|
2408
|
+
In-app message event object.
|
|
2409
|
+
|
|
2410
|
+
```typescript
|
|
2411
|
+
interface InAppMessageEvent {
|
|
2412
|
+
eventType: InAppMessageEventType;
|
|
2413
|
+
deliveryId?: string;
|
|
2414
|
+
messageId?: string;
|
|
2415
|
+
actionName?: string;
|
|
2416
|
+
actionValue?: string;
|
|
2417
|
+
}
|
|
2418
|
+
```
|
|
516
2419
|
|
|
517
|
-
This SDK is based on the Zixflow React Native SDK and maintains compatibility with the Zixflow platform while providing Zixflow-specific optimizations and branding.
|