@datalyr/react-native 1.1.1 → 1.2.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 +24 -141
- package/LICENSE +21 -0
- package/README.md +405 -217
- package/datalyr-react-native.podspec +31 -0
- package/ios/DatalyrNative.m +70 -0
- package/ios/DatalyrNative.swift +275 -0
- package/ios/DatalyrSKAdNetwork.m +26 -0
- package/lib/datalyr-sdk.d.ts +64 -3
- package/lib/datalyr-sdk.js +322 -3
- package/lib/index.d.ts +1 -0
- package/lib/index.js +4 -2
- package/lib/integrations/index.d.ts +6 -0
- package/lib/integrations/index.js +6 -0
- package/lib/integrations/meta-integration.d.ts +76 -0
- package/lib/integrations/meta-integration.js +218 -0
- package/lib/integrations/tiktok-integration.d.ts +82 -0
- package/lib/integrations/tiktok-integration.js +356 -0
- package/lib/native/DatalyrNativeBridge.d.ts +31 -0
- package/lib/native/DatalyrNativeBridge.js +168 -0
- package/lib/native/index.d.ts +5 -0
- package/lib/native/index.js +5 -0
- package/lib/types.d.ts +29 -0
- package/package.json +10 -5
- package/src/datalyr-sdk-expo.ts +957 -0
- package/src/datalyr-sdk.ts +419 -19
- package/src/expo.ts +38 -18
- package/src/index.ts +5 -2
- package/src/integrations/index.ts +7 -0
- package/src/integrations/meta-integration.ts +238 -0
- package/src/integrations/tiktok-integration.ts +360 -0
- package/src/native/DatalyrNativeBridge.ts +271 -0
- package/src/native/index.ts +11 -0
- package/src/types.ts +39 -0
- package/src/utils-expo.ts +25 -3
- package/src/utils-interface.ts +38 -0
- package/EXPO_INSTALL.md +0 -297
- package/INSTALL.md +0 -402
- package/examples/attribution-example.tsx +0 -377
- package/examples/auto-events-example.tsx +0 -403
- package/examples/example.tsx +0 -250
- package/examples/skadnetwork-example.tsx +0 -380
- package/examples/test-implementation.tsx +0 -163
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native Bridge for Meta and TikTok SDKs
|
|
3
|
+
* Uses bundled native modules instead of separate npm packages
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { NativeModules, Platform } from 'react-native';
|
|
7
|
+
|
|
8
|
+
interface DatalyrNativeModule {
|
|
9
|
+
// Meta SDK Methods
|
|
10
|
+
initializeMetaSDK(
|
|
11
|
+
appId: string,
|
|
12
|
+
clientToken: string | null,
|
|
13
|
+
advertiserTrackingEnabled: boolean
|
|
14
|
+
): Promise<boolean>;
|
|
15
|
+
fetchDeferredAppLink(): Promise<string | null>;
|
|
16
|
+
logMetaEvent(
|
|
17
|
+
eventName: string,
|
|
18
|
+
valueToSum: number | null,
|
|
19
|
+
parameters: Record<string, any> | null
|
|
20
|
+
): Promise<boolean>;
|
|
21
|
+
logMetaPurchase(
|
|
22
|
+
amount: number,
|
|
23
|
+
currency: string,
|
|
24
|
+
parameters: Record<string, any> | null
|
|
25
|
+
): Promise<boolean>;
|
|
26
|
+
setMetaUserData(userData: Record<string, string | undefined>): Promise<boolean>;
|
|
27
|
+
clearMetaUserData(): Promise<boolean>;
|
|
28
|
+
updateMetaTrackingAuthorization(enabled: boolean): Promise<boolean>;
|
|
29
|
+
|
|
30
|
+
// TikTok SDK Methods
|
|
31
|
+
initializeTikTokSDK(
|
|
32
|
+
appId: string,
|
|
33
|
+
tiktokAppId: string,
|
|
34
|
+
accessToken: string | null,
|
|
35
|
+
debug: boolean
|
|
36
|
+
): Promise<boolean>;
|
|
37
|
+
trackTikTokEvent(
|
|
38
|
+
eventName: string,
|
|
39
|
+
eventId: string | null,
|
|
40
|
+
properties: Record<string, any> | null
|
|
41
|
+
): Promise<boolean>;
|
|
42
|
+
identifyTikTokUser(
|
|
43
|
+
externalId: string,
|
|
44
|
+
externalUserName: string,
|
|
45
|
+
phoneNumber: string,
|
|
46
|
+
email: string
|
|
47
|
+
): Promise<boolean>;
|
|
48
|
+
logoutTikTok(): Promise<boolean>;
|
|
49
|
+
updateTikTokTrackingAuthorization(enabled: boolean): Promise<boolean>;
|
|
50
|
+
|
|
51
|
+
// SDK Availability
|
|
52
|
+
getSDKAvailability(): Promise<{ meta: boolean; tiktok: boolean }>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Native module is only available on iOS
|
|
56
|
+
const DatalyrNative: DatalyrNativeModule | null =
|
|
57
|
+
Platform.OS === 'ios' ? NativeModules.DatalyrNative : null;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Check if native module is available
|
|
61
|
+
*/
|
|
62
|
+
export const isNativeModuleAvailable = (): boolean => {
|
|
63
|
+
return DatalyrNative !== null;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get SDK availability status
|
|
68
|
+
*/
|
|
69
|
+
export const getSDKAvailability = async (): Promise<{
|
|
70
|
+
meta: boolean;
|
|
71
|
+
tiktok: boolean;
|
|
72
|
+
}> => {
|
|
73
|
+
if (!DatalyrNative) {
|
|
74
|
+
return { meta: false, tiktok: false };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
return await DatalyrNative.getSDKAvailability();
|
|
79
|
+
} catch {
|
|
80
|
+
return { meta: false, tiktok: false };
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// MARK: - Meta SDK Bridge
|
|
85
|
+
|
|
86
|
+
export const MetaNativeBridge = {
|
|
87
|
+
async initialize(
|
|
88
|
+
appId: string,
|
|
89
|
+
clientToken?: string,
|
|
90
|
+
advertiserTrackingEnabled: boolean = false
|
|
91
|
+
): Promise<boolean> {
|
|
92
|
+
if (!DatalyrNative) return false;
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
return await DatalyrNative.initializeMetaSDK(
|
|
96
|
+
appId,
|
|
97
|
+
clientToken || null,
|
|
98
|
+
advertiserTrackingEnabled
|
|
99
|
+
);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error('[Datalyr/MetaNative] Initialize failed:', error);
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
async fetchDeferredAppLink(): Promise<string | null> {
|
|
107
|
+
if (!DatalyrNative) return null;
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
return await DatalyrNative.fetchDeferredAppLink();
|
|
111
|
+
} catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
async logEvent(
|
|
117
|
+
eventName: string,
|
|
118
|
+
valueToSum?: number,
|
|
119
|
+
parameters?: Record<string, any>
|
|
120
|
+
): Promise<boolean> {
|
|
121
|
+
if (!DatalyrNative) return false;
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
return await DatalyrNative.logMetaEvent(
|
|
125
|
+
eventName,
|
|
126
|
+
valueToSum ?? null,
|
|
127
|
+
parameters || null
|
|
128
|
+
);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error('[Datalyr/MetaNative] Log event failed:', error);
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
async logPurchase(
|
|
136
|
+
amount: number,
|
|
137
|
+
currency: string,
|
|
138
|
+
parameters?: Record<string, any>
|
|
139
|
+
): Promise<boolean> {
|
|
140
|
+
if (!DatalyrNative) return false;
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
return await DatalyrNative.logMetaPurchase(amount, currency, parameters || null);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error('[Datalyr/MetaNative] Log purchase failed:', error);
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
async setUserData(userData: Record<string, string | undefined>): Promise<boolean> {
|
|
151
|
+
if (!DatalyrNative) return false;
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
return await DatalyrNative.setMetaUserData(userData);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error('[Datalyr/MetaNative] Set user data failed:', error);
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
async clearUserData(): Promise<boolean> {
|
|
162
|
+
if (!DatalyrNative) return false;
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
return await DatalyrNative.clearMetaUserData();
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error('[Datalyr/MetaNative] Clear user data failed:', error);
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
async updateTrackingAuthorization(enabled: boolean): Promise<boolean> {
|
|
173
|
+
if (!DatalyrNative) return false;
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
return await DatalyrNative.updateMetaTrackingAuthorization(enabled);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error('[Datalyr/MetaNative] Update tracking failed:', error);
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// MARK: - TikTok SDK Bridge
|
|
185
|
+
|
|
186
|
+
export const TikTokNativeBridge = {
|
|
187
|
+
async initialize(
|
|
188
|
+
appId: string,
|
|
189
|
+
tiktokAppId: string,
|
|
190
|
+
accessToken?: string,
|
|
191
|
+
debug: boolean = false
|
|
192
|
+
): Promise<boolean> {
|
|
193
|
+
if (!DatalyrNative) return false;
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
return await DatalyrNative.initializeTikTokSDK(
|
|
197
|
+
appId,
|
|
198
|
+
tiktokAppId,
|
|
199
|
+
accessToken || null,
|
|
200
|
+
debug
|
|
201
|
+
);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.error('[Datalyr/TikTokNative] Initialize failed:', error);
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
async trackEvent(
|
|
209
|
+
eventName: string,
|
|
210
|
+
eventId?: string,
|
|
211
|
+
properties?: Record<string, any>
|
|
212
|
+
): Promise<boolean> {
|
|
213
|
+
if (!DatalyrNative) return false;
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
return await DatalyrNative.trackTikTokEvent(
|
|
217
|
+
eventName,
|
|
218
|
+
eventId || null,
|
|
219
|
+
properties || null
|
|
220
|
+
);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.error('[Datalyr/TikTokNative] Track event failed:', error);
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
async identify(
|
|
228
|
+
externalId?: string,
|
|
229
|
+
email?: string,
|
|
230
|
+
phone?: string
|
|
231
|
+
): Promise<boolean> {
|
|
232
|
+
if (!DatalyrNative) return false;
|
|
233
|
+
|
|
234
|
+
// Only call if we have at least one value
|
|
235
|
+
if (!externalId && !email && !phone) return false;
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
return await DatalyrNative.identifyTikTokUser(
|
|
239
|
+
externalId || '',
|
|
240
|
+
'', // externalUserName - not typically available
|
|
241
|
+
phone || '',
|
|
242
|
+
email || ''
|
|
243
|
+
);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error('[Datalyr/TikTokNative] Identify failed:', error);
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
async logout(): Promise<boolean> {
|
|
251
|
+
if (!DatalyrNative) return false;
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
return await DatalyrNative.logoutTikTok();
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error('[Datalyr/TikTokNative] Logout failed:', error);
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
async updateTrackingAuthorization(enabled: boolean): Promise<boolean> {
|
|
262
|
+
if (!DatalyrNative) return false;
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
return await DatalyrNative.updateTikTokTrackingAuthorization(enabled);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error('[Datalyr/TikTokNative] Update tracking failed:', error);
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
};
|
package/src/types.ts
CHANGED
|
@@ -7,6 +7,39 @@ export interface AutoEventConfig {
|
|
|
7
7
|
sessionTimeoutMs?: number;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
// Meta (Facebook) SDK Configuration
|
|
11
|
+
export interface MetaConfig {
|
|
12
|
+
appId: string; // Facebook App ID
|
|
13
|
+
clientToken?: string; // Client Token for advanced features
|
|
14
|
+
enableDeferredDeepLink?: boolean; // Default: true
|
|
15
|
+
enableAppEvents?: boolean; // Default: true
|
|
16
|
+
advertiserTrackingEnabled?: boolean; // iOS ATT status (auto-detected if not set)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// TikTok SDK Configuration
|
|
20
|
+
export interface TikTokConfig {
|
|
21
|
+
appId: string; // Your App ID (for Datalyr)
|
|
22
|
+
tiktokAppId: string; // TikTok App ID
|
|
23
|
+
accessToken?: string; // Access Token for Events API
|
|
24
|
+
enableAppEvents?: boolean; // Default: true
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Deferred Deep Link Result (from platform SDKs)
|
|
28
|
+
export interface DeferredDeepLinkResult {
|
|
29
|
+
url?: string;
|
|
30
|
+
source?: string;
|
|
31
|
+
fbclid?: string;
|
|
32
|
+
ttclid?: string;
|
|
33
|
+
utmSource?: string;
|
|
34
|
+
utmMedium?: string;
|
|
35
|
+
utmCampaign?: string;
|
|
36
|
+
utmContent?: string;
|
|
37
|
+
utmTerm?: string;
|
|
38
|
+
campaignId?: string;
|
|
39
|
+
adsetId?: string;
|
|
40
|
+
adId?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
10
43
|
// Core SDK Configuration
|
|
11
44
|
export interface DatalyrConfig {
|
|
12
45
|
apiKey: string; // Required for server-side tracking
|
|
@@ -33,6 +66,12 @@ export interface DatalyrConfig {
|
|
|
33
66
|
retryDelay: number;
|
|
34
67
|
};
|
|
35
68
|
skadTemplate?: 'ecommerce' | 'gaming' | 'subscription';
|
|
69
|
+
|
|
70
|
+
// Meta (Facebook) SDK Configuration
|
|
71
|
+
meta?: MetaConfig;
|
|
72
|
+
|
|
73
|
+
// TikTok SDK Configuration
|
|
74
|
+
tiktok?: TikTokConfig;
|
|
36
75
|
}
|
|
37
76
|
// Event Types
|
|
38
77
|
export interface EventData {
|
package/src/utils-expo.ts
CHANGED
|
@@ -9,13 +9,16 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
9
9
|
// Storage keys
|
|
10
10
|
export const STORAGE_KEYS = {
|
|
11
11
|
VISITOR_ID: '@datalyr/visitor_id',
|
|
12
|
+
ANONYMOUS_ID: '@datalyr/anonymous_id', // Persistent anonymous identifier
|
|
12
13
|
SESSION_ID: '@datalyr/session_id',
|
|
13
14
|
SESSION_START: '@datalyr/session_start',
|
|
14
15
|
USER_ID: '@datalyr/user_id',
|
|
15
16
|
USER_PROPERTIES: '@datalyr/user_properties',
|
|
17
|
+
EVENT_QUEUE: '@datalyr/event_queue',
|
|
16
18
|
ATTRIBUTION_DATA: '@datalyr/attribution_data',
|
|
17
19
|
INSTALL_TIME: '@datalyr/install_time',
|
|
18
20
|
LAST_APP_VERSION: '@datalyr/last_app_version',
|
|
21
|
+
LAST_SESSION_TIME: '@datalyr/last_session_time',
|
|
19
22
|
DEVICE_ID: '@datalyr/device_id',
|
|
20
23
|
} as const;
|
|
21
24
|
|
|
@@ -136,17 +139,17 @@ const getOrCreateDeviceId = async (): Promise<string> => {
|
|
|
136
139
|
}
|
|
137
140
|
};
|
|
138
141
|
|
|
139
|
-
// Visitor ID management
|
|
142
|
+
// Visitor ID management
|
|
140
143
|
export const getOrCreateVisitorId = async (): Promise<string> => {
|
|
141
144
|
try {
|
|
142
145
|
let visitorId = await Storage.getItem<string>(STORAGE_KEYS.VISITOR_ID);
|
|
143
|
-
|
|
146
|
+
|
|
144
147
|
if (!visitorId) {
|
|
145
148
|
visitorId = generateUUID();
|
|
146
149
|
await Storage.setItem(STORAGE_KEYS.VISITOR_ID, visitorId);
|
|
147
150
|
debugLog('Created new visitor ID:', visitorId);
|
|
148
151
|
}
|
|
149
|
-
|
|
152
|
+
|
|
150
153
|
return visitorId;
|
|
151
154
|
} catch (error) {
|
|
152
155
|
errorLog('Error managing visitor ID:', error as Error);
|
|
@@ -154,6 +157,25 @@ export const getOrCreateVisitorId = async (): Promise<string> => {
|
|
|
154
157
|
}
|
|
155
158
|
};
|
|
156
159
|
|
|
160
|
+
// Anonymous ID management - persistent across app reinstalls
|
|
161
|
+
export const getOrCreateAnonymousId = async (): Promise<string> => {
|
|
162
|
+
try {
|
|
163
|
+
let anonymousId = await Storage.getItem<string>(STORAGE_KEYS.ANONYMOUS_ID);
|
|
164
|
+
|
|
165
|
+
if (!anonymousId) {
|
|
166
|
+
// Generate anonymous_id with anon_ prefix to match web SDK
|
|
167
|
+
anonymousId = `anon_${generateUUID()}`;
|
|
168
|
+
await Storage.setItem(STORAGE_KEYS.ANONYMOUS_ID, anonymousId);
|
|
169
|
+
debugLog('Created new anonymous ID:', anonymousId);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return anonymousId;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
errorLog('Error managing anonymous ID:', error as Error);
|
|
175
|
+
return `anon_${generateUUID()}`;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
157
179
|
// Session management
|
|
158
180
|
export const getOrCreateSessionId = async (): Promise<string> => {
|
|
159
181
|
try {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for SDK utilities
|
|
3
|
+
* Both utils.ts and utils-expo.ts implement this interface
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DeviceInfo, FingerprintData } from './types';
|
|
7
|
+
|
|
8
|
+
export interface SDKUtils {
|
|
9
|
+
STORAGE_KEYS: {
|
|
10
|
+
VISITOR_ID: string;
|
|
11
|
+
ANONYMOUS_ID: string;
|
|
12
|
+
SESSION_ID: string;
|
|
13
|
+
USER_ID: string;
|
|
14
|
+
USER_PROPERTIES: string;
|
|
15
|
+
EVENT_QUEUE: string;
|
|
16
|
+
ATTRIBUTION_DATA: string;
|
|
17
|
+
LAST_SESSION_TIME: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
generateUUID: () => string;
|
|
21
|
+
getOrCreateVisitorId: () => Promise<string>;
|
|
22
|
+
getOrCreateAnonymousId: () => Promise<string>;
|
|
23
|
+
getOrCreateSessionId: () => Promise<string>;
|
|
24
|
+
getDeviceInfo: () => Promise<DeviceInfo>;
|
|
25
|
+
createFingerprintData: () => Promise<FingerprintData>;
|
|
26
|
+
getNetworkType: () => string | Promise<string>;
|
|
27
|
+
validateEventName: (eventName: string) => boolean;
|
|
28
|
+
validateEventData: (eventData: any) => boolean;
|
|
29
|
+
debugLog: (message: string, ...args: any[]) => void;
|
|
30
|
+
errorLog: (message: string, error?: Error) => void;
|
|
31
|
+
|
|
32
|
+
Storage: {
|
|
33
|
+
setItem: (key: string, value: any) => Promise<void>;
|
|
34
|
+
getItem: <T>(key: string) => Promise<T | null>;
|
|
35
|
+
removeItem: (key: string) => Promise<void>;
|
|
36
|
+
clear?: () => Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
}
|