@datalyr/react-native 1.3.0 → 1.4.0
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/CHANGELOG.md +19 -0
- package/README.md +145 -9
- package/datalyr-react-native.podspec +1 -1
- package/expo-module.config.json +6 -0
- package/ios/DatalyrNativeModule.swift +221 -0
- package/ios/DatalyrSKAdNetworkModule.swift +333 -0
- package/ios/PrivacyInfo.xcprivacy +48 -0
- package/lib/datalyr-sdk.d.ts +6 -0
- package/lib/datalyr-sdk.js +84 -27
- package/lib/index.d.ts +3 -1
- package/lib/index.js +3 -1
- package/lib/integrations/play-install-referrer.d.ts +5 -1
- package/lib/integrations/play-install-referrer.js +14 -4
- package/lib/native/DatalyrNativeBridge.js +20 -4
- package/lib/native/SKAdNetworkBridge.d.ts +121 -0
- package/lib/native/SKAdNetworkBridge.js +288 -4
- package/lib/network-status.d.ts +84 -0
- package/lib/network-status.js +281 -0
- package/lib/types.d.ts +51 -0
- package/lib/utils.d.ts +6 -1
- package/lib/utils.js +52 -2
- package/package.json +12 -2
- package/src/datalyr-sdk.ts +96 -32
- package/src/index.ts +5 -1
- package/src/integrations/play-install-referrer.ts +19 -4
- package/src/native/DatalyrNativeBridge.ts +17 -4
- package/src/native/SKAdNetworkBridge.ts +411 -9
- package/src/network-status.ts +312 -0
- package/src/types.ts +74 -6
- package/src/utils.ts +62 -6
- package/ios/DatalyrNative.m +0 -74
- package/ios/DatalyrNative.swift +0 -332
- package/ios/DatalyrSKAdNetwork.m +0 -77
package/src/datalyr-sdk.ts
CHANGED
|
@@ -33,6 +33,7 @@ import { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEn
|
|
|
33
33
|
import { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
34
34
|
import { metaIntegration, tiktokIntegration, appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
|
|
35
35
|
import { AppleSearchAdsAttribution } from './native/DatalyrNativeBridge';
|
|
36
|
+
import { networkStatusManager } from './network-status';
|
|
36
37
|
|
|
37
38
|
export class DatalyrSDK {
|
|
38
39
|
private state: SDKState;
|
|
@@ -40,6 +41,7 @@ export class DatalyrSDK {
|
|
|
40
41
|
private eventQueue: EventQueue;
|
|
41
42
|
private autoEventsManager: AutoEventsManager | null = null;
|
|
42
43
|
private appStateSubscription: any = null;
|
|
44
|
+
private networkStatusUnsubscribe: (() => void) | null = null;
|
|
43
45
|
private static conversionEncoder?: ConversionValueEncoder;
|
|
44
46
|
private static debugEnabled = false;
|
|
45
47
|
|
|
@@ -112,21 +114,21 @@ export class DatalyrSDK {
|
|
|
112
114
|
maxRetryCount: this.state.config.maxRetries || 3,
|
|
113
115
|
});
|
|
114
116
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
117
|
+
// PARALLEL INITIALIZATION: IDs and core managers
|
|
118
|
+
// Run ID creation and core manager initialization in parallel for faster startup
|
|
119
|
+
const [visitorId, anonymousId, sessionId] = await Promise.all([
|
|
120
|
+
getOrCreateVisitorId(),
|
|
121
|
+
getOrCreateAnonymousId(),
|
|
122
|
+
getOrCreateSessionId(),
|
|
123
|
+
// These run concurrently but don't return values we need to capture
|
|
124
|
+
this.loadPersistedUserData(),
|
|
125
|
+
this.state.config.enableAttribution ? attributionManager.initialize() : Promise.resolve(),
|
|
126
|
+
journeyManager.initialize(),
|
|
127
|
+
]);
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
this.state.visitorId = visitorId;
|
|
130
|
+
this.state.anonymousId = anonymousId;
|
|
131
|
+
this.state.sessionId = sessionId;
|
|
130
132
|
|
|
131
133
|
// Record initial attribution to journey if this is a new session with attribution
|
|
132
134
|
const initialAttribution = attributionManager.getAttributionData();
|
|
@@ -169,7 +171,7 @@ export class DatalyrSDK {
|
|
|
169
171
|
}
|
|
170
172
|
}, 50);
|
|
171
173
|
|
|
172
|
-
// Initialize SKAdNetwork conversion encoder
|
|
174
|
+
// Initialize SKAdNetwork conversion encoder (synchronous, no await needed)
|
|
173
175
|
if (config.skadTemplate) {
|
|
174
176
|
const template = ConversionTemplates[config.skadTemplate];
|
|
175
177
|
if (template) {
|
|
@@ -183,29 +185,41 @@ export class DatalyrSDK {
|
|
|
183
185
|
}
|
|
184
186
|
}
|
|
185
187
|
|
|
186
|
-
//
|
|
188
|
+
// PARALLEL INITIALIZATION: Network monitoring and platform integrations
|
|
189
|
+
// These are independent and can run concurrently for faster startup
|
|
190
|
+
const platformInitPromises: Promise<void>[] = [
|
|
191
|
+
// Network monitoring
|
|
192
|
+
this.initializeNetworkMonitoring(),
|
|
193
|
+
// Apple Search Ads (iOS only)
|
|
194
|
+
appleSearchAdsIntegration.initialize(config.debug),
|
|
195
|
+
// Google Play Install Referrer (Android only)
|
|
196
|
+
playInstallReferrerIntegration.initialize(),
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
// Add Meta initialization if configured
|
|
187
200
|
if (config.meta?.appId) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
201
|
+
platformInitPromises.push(
|
|
202
|
+
metaIntegration.initialize(config.meta, config.debug).then(async () => {
|
|
203
|
+
// After Meta initializes, fetch deferred deep link
|
|
204
|
+
if (config.enableAttribution !== false) {
|
|
205
|
+
const deferredLink = await metaIntegration.fetchDeferredDeepLink();
|
|
206
|
+
if (deferredLink) {
|
|
207
|
+
await this.handleDeferredDeepLink(deferredLink);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
);
|
|
197
212
|
}
|
|
198
213
|
|
|
199
|
-
//
|
|
214
|
+
// Add TikTok initialization if configured
|
|
200
215
|
if (config.tiktok?.appId && config.tiktok?.tiktokAppId) {
|
|
201
|
-
|
|
216
|
+
platformInitPromises.push(
|
|
217
|
+
tiktokIntegration.initialize(config.tiktok, config.debug)
|
|
218
|
+
);
|
|
202
219
|
}
|
|
203
220
|
|
|
204
|
-
//
|
|
205
|
-
await
|
|
206
|
-
|
|
207
|
-
// Initialize Google Play Install Referrer (Android only)
|
|
208
|
-
await playInstallReferrerIntegration.initialize();
|
|
221
|
+
// Wait for all platform integrations to complete
|
|
222
|
+
await Promise.all(platformInitPromises);
|
|
209
223
|
|
|
210
224
|
debugLog('Platform integrations initialized', {
|
|
211
225
|
meta: metaIntegration.isAvailable(),
|
|
@@ -1094,6 +1108,45 @@ export class DatalyrSDK {
|
|
|
1094
1108
|
}
|
|
1095
1109
|
}
|
|
1096
1110
|
|
|
1111
|
+
/**
|
|
1112
|
+
* Initialize network status monitoring
|
|
1113
|
+
* Automatically updates event queue when network status changes
|
|
1114
|
+
*/
|
|
1115
|
+
private async initializeNetworkMonitoring(): Promise<void> {
|
|
1116
|
+
try {
|
|
1117
|
+
await networkStatusManager.initialize();
|
|
1118
|
+
|
|
1119
|
+
// Update event queue with current network status
|
|
1120
|
+
this.state.isOnline = networkStatusManager.isOnline();
|
|
1121
|
+
this.eventQueue.setOnlineStatus(this.state.isOnline);
|
|
1122
|
+
|
|
1123
|
+
// Subscribe to network changes
|
|
1124
|
+
this.networkStatusUnsubscribe = networkStatusManager.subscribe((state) => {
|
|
1125
|
+
const isOnline = state.isConnected && (state.isInternetReachable !== false);
|
|
1126
|
+
this.state.isOnline = isOnline;
|
|
1127
|
+
this.eventQueue.setOnlineStatus(isOnline);
|
|
1128
|
+
|
|
1129
|
+
// Track network status change event (only if SDK is fully initialized)
|
|
1130
|
+
if (this.state.initialized) {
|
|
1131
|
+
this.track('$network_status_change', {
|
|
1132
|
+
is_online: isOnline,
|
|
1133
|
+
network_type: state.type,
|
|
1134
|
+
is_internet_reachable: state.isInternetReachable,
|
|
1135
|
+
}).catch(() => {
|
|
1136
|
+
// Ignore errors for network status events
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
debugLog(`Network monitoring initialized, online: ${this.state.isOnline}`);
|
|
1142
|
+
} catch (error) {
|
|
1143
|
+
errorLog('Error initializing network monitoring (non-blocking):', error as Error);
|
|
1144
|
+
// Default to online if monitoring fails
|
|
1145
|
+
this.state.isOnline = true;
|
|
1146
|
+
this.eventQueue.setOnlineStatus(true);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1097
1150
|
/**
|
|
1098
1151
|
* Set up app state monitoring for lifecycle events (optimized)
|
|
1099
1152
|
*/
|
|
@@ -1114,6 +1167,8 @@ export class DatalyrSDK {
|
|
|
1114
1167
|
} else if (nextAppState === 'active') {
|
|
1115
1168
|
// App became active, ensure we have fresh session if needed
|
|
1116
1169
|
this.refreshSession();
|
|
1170
|
+
// Refresh network status when coming back from background
|
|
1171
|
+
networkStatusManager.refresh();
|
|
1117
1172
|
// Notify auto-events manager for session handling
|
|
1118
1173
|
if (this.autoEventsManager) {
|
|
1119
1174
|
this.autoEventsManager.handleAppForeground();
|
|
@@ -1154,6 +1209,15 @@ export class DatalyrSDK {
|
|
|
1154
1209
|
this.appStateSubscription = null;
|
|
1155
1210
|
}
|
|
1156
1211
|
|
|
1212
|
+
// Remove network status listener
|
|
1213
|
+
if (this.networkStatusUnsubscribe) {
|
|
1214
|
+
this.networkStatusUnsubscribe();
|
|
1215
|
+
this.networkStatusUnsubscribe = null;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// Destroy network status manager
|
|
1219
|
+
networkStatusManager.destroy();
|
|
1220
|
+
|
|
1157
1221
|
// Destroy event queue
|
|
1158
1222
|
this.eventQueue.destroy();
|
|
1159
1223
|
|
package/src/index.ts
CHANGED
|
@@ -29,7 +29,11 @@ export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEn
|
|
|
29
29
|
export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
30
30
|
|
|
31
31
|
// Export platform integrations
|
|
32
|
-
export { metaIntegration, tiktokIntegration, appleSearchAdsIntegration } from './integrations';
|
|
32
|
+
export { metaIntegration, tiktokIntegration, appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
|
|
33
|
+
|
|
34
|
+
// Export network status manager
|
|
35
|
+
export { networkStatusManager } from './network-status';
|
|
36
|
+
export type { NetworkState, NetworkStateListener } from './network-status';
|
|
33
37
|
|
|
34
38
|
// Export native bridge types
|
|
35
39
|
export type { AppleSearchAdsAttribution } from './native/DatalyrNativeBridge';
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
* - referrer_url: Full referrer URL from Play Store
|
|
20
20
|
* - referrer_click_timestamp: When the referrer link was clicked
|
|
21
21
|
* - install_begin_timestamp: When the install began
|
|
22
|
-
* - gclid: Google Ads click ID (
|
|
22
|
+
* - gclid: Google Ads click ID (standard)
|
|
23
|
+
* - gbraid: Google Ads privacy-safe click ID (iOS App campaigns)
|
|
24
|
+
* - wbraid: Google Ads privacy-safe click ID (Web-to-App campaigns)
|
|
23
25
|
* - utm_source, utm_medium, utm_campaign, etc.
|
|
24
26
|
*/
|
|
25
27
|
|
|
@@ -37,6 +39,10 @@ export interface PlayInstallReferrer {
|
|
|
37
39
|
installCompleteTimestamp?: number;
|
|
38
40
|
// Google Ads click ID
|
|
39
41
|
gclid?: string;
|
|
42
|
+
// Google Ads privacy-safe click IDs (iOS App campaigns)
|
|
43
|
+
gbraid?: string;
|
|
44
|
+
// Google Ads privacy-safe click IDs (Web-to-App campaigns)
|
|
45
|
+
wbraid?: string;
|
|
40
46
|
// UTM Parameters
|
|
41
47
|
utmSource?: string;
|
|
42
48
|
utmMedium?: string;
|
|
@@ -94,6 +100,8 @@ class PlayInstallReferrerIntegration {
|
|
|
94
100
|
utmSource: this.referrerData.utmSource,
|
|
95
101
|
utmMedium: this.referrerData.utmMedium,
|
|
96
102
|
hasGclid: !!this.referrerData.gclid,
|
|
103
|
+
hasGbraid: !!this.referrerData.gbraid,
|
|
104
|
+
hasWbraid: !!this.referrerData.wbraid,
|
|
97
105
|
});
|
|
98
106
|
}
|
|
99
107
|
} catch (error) {
|
|
@@ -149,12 +157,15 @@ class PlayInstallReferrerIntegration {
|
|
|
149
157
|
params.utmTerm = searchParams.get('utm_term') || undefined;
|
|
150
158
|
params.utmContent = searchParams.get('utm_content') || undefined;
|
|
151
159
|
|
|
152
|
-
// Extract click IDs
|
|
160
|
+
// Extract click IDs (gclid, gbraid, wbraid)
|
|
153
161
|
params.gclid = searchParams.get('gclid') || undefined;
|
|
162
|
+
params.gbraid = searchParams.get('gbraid') || undefined;
|
|
163
|
+
params.wbraid = searchParams.get('wbraid') || undefined;
|
|
154
164
|
|
|
155
165
|
// Store any additional parameters
|
|
166
|
+
const knownParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid', 'gbraid', 'wbraid'];
|
|
156
167
|
searchParams.forEach((value, key) => {
|
|
157
|
-
if (!
|
|
168
|
+
if (!knownParams.includes(key) && !key.startsWith('utm_')) {
|
|
158
169
|
params[key] = value;
|
|
159
170
|
}
|
|
160
171
|
});
|
|
@@ -186,8 +197,12 @@ class PlayInstallReferrerIntegration {
|
|
|
186
197
|
referrer_click_timestamp: this.referrerData.referrerClickTimestamp,
|
|
187
198
|
install_begin_timestamp: this.referrerData.installBeginTimestamp,
|
|
188
199
|
|
|
189
|
-
//
|
|
200
|
+
// Google Ads click IDs (gclid is standard, gbraid/wbraid are privacy-safe alternatives)
|
|
190
201
|
gclid: this.referrerData.gclid,
|
|
202
|
+
gbraid: this.referrerData.gbraid,
|
|
203
|
+
wbraid: this.referrerData.wbraid,
|
|
204
|
+
|
|
205
|
+
// UTM parameters
|
|
191
206
|
utm_source: this.referrerData.utmSource,
|
|
192
207
|
utm_medium: this.referrerData.utmMedium,
|
|
193
208
|
utm_campaign: this.referrerData.utmCampaign,
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { NativeModules, Platform } from 'react-native';
|
|
11
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Apple Search Ads attribution data returned from AdServices API (iOS only)
|
|
@@ -107,11 +108,23 @@ interface PlayInstallReferrerModule {
|
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
// Native modules - available on both iOS and Android
|
|
110
|
-
|
|
111
|
+
// iOS uses Expo Modules (new arch compatible), Android uses NativeModules (interop layer)
|
|
112
|
+
let DatalyrNative: DatalyrNativeModule | null = null;
|
|
113
|
+
if (Platform.OS === 'ios') {
|
|
114
|
+
try {
|
|
115
|
+
DatalyrNative = requireNativeModule<DatalyrNativeModule>('DatalyrNative');
|
|
116
|
+
} catch {
|
|
117
|
+
// Native module not available
|
|
118
|
+
}
|
|
119
|
+
} else if (Platform.OS === 'android') {
|
|
120
|
+
DatalyrNative = NativeModules.DatalyrNative ?? null;
|
|
121
|
+
}
|
|
111
122
|
|
|
112
|
-
// Play Install Referrer - Android only
|
|
113
|
-
|
|
114
|
-
|
|
123
|
+
// Play Install Referrer - Android only (stays on NativeModules)
|
|
124
|
+
let DatalyrPlayInstallReferrer: PlayInstallReferrerModule | null = null;
|
|
125
|
+
if (Platform.OS === 'android') {
|
|
126
|
+
DatalyrPlayInstallReferrer = NativeModules.DatalyrPlayInstallReferrer ?? null;
|
|
127
|
+
}
|
|
115
128
|
|
|
116
129
|
/**
|
|
117
130
|
* Check if native module is available
|