zixflow-reactnative 1.1.3 → 1.1.5
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 +631 -261
- 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 +41 -5
- 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 -0
- 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/messaginginapp/NativeMessagingInAppModule.kt +150 -0
- 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 +15 -2
- 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 +91 -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/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 +13 -1
- 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 +76 -1
- package/lib/commonjs/utils/param-validation.js.map +1 -1
- package/lib/commonjs/zixflow-cdp.js +99 -1
- 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/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 +13 -0
- 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 +76 -1
- package/lib/module/utils/param-validation.js.map +1 -1
- package/lib/module/zixflow-cdp.js +99 -1
- 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/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 +53 -1
- 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/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 +53 -1
- 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/package.json +11 -2
- 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 +54 -1
- 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 +103 -0
- package/src/zixflow-cdp.ts +117 -1
- package/src/zixflow-geofence.ts +56 -0
- package/src/zixflow-inapp.ts +125 -2
- package/src/zixflow-liveactivities.ts +86 -0
- package/zixflow-reactnative-richpush.podspec +4 -1
- package/zixflow-reactnative.podspec +26 -0
|
@@ -2,14 +2,102 @@ import ZixflowAnalytics
|
|
|
2
2
|
import ZixflowDataPipelines
|
|
3
3
|
import ZixflowInternalCommon
|
|
4
4
|
import ZixflowMessagingInApp
|
|
5
|
+
import Foundation
|
|
5
6
|
|
|
6
7
|
@objc(NativeZixflow)
|
|
7
8
|
public class NativeZixflow: NSObject {
|
|
8
9
|
private let logger: ZixflowInternalCommon.Logger = DIGraphShared.shared.logger
|
|
10
|
+
private weak var objcEventEmitter: AnyObject?
|
|
11
|
+
private var deepLinkHandlerToken: UUID?
|
|
9
12
|
/// Checks whether the Zixflow SDK has been initialized.
|
|
10
13
|
/// Returns `true` if the SDK has been successfully initialized, `false` otherwise.
|
|
11
14
|
private var isInitialized: Bool { Zixflow.shared.implementation != nil }
|
|
12
15
|
|
|
16
|
+
/// Installs React Native scene deep-link routing before the JavaScript bridge starts.
|
|
17
|
+
/// Call this at the start of `scene(_:willConnectTo:options:)` in a UIScene host.
|
|
18
|
+
@objc
|
|
19
|
+
public static func configureSceneDeepLinkRouting() {
|
|
20
|
+
// A natively initialized host owns its callback. Scene connection happens after
|
|
21
|
+
// application launch, so never replace a callback that native initialization installed.
|
|
22
|
+
guard Zixflow.shared.implementation == nil else { return }
|
|
23
|
+
ZixflowReactNativeDeepLinkRouter.install()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Installs scene deep-link routing for the acknowledged JavaScript handler before the React
|
|
27
|
+
/// Native bridge starts. Call this at the start of `scene(_:willConnectTo:options:)` when the
|
|
28
|
+
/// host uses `Zixflow.setDeepLinkHandler`.
|
|
29
|
+
@objc
|
|
30
|
+
public static func configureAcknowledgedSceneDeepLinkRouting() {
|
|
31
|
+
// The cold scene connection happens before JavaScript can register its handler. Record the
|
|
32
|
+
// host's ownership choice now so initialization cannot drain a buffered URL into Linking.
|
|
33
|
+
guard Zixflow.shared.implementation == nil else { return }
|
|
34
|
+
ZixflowReactNativeDeepLinkRouter.installAcknowledgedHandler()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Installs deep-link routing for an Expo-owned scene lifecycle.
|
|
38
|
+
///
|
|
39
|
+
/// Expo forwards scene URLs into React Native Linking itself, including on React Native
|
|
40
|
+
/// versions that do not expose the scene Linking selector used by the generic integration.
|
|
41
|
+
@objc
|
|
42
|
+
public static func configureExpoSceneDeepLinkRouting() {
|
|
43
|
+
guard Zixflow.shared.implementation == nil else { return }
|
|
44
|
+
// Native initialization preserves an existing callback when its config does not provide one.
|
|
45
|
+
ZixflowReactNativeDeepLinkRouter.installForExpoSceneLifecycle()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Marks the React Native Linking listener as ready for buffered scene URLs.
|
|
49
|
+
@objc
|
|
50
|
+
func setDeepLinkRoutingReady() {
|
|
51
|
+
ZixflowReactNativeDeepLinkRouter.markReactNativeReady()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@objc
|
|
55
|
+
public func setEventEmitter(_ emitter: AnyObject) {
|
|
56
|
+
objcEventEmitter = emitter
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@objc
|
|
60
|
+
func registerDeepLinkHandler() {
|
|
61
|
+
guard objcEventEmitter != nil else {
|
|
62
|
+
logger.error("Zixflow could not register the React Native deep-link handler")
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
deepLinkHandlerToken = ZixflowReactNativeDeepLinkRouter.registerHandler { [weak self] id, url in
|
|
67
|
+
self?.sendDeepLink(id: id, url: url)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@objc
|
|
72
|
+
func unregisterDeepLinkHandler() {
|
|
73
|
+
guard let token = deepLinkHandlerToken else { return }
|
|
74
|
+
ZixflowReactNativeDeepLinkRouter.unregisterHandler(token)
|
|
75
|
+
deepLinkHandlerToken = nil
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@objc
|
|
79
|
+
func acknowledgeDeepLink(_ id: String, handled: Bool) {
|
|
80
|
+
ZixflowReactNativeDeepLinkRouter.acknowledge(id, handled: handled)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@objc
|
|
84
|
+
func invalidate() {
|
|
85
|
+
unregisterDeepLinkHandler()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private func sendDeepLink(id: String, url: String) {
|
|
89
|
+
guard let emitter = objcEventEmitter else { return }
|
|
90
|
+
|
|
91
|
+
// Codegen derives this selector from `onDeepLinkReceived` in NativeZixflow.ts. Keep it
|
|
92
|
+
// in sync with that spec property and the forwarding override in NativeZixflow.mm.
|
|
93
|
+
let selector = Selector(("emitOnDeepLinkReceived:"))
|
|
94
|
+
guard emitter.responds(to: selector) else {
|
|
95
|
+
logger.error("Zixflow could not emit a React Native deep-link event")
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
_ = emitter.perform(selector, with: ["id": id, "url": url] as NSDictionary)
|
|
99
|
+
}
|
|
100
|
+
|
|
13
101
|
/// Ensures that the Zixflow SDK is initialized before performing operations.
|
|
14
102
|
/// Logs an error and returns false if the SDK is not initialized.
|
|
15
103
|
private func ensureInitialized() -> Bool {
|
|
@@ -30,6 +118,7 @@ public class NativeZixflow: NSObject {
|
|
|
30
118
|
// Skip initialization if already initialized
|
|
31
119
|
if isInitialized {
|
|
32
120
|
logger.info("Zixflow SDK is already initialized. Skipping initialization.")
|
|
121
|
+
ZixflowReactNativeDeepLinkRouter.markReactNativeReady()
|
|
33
122
|
resolve(true)
|
|
34
123
|
return
|
|
35
124
|
}
|
|
@@ -48,12 +137,56 @@ public class NativeZixflow: NSObject {
|
|
|
48
137
|
|
|
49
138
|
let sdkConfigBuilder = try SDKConfigBuilder.create(from: config)
|
|
50
139
|
|
|
140
|
+
let shouldInstallSceneRouter =
|
|
141
|
+
ZixflowReactNativeDeepLinkRouter.isSceneLifecycleEnabled ||
|
|
142
|
+
(packageSource == "Expo" && ZixflowReactNativeDeepLinkRouter.isExpoSceneLifecycleEnabled)
|
|
143
|
+
if shouldInstallSceneRouter {
|
|
144
|
+
_ = sdkConfigBuilder.deepLinkCallback { url in
|
|
145
|
+
ZixflowReactNativeDeepLinkRouter.accept(url)
|
|
146
|
+
return true // React Native Linking cannot report handling; JavaScript owns fallback.
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
51
150
|
#if ZIXFLOW_LOCATION_ENABLED
|
|
52
|
-
|
|
151
|
+
// Geofence implies location: register the location module whenever location
|
|
152
|
+
// config is provided or geofence is enabled, since geofence relies on the
|
|
153
|
+
// location module's fixes.
|
|
154
|
+
#if ZIXFLOW_GEOFENCE_ENABLED
|
|
155
|
+
// Match NativeGeofence.module(from:): only a dictionary counts as configured,
|
|
156
|
+
// so a non-map value (e.g. null bridged from JS) doesn't imply location.
|
|
157
|
+
let geofenceConfigured = config["geofence"] as? [String: Any] != nil
|
|
158
|
+
#else
|
|
159
|
+
let geofenceConfigured = false
|
|
160
|
+
#endif
|
|
161
|
+
if let locationModule = NativeLocation.module(from: config, geofenceEnabled: geofenceConfigured) {
|
|
53
162
|
_ = sdkConfigBuilder.addModule(locationModule)
|
|
54
163
|
}
|
|
55
164
|
#endif
|
|
56
165
|
|
|
166
|
+
#if ZIXFLOW_GEOFENCE_ENABLED
|
|
167
|
+
// Geofence runs automatically once registered; relies on the location module above.
|
|
168
|
+
if let geofenceModule = NativeGeofence.module(from: config) {
|
|
169
|
+
_ = sdkConfigBuilder.addModule(geofenceModule)
|
|
170
|
+
}
|
|
171
|
+
#endif
|
|
172
|
+
|
|
173
|
+
#if ZIXFLOW_LIVEACTIVITIES_ENABLED
|
|
174
|
+
if let liveActivitiesModule = NativeLiveActivities.module(from: config) {
|
|
175
|
+
_ = sdkConfigBuilder.addModule(liveActivitiesModule)
|
|
176
|
+
}
|
|
177
|
+
#endif
|
|
178
|
+
|
|
179
|
+
// Customer value wins; default on when the geofence module is added, off otherwise.
|
|
180
|
+
#if ZIXFLOW_GEOFENCE_ENABLED
|
|
181
|
+
// Match NativeGeofence.module(from:): only a dictionary adds the module, so
|
|
182
|
+
// background delivery shouldn't default on for a non-map geofence value.
|
|
183
|
+
let geofenceAdded = config["geofence"] as? [String: Any] != nil
|
|
184
|
+
#else
|
|
185
|
+
let geofenceAdded = false
|
|
186
|
+
#endif
|
|
187
|
+
let allowBackgroundDelivery = (config["ios"] as? [String: Any])?["allowBackgroundDelivery"] as? Bool
|
|
188
|
+
_ = sdkConfigBuilder.allowBackgroundDelivery(allowBackgroundDelivery ?? geofenceAdded)
|
|
189
|
+
|
|
57
190
|
let builtConfig = sdkConfigBuilder.build()
|
|
58
191
|
|
|
59
192
|
// Only Zixflow.initialize must run on the main thread (e.g. for Location module).
|
|
@@ -77,6 +210,7 @@ public class NativeZixflow: NSObject {
|
|
|
77
210
|
self.logger.debug(
|
|
78
211
|
"Zixflow SDK (\(packageSource ?? "") \(packageVersion ?? "")) initialized with config: \(config)"
|
|
79
212
|
)
|
|
213
|
+
ZixflowReactNativeDeepLinkRouter.markReactNativeReady()
|
|
80
214
|
resolve(true)
|
|
81
215
|
}
|
|
82
216
|
} catch {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
struct ZixflowReactNativeDeepLinkRequestStore {
|
|
4
|
+
enum Acceptance: Equatable {
|
|
5
|
+
case buffered(UUID)
|
|
6
|
+
case linking(URL)
|
|
7
|
+
case handler(UUID, URL)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
enum Resolution: Equatable {
|
|
11
|
+
case handled
|
|
12
|
+
case fallback(URL)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private enum DeliveryMode: Equatable {
|
|
16
|
+
case unavailable
|
|
17
|
+
case linking
|
|
18
|
+
case handler
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private enum RequestState: Equatable {
|
|
22
|
+
case buffered
|
|
23
|
+
case awaitingAcknowledgement
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private struct Request {
|
|
27
|
+
let id: UUID
|
|
28
|
+
let url: URL
|
|
29
|
+
var state: RequestState
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private var isLinkingReady = false
|
|
33
|
+
private var requiresHandler = false
|
|
34
|
+
private var hasHandler = false
|
|
35
|
+
private var requests: [Request] = []
|
|
36
|
+
|
|
37
|
+
private var deliveryMode: DeliveryMode {
|
|
38
|
+
if hasHandler {
|
|
39
|
+
return .handler
|
|
40
|
+
}
|
|
41
|
+
if !requiresHandler, isLinkingReady {
|
|
42
|
+
return .linking
|
|
43
|
+
}
|
|
44
|
+
return .unavailable
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
var requiresAcknowledgedHandler: Bool {
|
|
48
|
+
requiresHandler
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
var canDeliverWithLinking: Bool {
|
|
52
|
+
isLinkingReady
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
mutating func requireAcknowledgedHandler() {
|
|
56
|
+
requiresHandler = true
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
mutating func accept(_ url: URL) -> Acceptance {
|
|
60
|
+
switch deliveryMode {
|
|
61
|
+
case .unavailable:
|
|
62
|
+
let request = Request(id: UUID(), url: url, state: .buffered)
|
|
63
|
+
requests.append(request)
|
|
64
|
+
return .buffered(request.id)
|
|
65
|
+
case .linking:
|
|
66
|
+
return .linking(url)
|
|
67
|
+
case .handler:
|
|
68
|
+
let request = Request(id: UUID(), url: url, state: .awaitingAcknowledgement)
|
|
69
|
+
requests.append(request)
|
|
70
|
+
return .handler(request.id, request.url)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
mutating func useLinking() -> [URL] {
|
|
75
|
+
isLinkingReady = true
|
|
76
|
+
guard deliveryMode == .linking else { return [] }
|
|
77
|
+
|
|
78
|
+
let urls = requests.compactMap { request in
|
|
79
|
+
request.state == .buffered ? request.url : nil
|
|
80
|
+
}
|
|
81
|
+
requests.removeAll { $0.state == .buffered }
|
|
82
|
+
return urls
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
mutating func useHandler() -> [(UUID, URL)] {
|
|
86
|
+
hasHandler = true
|
|
87
|
+
var deliveries: [(UUID, URL)] = []
|
|
88
|
+
for index in requests.indices where requests[index].state == .buffered {
|
|
89
|
+
requests[index].state = .awaitingAcknowledgement
|
|
90
|
+
deliveries.append((requests[index].id, requests[index].url))
|
|
91
|
+
}
|
|
92
|
+
return deliveries
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
mutating func removeHandler() {
|
|
96
|
+
hasHandler = false
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
mutating func acknowledge(_ id: UUID, handled: Bool) -> Resolution? {
|
|
100
|
+
guard let index = requests.firstIndex(where: {
|
|
101
|
+
$0.id == id && $0.state == .awaitingAcknowledgement
|
|
102
|
+
}) else { return nil }
|
|
103
|
+
|
|
104
|
+
let url = requests.remove(at: index).url
|
|
105
|
+
return handled ? .handled : .fallback(url)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
mutating func expireReadiness(_ id: UUID) -> URL? {
|
|
109
|
+
expire(id, expectedState: .buffered)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
mutating func expireAcknowledgement(_ id: UUID) -> URL? {
|
|
113
|
+
expire(id, expectedState: .awaitingAcknowledgement)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private mutating func expire(_ id: UUID, expectedState: RequestState) -> URL? {
|
|
117
|
+
guard let index = requests.firstIndex(where: {
|
|
118
|
+
$0.id == id && $0.state == expectedState
|
|
119
|
+
}) else { return nil }
|
|
120
|
+
|
|
121
|
+
return requests.remove(at: index).url
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
@_spi(Internal) import ZixflowInternalCommon
|
|
2
|
+
import Foundation
|
|
3
|
+
import UIKit
|
|
4
|
+
|
|
5
|
+
enum ZixflowReactNativeDeepLinkRouter {
|
|
6
|
+
private static let readinessTimeout: TimeInterval = 10
|
|
7
|
+
private static let acknowledgementTimeout: TimeInterval = 10
|
|
8
|
+
private static let sceneManifestKey = "UIApplicationSceneManifest"
|
|
9
|
+
private static let sceneConfigurationsKey = "UISceneConfigurations"
|
|
10
|
+
private static let sceneOpenURLContextsSelector = NSSelectorFromString("scene:openURLContexts:")
|
|
11
|
+
// React Native 0.83.6 and 0.88.0-nightly-20260823-0c7f63a4e use this
|
|
12
|
+
// notification name and payload for Linking URL events.
|
|
13
|
+
private static let openURLNotification = Notification.Name("RCTOpenURLNotification")
|
|
14
|
+
private static let stateLock = NSLock()
|
|
15
|
+
private static var requestStore = ZixflowReactNativeDeepLinkRequestStore()
|
|
16
|
+
private static var handlerEmitter: ((_ id: String, _ url: String) -> Void)?
|
|
17
|
+
private static var handlerToken: UUID?
|
|
18
|
+
|
|
19
|
+
static var requiresAcknowledgedHandler: Bool {
|
|
20
|
+
stateLock.lock()
|
|
21
|
+
defer { stateLock.unlock() }
|
|
22
|
+
return requestStore.requiresAcknowledgedHandler
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private static var hasSceneManifest: Bool {
|
|
26
|
+
guard let manifest = Bundle.main.object(forInfoDictionaryKey: sceneManifestKey) as? [String: Any],
|
|
27
|
+
let configurations = manifest[sceneConfigurationsKey] as? [String: Any]
|
|
28
|
+
else { return false }
|
|
29
|
+
|
|
30
|
+
return !configurations.isEmpty
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Mirrors `RCTIsSceneDelegateApp()` without linking to the internal symbol, and also requires
|
|
34
|
+
/// React Native's scene-Linking entry point. A scene manifest alone does not make older React
|
|
35
|
+
/// Native versions scene-aware.
|
|
36
|
+
static var isSceneLifecycleEnabled: Bool {
|
|
37
|
+
guard hasSceneManifest,
|
|
38
|
+
let linkingManager = NSClassFromString("RCTLinkingManager")
|
|
39
|
+
else { return false }
|
|
40
|
+
|
|
41
|
+
return linkingManager.responds(to: sceneOpenURLContextsSelector)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Expo owns scene-to-Linking forwarding even when its React Native version does not expose
|
|
45
|
+
/// the scene selector itself.
|
|
46
|
+
static var isExpoSceneLifecycleEnabled: Bool {
|
|
47
|
+
hasSceneManifest && NSClassFromString("EXExpoAppSceneDelegate") != nil
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static func install() {
|
|
51
|
+
guard isSceneLifecycleEnabled else { return }
|
|
52
|
+
installCallback(requiringAcknowledgedHandler: false)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static func installAcknowledgedHandler() {
|
|
56
|
+
guard isSceneLifecycleEnabled else {
|
|
57
|
+
DIGraphShared.shared.logger.error(
|
|
58
|
+
"Zixflow could not install acknowledged deep-link routing because the host " +
|
|
59
|
+
"does not expose a supported React Native scene lifecycle"
|
|
60
|
+
)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
installCallback(requiringAcknowledgedHandler: true)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Expo owns scene-to-Linking forwarding even on React Native versions that do not expose the
|
|
67
|
+
/// scene selector themselves. Prefer the generic React Native capability when available, then
|
|
68
|
+
/// fall back to Expo's scene delegate capability.
|
|
69
|
+
static func installForExpoSceneLifecycle() {
|
|
70
|
+
guard isSceneLifecycleEnabled || isExpoSceneLifecycleEnabled else {
|
|
71
|
+
DIGraphShared.shared.logger.error(
|
|
72
|
+
"Zixflow could not install Expo scene deep-link routing because the host " +
|
|
73
|
+
"does not expose a supported React Native or Expo scene lifecycle"
|
|
74
|
+
)
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
installCallback(requiringAcknowledgedHandler: false)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private static func installCallback(requiringAcknowledgedHandler: Bool) {
|
|
81
|
+
stateLock.lock()
|
|
82
|
+
if requiringAcknowledgedHandler {
|
|
83
|
+
requestStore.requireAcknowledgedHandler()
|
|
84
|
+
}
|
|
85
|
+
stateLock.unlock()
|
|
86
|
+
DIGraphShared.shared.deepLinkUtil.setDeepLinkCallback { url in
|
|
87
|
+
accept(url)
|
|
88
|
+
return true
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static func accept(_ url: URL) {
|
|
93
|
+
stateLock.lock()
|
|
94
|
+
let acceptance = requestStore.accept(url)
|
|
95
|
+
stateLock.unlock()
|
|
96
|
+
|
|
97
|
+
switch acceptance {
|
|
98
|
+
case let .buffered(id):
|
|
99
|
+
DIGraphShared.shared.logger.info(
|
|
100
|
+
"Zixflow buffered an SDK deep link until a React Native route is ready"
|
|
101
|
+
)
|
|
102
|
+
scheduleReadinessExpiration(for: id)
|
|
103
|
+
case let .linking(url):
|
|
104
|
+
route(url)
|
|
105
|
+
case let .handler(id, url):
|
|
106
|
+
publishToHandler(id: id, url: url)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static func markReactNativeReady() {
|
|
111
|
+
let markReady = {
|
|
112
|
+
stateLock.lock()
|
|
113
|
+
let urls = requestStore.useLinking()
|
|
114
|
+
stateLock.unlock()
|
|
115
|
+
|
|
116
|
+
urls.forEach(route)
|
|
117
|
+
}
|
|
118
|
+
if Thread.isMainThread {
|
|
119
|
+
markReady()
|
|
120
|
+
} else {
|
|
121
|
+
DispatchQueue.main.async(execute: markReady)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static func registerHandler(
|
|
126
|
+
_ emitter: @escaping (_ id: String, _ url: String) -> Void
|
|
127
|
+
) -> UUID {
|
|
128
|
+
let token = UUID()
|
|
129
|
+
stateLock.lock()
|
|
130
|
+
let hasAcknowledgedHandlerConfiguration = requestStore.requiresAcknowledgedHandler
|
|
131
|
+
handlerToken = token
|
|
132
|
+
handlerEmitter = emitter
|
|
133
|
+
let deliveries = requestStore.useHandler()
|
|
134
|
+
stateLock.unlock()
|
|
135
|
+
|
|
136
|
+
if !hasAcknowledgedHandlerConfiguration {
|
|
137
|
+
DIGraphShared.shared.logger.error(
|
|
138
|
+
"Zixflow registered an acknowledged deep-link handler without configuring " +
|
|
139
|
+
"acknowledged scene routing before React Native started"
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
for (id, url) in deliveries {
|
|
143
|
+
publishToHandler(id: id, url: url)
|
|
144
|
+
}
|
|
145
|
+
return token
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static func unregisterHandler(_ token: UUID) {
|
|
149
|
+
stateLock.lock()
|
|
150
|
+
guard handlerToken == token else {
|
|
151
|
+
stateLock.unlock()
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
handlerToken = nil
|
|
155
|
+
handlerEmitter = nil
|
|
156
|
+
requestStore.removeHandler()
|
|
157
|
+
stateLock.unlock()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static func acknowledge(_ id: String, handled: Bool) {
|
|
161
|
+
guard let requestId = UUID(uuidString: id) else {
|
|
162
|
+
DIGraphShared.shared.logger.error(
|
|
163
|
+
"Zixflow ignored an invalid React Native deep-link acknowledgement"
|
|
164
|
+
)
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
stateLock.lock()
|
|
169
|
+
let resolution = requestStore.acknowledge(requestId, handled: handled)
|
|
170
|
+
stateLock.unlock()
|
|
171
|
+
|
|
172
|
+
switch resolution {
|
|
173
|
+
case .handled:
|
|
174
|
+
DIGraphShared.shared.logger.info(
|
|
175
|
+
"React Native acknowledged the Zixflow deep link"
|
|
176
|
+
)
|
|
177
|
+
case let .fallback(url):
|
|
178
|
+
DIGraphShared.shared.logger.info(
|
|
179
|
+
"React Native declined the Zixflow deep link; using the native fallback"
|
|
180
|
+
)
|
|
181
|
+
fallback(url)
|
|
182
|
+
case nil:
|
|
183
|
+
DIGraphShared.shared.logger.info(
|
|
184
|
+
"Zixflow ignored a late or unknown React Native deep-link acknowledgement; " +
|
|
185
|
+
"the native fallback may have already routed the destination"
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
static func route(_ url: URL) {
|
|
191
|
+
let publish = {
|
|
192
|
+
// React Native's AppDelegate URL entry point rejects scene-based hosts. Linking listens
|
|
193
|
+
// for this notification in both lifecycle modes, so publish the URL without choosing a scene.
|
|
194
|
+
DIGraphShared.shared.logger.info(
|
|
195
|
+
"Zixflow published an SDK deep link to React Native Linking"
|
|
196
|
+
)
|
|
197
|
+
NotificationCenter.default.post(
|
|
198
|
+
name: openURLNotification,
|
|
199
|
+
object: nil,
|
|
200
|
+
userInfo: ["url": url.absoluteString]
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
if Thread.isMainThread {
|
|
204
|
+
publish()
|
|
205
|
+
} else {
|
|
206
|
+
DispatchQueue.main.async(execute: publish)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private static func publishToHandler(id: UUID, url: URL) {
|
|
211
|
+
let publish = {
|
|
212
|
+
stateLock.lock()
|
|
213
|
+
let emitter = handlerEmitter
|
|
214
|
+
stateLock.unlock()
|
|
215
|
+
|
|
216
|
+
emitter?(id.uuidString, url.absoluteString)
|
|
217
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + acknowledgementTimeout) {
|
|
218
|
+
expireAcknowledgement(id)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if Thread.isMainThread {
|
|
222
|
+
publish()
|
|
223
|
+
} else {
|
|
224
|
+
DispatchQueue.main.async(execute: publish)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private static func scheduleReadinessExpiration(for id: UUID) {
|
|
229
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + readinessTimeout) {
|
|
230
|
+
expireReadiness(id)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private static func expireReadiness(_ id: UUID) {
|
|
235
|
+
stateLock.lock()
|
|
236
|
+
let url = requestStore.expireReadiness(id)
|
|
237
|
+
stateLock.unlock()
|
|
238
|
+
|
|
239
|
+
guard let url else { return }
|
|
240
|
+
|
|
241
|
+
DIGraphShared.shared.logger.error(
|
|
242
|
+
"Zixflow is using the native fallback because React Native deep-link routing " +
|
|
243
|
+
"did not become ready in time"
|
|
244
|
+
)
|
|
245
|
+
fallback(url)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private static func expireAcknowledgement(_ id: UUID) {
|
|
249
|
+
stateLock.lock()
|
|
250
|
+
let url = requestStore.expireAcknowledgement(id)
|
|
251
|
+
stateLock.unlock()
|
|
252
|
+
|
|
253
|
+
guard let url else { return }
|
|
254
|
+
|
|
255
|
+
DIGraphShared.shared.logger.error(
|
|
256
|
+
"Zixflow is using the native fallback because the React Native deep-link " +
|
|
257
|
+
"handler did not acknowledge the URL in time"
|
|
258
|
+
)
|
|
259
|
+
fallback(url)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private static func fallback(_ url: URL) {
|
|
263
|
+
let open = {
|
|
264
|
+
let uiKit = DIGraphShared.shared.uIKitWrapper
|
|
265
|
+
if uiKit.continueNSUserActivity(webpageURL: url) {
|
|
266
|
+
DIGraphShared.shared.logger.info(
|
|
267
|
+
"Zixflow deep link was handled by the host AppDelegate fallback"
|
|
268
|
+
)
|
|
269
|
+
} else if isAppOwnedCustomScheme(url) {
|
|
270
|
+
stateLock.lock()
|
|
271
|
+
let linkingIsReady = requestStore.canDeliverWithLinking
|
|
272
|
+
stateLock.unlock()
|
|
273
|
+
route(url)
|
|
274
|
+
if linkingIsReady {
|
|
275
|
+
DIGraphShared.shared.logger.info(
|
|
276
|
+
"Zixflow forwarded the host app's custom-scheme deep link to React " +
|
|
277
|
+
"Native Linking"
|
|
278
|
+
)
|
|
279
|
+
} else {
|
|
280
|
+
DIGraphShared.shared.logger.error(
|
|
281
|
+
"Zixflow published the host app's custom-scheme deep link before " +
|
|
282
|
+
"React Native Linking was marked ready; the destination may not be handled"
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
uiKit.open(url: url)
|
|
287
|
+
DIGraphShared.shared.logger.info(
|
|
288
|
+
"Zixflow opened the deep link through the system fallback"
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if Thread.isMainThread {
|
|
293
|
+
open()
|
|
294
|
+
} else {
|
|
295
|
+
DispatchQueue.main.async(execute: open)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private static func isAppOwnedCustomScheme(_ url: URL) -> Bool {
|
|
300
|
+
guard let scheme = url.scheme,
|
|
301
|
+
scheme.caseInsensitiveCompare("http") != .orderedSame,
|
|
302
|
+
scheme.caseInsensitiveCompare("https") != .orderedSame,
|
|
303
|
+
let urlTypes = Bundle.main.object(forInfoDictionaryKey: "CFBundleURLTypes")
|
|
304
|
+
as? [[String: Any]]
|
|
305
|
+
else { return false }
|
|
306
|
+
|
|
307
|
+
return urlTypes.contains { urlType in
|
|
308
|
+
guard let schemes = urlType["CFBundleURLSchemes"] as? [String] else { return false }
|
|
309
|
+
return schemes.contains { $0.caseInsensitiveCompare(scheme) == .orderedSame }
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#import <React/RCTBridgeModule.h>
|
|
2
|
+
#import <RNZixflowSpec/RNZixflowSpec.h>
|
|
3
|
+
|
|
4
|
+
// Objective-C wrapper for new architecture TurboModule implementation
|
|
5
|
+
@interface RCTNativeZixflowGeofence : NSObject <NativeZixflowGeofenceSpec>
|
|
6
|
+
// Bridge to Swift implementation for cross-language compatibility
|
|
7
|
+
@property(nonatomic, strong) id<NativeZixflowGeofenceSpec> swiftBridge;
|
|
8
|
+
@end
|
|
9
|
+
|
|
10
|
+
@implementation RCTNativeZixflowGeofence
|
|
11
|
+
|
|
12
|
+
RCT_EXPORT_MODULE()
|
|
13
|
+
|
|
14
|
+
// Create TurboModule instance for new architecture JSI integration
|
|
15
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
16
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
17
|
+
return std::make_shared<facebook::react::NativeZixflowGeofenceSpecJSI>(params);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
- (instancetype)init {
|
|
21
|
+
if (self = [super init]) {
|
|
22
|
+
// Use runtime class lookup - NativeGeofence class only exists when the ZixflowLocationGeofence subspec is installed
|
|
23
|
+
Class swiftClass = NSClassFromString(@"NativeZixflowGeofence");
|
|
24
|
+
if (swiftClass) {
|
|
25
|
+
_swiftBridge = [[swiftClass alloc] init];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return self;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Module initialization can happen on background thread
|
|
32
|
+
+ (BOOL)requiresMainQueueSetup {
|
|
33
|
+
return NO;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
- (void)refreshFromCurrentLocation {
|
|
37
|
+
if (!_swiftBridge) return;
|
|
38
|
+
[_swiftBridge refreshFromCurrentLocation];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Export class factory function for React Native component registration
|
|
42
|
+
Class<RCTBridgeModule> NativeZixflowGeofenceCls(void) {
|
|
43
|
+
return RCTNativeZixflowGeofence.class;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#if ZIXFLOW_GEOFENCE_ENABLED
|
|
2
|
+
import ZixflowDataPipelines
|
|
3
|
+
import ZixflowLocationGeofence
|
|
4
|
+
|
|
5
|
+
/// Registers the optional geofence module with the native iOS SDK and exposes its app-facing
|
|
6
|
+
/// methods. The reference to `GeofenceModule` is isolated here so it is only compiled when the
|
|
7
|
+
/// geofence subspec is installed. Geofence depends on the location module, which the caller
|
|
8
|
+
/// registers alongside it.
|
|
9
|
+
@objc(NativeZixflowGeofence)
|
|
10
|
+
public class NativeGeofence: NSObject {
|
|
11
|
+
|
|
12
|
+
/// Returns a `GeofenceModule` when the app opts into geofence via the `geofence` config,
|
|
13
|
+
/// applying the optional `locationMode` (defaults to `.automatic`).
|
|
14
|
+
static func module(from config: [String: Any]) -> GeofenceModule? {
|
|
15
|
+
guard let geofenceConfig = config["geofence"] as? [String: Any] else { return nil }
|
|
16
|
+
let locationModeValue = geofenceConfig["locationMode"] as? String
|
|
17
|
+
let locationMode: GeofenceLocationMode =
|
|
18
|
+
locationModeValue?.uppercased() == "MANUAL" ? .manual : .automatic
|
|
19
|
+
return GeofenceModule(config: GeofenceModuleConfig(locationMode: locationMode))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@objc
|
|
23
|
+
func refreshFromCurrentLocation() {
|
|
24
|
+
Zixflow.geofence.refreshFromCurrentLocation()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
#endif
|