@posthog/react-native-plugin 2.9.0 → 2.9.2
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/android/build.gradle
CHANGED
|
@@ -91,7 +91,7 @@ dependencies {
|
|
|
91
91
|
//noinspection GradleDynamicVersion
|
|
92
92
|
implementation "com.facebook.react:react-native:+"
|
|
93
93
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
94
|
-
implementation "com.posthog:posthog-android:3.
|
|
94
|
+
implementation "com.posthog:posthog-android:3.65.2"
|
|
95
95
|
|
|
96
96
|
testImplementation "junit:junit:4.13.2"
|
|
97
97
|
}
|
package/ios/Package.swift
CHANGED
|
@@ -75,3 +75,25 @@ RCT_EXTERN_METHOD(providePushIdentityToken:(NSString)requestId
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
@end
|
|
78
|
+
|
|
79
|
+
#if TARGET_OS_IOS
|
|
80
|
+
#import <UIKit/UIKit.h>
|
|
81
|
+
|
|
82
|
+
// Implemented in PosthogReactNativePlugin.swift.
|
|
83
|
+
@interface PosthogReactNativePlugin (PushNotificationOpenPrewarm)
|
|
84
|
+
+ (void)prewarmPushNotificationOpenCapture;
|
|
85
|
+
@end
|
|
86
|
+
|
|
87
|
+
// A notification tap that cold-launches the app is delivered right after launch, long before JS
|
|
88
|
+
// reaches setup(), and native modules are created lazily, so no module code runs in time. This
|
|
89
|
+
// runs at image load, like the RCT_EXTERN_MODULE registration above, so the host app needs no code.
|
|
90
|
+
__attribute__((constructor)) static void PosthogReactNativePluginObserveLaunch(void)
|
|
91
|
+
{
|
|
92
|
+
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification
|
|
93
|
+
object:nil
|
|
94
|
+
queue:nil
|
|
95
|
+
usingBlock:^(__unused NSNotification *notification) {
|
|
96
|
+
[PosthogReactNativePlugin prewarmPushNotificationOpenCapture];
|
|
97
|
+
}];
|
|
98
|
+
}
|
|
99
|
+
#endif
|
|
@@ -47,6 +47,50 @@ private func isReactNativeFatalJsError(_ event: PostHogEvent) -> Bool {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/// Carries the distinct id and consent JS holds into `beforeSend` for the length of
|
|
51
|
+
/// `PostHogSDK.setup(_:)`.
|
|
52
|
+
///
|
|
53
|
+
/// `setup()` installs the push-open integration, which replays a tap that cold-launched the app
|
|
54
|
+
/// synchronously — before `setIdentify` can mirror that id into native storage, so the replayed
|
|
55
|
+
/// event would carry whatever identity the previous launch left behind, and before the JS layer
|
|
56
|
+
/// re-asserts its consent: the native SDK prefers the opt-out it persisted itself, so after an
|
|
57
|
+
/// earlier launch opted in, `config.optOut` no longer says what JS said and the replay is captured
|
|
58
|
+
/// for a user JS considers opted out. posthog-ios offers no seam for either earlier (its storage
|
|
59
|
+
/// manager is created inside `setup()`, with an internal initializer, and disabling the integration
|
|
60
|
+
/// to delay the replay discards the held tap instead of deferring it) — so both are applied on the
|
|
61
|
+
/// way out instead: the id is stamped, and a tap JS denied is dropped, consumed so a later opt-in
|
|
62
|
+
/// cannot resurrect it. Cleared once `setup()` returns, after which every event resolves its
|
|
63
|
+
/// identity and consent from native state as before.
|
|
64
|
+
private final class SetupWindow {
|
|
65
|
+
private let lock = NSLock()
|
|
66
|
+
private var distinctId: String?
|
|
67
|
+
private var jsOptedOut: Bool
|
|
68
|
+
|
|
69
|
+
init(distinctId: String, jsOptedOut: Bool) {
|
|
70
|
+
self.distinctId = distinctId.isEmpty ? nil : distinctId
|
|
71
|
+
self.jsOptedOut = jsOptedOut
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
var pendingDistinctId: String? {
|
|
75
|
+
lock.lock()
|
|
76
|
+
defer { lock.unlock() }
|
|
77
|
+
return distinctId
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
var dropsPushOpens: Bool {
|
|
81
|
+
lock.lock()
|
|
82
|
+
defer { lock.unlock() }
|
|
83
|
+
return jsOptedOut
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
func settled() {
|
|
87
|
+
lock.lock()
|
|
88
|
+
defer { lock.unlock() }
|
|
89
|
+
distinctId = nil
|
|
90
|
+
jsOptedOut = false
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
50
94
|
// A nil identity token sends the request unauthenticated, which a project requiring
|
|
51
95
|
// identity verification rejects server-side. Log the reason so that failure is greppable
|
|
52
96
|
// and distinct from a host that deliberately returned nil.
|
|
@@ -69,6 +113,23 @@ public class PosthogReactNativePlugin: RCTEventEmitter {
|
|
|
69
113
|
private var hasPushListeners = false
|
|
70
114
|
private var pushIdentityCompletions: [String: (String?) -> Void] = [:]
|
|
71
115
|
|
|
116
|
+
#if os(iOS)
|
|
117
|
+
/// The one opt-out that reaches both halves of push-open capture, named and read exactly as in
|
|
118
|
+
/// posthog-flutter: `false` skips the launch hook below *and* forces the native config flag off
|
|
119
|
+
/// in setup(), so the JS flag can't re-enable what the plist turned off.
|
|
120
|
+
private static var plistCapturePushNotificationOpened: Bool {
|
|
121
|
+
Bundle.main.object(forInfoDictionaryKey: "com.posthog.posthog.CAPTURE_PUSH_NOTIFICATION_OPENED") as? Bool ?? true
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// Runs at launch, before JS config exists, so the Info.plist key is the only way to skip it.
|
|
125
|
+
@objc static func prewarmPushNotificationOpenCapture() {
|
|
126
|
+
guard plistCapturePushNotificationOpened else { return }
|
|
127
|
+
if #available(iOS 14.0, *) {
|
|
128
|
+
PostHogSDK.prewarmPushNotificationOpenCapture()
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
#endif
|
|
132
|
+
|
|
72
133
|
public override func supportedEvents() -> [String]! {
|
|
73
134
|
[PosthogReactNativePlugin.pushIdentityEvent]
|
|
74
135
|
}
|
|
@@ -183,10 +244,31 @@ public class PosthogReactNativePlugin: RCTEventEmitter {
|
|
|
183
244
|
}
|
|
184
245
|
#endif
|
|
185
246
|
|
|
247
|
+
let distinctId = sdkOptions["distinctId"] as? String ?? ""
|
|
248
|
+
let anonymousId = sdkOptions["anonymousId"] as? String ?? ""
|
|
249
|
+
let jsOptedOut = sdkOptions["optOut"] as? Bool ?? false
|
|
250
|
+
let setupWindow = SetupWindow(distinctId: distinctId, jsOptedOut: jsOptedOut)
|
|
251
|
+
// Every exit below is past the point where native storage carries the ids, or past a
|
|
252
|
+
// failure that left the SDK disabled, so no path can leave the window armed.
|
|
253
|
+
defer { setupWindow.settled() }
|
|
254
|
+
|
|
186
255
|
// React Native rethrows fatal JS errors natively (RCTFatalException / ExceptionsManager).
|
|
187
256
|
// The JS layer already captured them, so drop the native duplicate.
|
|
188
257
|
config.setBeforeSend { event in
|
|
189
|
-
isReactNativeFatalJsError(event)
|
|
258
|
+
if isReactNativeFatalJsError(event) {
|
|
259
|
+
return nil
|
|
260
|
+
}
|
|
261
|
+
// Only the replayed tap. `setup()` also replays the previous launch's crash report,
|
|
262
|
+
// and that `$exception` carries the distinct id recorded at crash time, which stands.
|
|
263
|
+
if event.event == "$push_notification_opened" {
|
|
264
|
+
if setupWindow.dropsPushOpens {
|
|
265
|
+
return nil
|
|
266
|
+
}
|
|
267
|
+
if let distinctId = setupWindow.pendingDistinctId {
|
|
268
|
+
event.distinctId = distinctId
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return event
|
|
190
272
|
}
|
|
191
273
|
|
|
192
274
|
// Surveys and session replay are iOS-only in posthog-ios, so the APIs below
|
|
@@ -238,15 +320,12 @@ public class PosthogReactNativePlugin: RCTEventEmitter {
|
|
|
238
320
|
}
|
|
239
321
|
#endif
|
|
240
322
|
|
|
241
|
-
let distinctId = sdkOptions["distinctId"] as? String ?? ""
|
|
242
|
-
let anonymousId = sdkOptions["anonymousId"] as? String ?? ""
|
|
243
|
-
|
|
244
323
|
let sdkVersion = sdkOptions["sdkVersion"] as? String ?? ""
|
|
245
324
|
|
|
246
325
|
let flushAt = sdkOptions["flushAt"] as? Int ?? 20
|
|
247
326
|
config.flushAt = flushAt
|
|
248
327
|
|
|
249
|
-
config.optOut =
|
|
328
|
+
config.optOut = jsOptedOut
|
|
250
329
|
// JS owns flags; it tells us when the native preload would be a duplicate. posthog-ios
|
|
251
330
|
// has no remoteConfig switch to mirror — it deprecated the option and always loads.
|
|
252
331
|
config.preloadFeatureFlags = sdkOptions["preloadFeatureFlags"] as? Bool ?? true
|
|
@@ -271,6 +350,13 @@ public class PosthogReactNativePlugin: RCTEventEmitter {
|
|
|
271
350
|
if let capturePushOpened = pushConfig["capturePushNotificationOpened"] as? Bool {
|
|
272
351
|
config.capturePushNotificationOpened = capturePushOpened
|
|
273
352
|
}
|
|
353
|
+
#if os(iOS)
|
|
354
|
+
// Only ever turns capture off: the plist is the app's build-time kill switch, so it wins
|
|
355
|
+
// over the JS flag, while a plist `true` leaves a JS opt-out alone.
|
|
356
|
+
if !PosthogReactNativePlugin.plistCapturePushNotificationOpened {
|
|
357
|
+
config.capturePushNotificationOpened = false
|
|
358
|
+
}
|
|
359
|
+
#endif
|
|
274
360
|
|
|
275
361
|
// Installed only when JS asked for it: an uninvited bridging provider would change
|
|
276
362
|
// how the native SDK handles a 401 on the subscription call.
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@ folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1
|
|
|
6
6
|
# Single source of truth for the posthog-ios native dependency version.
|
|
7
7
|
# Used by both the SPM and CocoaPods resolution paths below; bump this
|
|
8
8
|
# line when picking up a new posthog-ios release.
|
|
9
|
-
posthog_ios_version = '3.
|
|
9
|
+
posthog_ios_version = '3.75.2'
|
|
10
10
|
|
|
11
11
|
Pod::Spec.new do |s|
|
|
12
12
|
s.name = "posthog-react-native-plugin"
|