@datalyr/react-native 1.4.9 → 1.6.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 +39 -0
- package/README.md +17 -127
- package/android/build.gradle +0 -7
- package/android/src/main/java/com/datalyr/reactnative/DatalyrNativeModule.java +2 -380
- package/android/src/main/java/com/datalyr/reactnative/DatalyrPackage.java +1 -1
- package/datalyr-react-native.podspec +3 -7
- package/expo-module.config.json +4 -1
- package/ios/DatalyrNativeModule.swift +0 -266
- package/lib/datalyr-sdk.d.ts +8 -4
- package/lib/datalyr-sdk.js +83 -143
- package/lib/http-client.js +2 -2
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/integrations/index.d.ts +3 -4
- package/lib/integrations/index.js +3 -4
- package/lib/native/DatalyrNativeBridge.d.ts +6 -22
- package/lib/native/DatalyrNativeBridge.js +6 -147
- package/lib/native/index.d.ts +1 -1
- package/lib/native/index.js +1 -1
- package/lib/types.d.ts +1 -19
- package/package.json +3 -5
- package/src/datalyr-sdk-expo.ts +6 -141
- package/src/datalyr-sdk.ts +96 -173
- package/src/http-client.ts +2 -2
- package/src/index.ts +1 -1
- package/src/integrations/index.ts +3 -4
- package/src/native/DatalyrNativeBridge.ts +6 -241
- package/src/native/index.ts +0 -2
- package/src/types.ts +2 -25
- package/src/utils-expo.ts +2 -2
- package/ios/DatalyrObjCExceptionCatcher.h +0 -14
- package/ios/DatalyrObjCExceptionCatcher.m +0 -30
- package/lib/integrations/meta-integration.d.ts +0 -77
- package/lib/integrations/meta-integration.js +0 -219
- package/lib/integrations/tiktok-integration.d.ts +0 -83
- package/lib/integrations/tiktok-integration.js +0 -360
- package/src/integrations/meta-integration.ts +0 -239
- package/src/integrations/tiktok-integration.ts +0 -363
|
@@ -1,283 +1,17 @@
|
|
|
1
1
|
import ExpoModulesCore
|
|
2
|
-
import FBSDKCoreKit
|
|
3
|
-
import TikTokBusinessSDK
|
|
4
2
|
import AdServices
|
|
5
3
|
import AppTrackingTransparency
|
|
6
4
|
import AdSupport
|
|
7
5
|
|
|
8
6
|
public class DatalyrNativeModule: Module {
|
|
9
|
-
private var tiktokInitialized = false
|
|
10
|
-
private var metaInitialized = false
|
|
11
7
|
|
|
12
8
|
public func definition() -> ModuleDefinition {
|
|
13
9
|
Name("DatalyrNative")
|
|
14
10
|
|
|
15
|
-
// MARK: - Meta (Facebook) SDK Methods
|
|
16
|
-
|
|
17
|
-
AsyncFunction("initializeMetaSDK") { (appId: String, clientToken: String?, advertiserTrackingEnabled: Bool, promise: Promise) in
|
|
18
|
-
DispatchQueue.main.async { [weak self] in
|
|
19
|
-
Settings.shared.appID = appId
|
|
20
|
-
|
|
21
|
-
if let token = clientToken, !token.isEmpty {
|
|
22
|
-
Settings.shared.clientToken = token
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
Settings.shared.isAdvertiserTrackingEnabled = advertiserTrackingEnabled
|
|
26
|
-
Settings.shared.isAdvertiserIDCollectionEnabled = advertiserTrackingEnabled
|
|
27
|
-
|
|
28
|
-
var initError: NSError?
|
|
29
|
-
let success = DatalyrObjCExceptionCatcher.tryBlock({
|
|
30
|
-
ApplicationDelegate.shared.application(
|
|
31
|
-
UIApplication.shared,
|
|
32
|
-
didFinishLaunchingWithOptions: nil
|
|
33
|
-
)
|
|
34
|
-
}, error: &initError)
|
|
35
|
-
|
|
36
|
-
if success {
|
|
37
|
-
self?.metaInitialized = true
|
|
38
|
-
promise.resolve(true)
|
|
39
|
-
} else {
|
|
40
|
-
let message = initError?.localizedDescription ?? "Unknown ObjC exception during Meta SDK init"
|
|
41
|
-
promise.reject("meta_init_error", message)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
AsyncFunction("fetchDeferredAppLink") { (promise: Promise) in
|
|
47
|
-
DispatchQueue.main.async {
|
|
48
|
-
AppLinkUtility.fetchDeferredAppLink { url, error in
|
|
49
|
-
if error != nil {
|
|
50
|
-
promise.resolve(nil)
|
|
51
|
-
return
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if let url = url {
|
|
55
|
-
promise.resolve(url.absoluteString)
|
|
56
|
-
} else {
|
|
57
|
-
promise.resolve(nil)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
AsyncFunction("logMetaEvent") { (eventName: String, valueToSum: Double?, parameters: [String: Any]?, promise: Promise) in
|
|
64
|
-
guard self.metaInitialized else {
|
|
65
|
-
promise.reject("meta_not_initialized", "Meta SDK not initialized. Call initializeMetaSDK first.")
|
|
66
|
-
return
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
DispatchQueue.main.async {
|
|
70
|
-
var params: [AppEvents.ParameterName: Any] = [:]
|
|
71
|
-
|
|
72
|
-
if let dict = parameters {
|
|
73
|
-
for (key, value) in dict {
|
|
74
|
-
params[AppEvents.ParameterName(key)] = value
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
var logError: NSError?
|
|
79
|
-
DatalyrObjCExceptionCatcher.tryBlock({
|
|
80
|
-
if let value = valueToSum {
|
|
81
|
-
AppEvents.shared.logEvent(AppEvents.Name(eventName), valueToSum: value, parameters: params)
|
|
82
|
-
} else if params.isEmpty {
|
|
83
|
-
AppEvents.shared.logEvent(AppEvents.Name(eventName))
|
|
84
|
-
} else {
|
|
85
|
-
AppEvents.shared.logEvent(AppEvents.Name(eventName), parameters: params)
|
|
86
|
-
}
|
|
87
|
-
}, error: &logError)
|
|
88
|
-
|
|
89
|
-
if let logError = logError {
|
|
90
|
-
promise.reject("meta_event_error", logError.localizedDescription)
|
|
91
|
-
} else {
|
|
92
|
-
promise.resolve(true)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
AsyncFunction("logMetaPurchase") { (amount: Double, currency: String, parameters: [String: Any]?, promise: Promise) in
|
|
98
|
-
guard self.metaInitialized else {
|
|
99
|
-
promise.reject("meta_not_initialized", "Meta SDK not initialized. Call initializeMetaSDK first.")
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
DispatchQueue.main.async {
|
|
104
|
-
var params: [AppEvents.ParameterName: Any] = [:]
|
|
105
|
-
|
|
106
|
-
if let dict = parameters {
|
|
107
|
-
for (key, value) in dict {
|
|
108
|
-
params[AppEvents.ParameterName(key)] = value
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
var logError: NSError?
|
|
113
|
-
DatalyrObjCExceptionCatcher.tryBlock({
|
|
114
|
-
AppEvents.shared.logPurchase(amount: amount, currency: currency, parameters: params)
|
|
115
|
-
}, error: &logError)
|
|
116
|
-
|
|
117
|
-
if let logError = logError {
|
|
118
|
-
promise.reject("meta_event_error", logError.localizedDescription)
|
|
119
|
-
} else {
|
|
120
|
-
promise.resolve(true)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
AsyncFunction("setMetaUserData") { (userData: [String: Any], promise: Promise) in
|
|
126
|
-
guard self.metaInitialized else {
|
|
127
|
-
promise.reject("meta_not_initialized", "Meta SDK not initialized. Call initializeMetaSDK first.")
|
|
128
|
-
return
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
DispatchQueue.main.async {
|
|
132
|
-
if let email = userData["email"] as? String { AppEvents.shared.setUserData(email, forType: .email) }
|
|
133
|
-
if let firstName = userData["firstName"] as? String { AppEvents.shared.setUserData(firstName, forType: .firstName) }
|
|
134
|
-
if let lastName = userData["lastName"] as? String { AppEvents.shared.setUserData(lastName, forType: .lastName) }
|
|
135
|
-
if let phone = userData["phone"] as? String { AppEvents.shared.setUserData(phone, forType: .phone) }
|
|
136
|
-
if let dateOfBirth = userData["dateOfBirth"] as? String { AppEvents.shared.setUserData(dateOfBirth, forType: .dateOfBirth) }
|
|
137
|
-
if let gender = userData["gender"] as? String { AppEvents.shared.setUserData(gender, forType: .gender) }
|
|
138
|
-
if let city = userData["city"] as? String { AppEvents.shared.setUserData(city, forType: .city) }
|
|
139
|
-
if let state = userData["state"] as? String { AppEvents.shared.setUserData(state, forType: .state) }
|
|
140
|
-
if let zip = userData["zip"] as? String { AppEvents.shared.setUserData(zip, forType: .zip) }
|
|
141
|
-
if let country = userData["country"] as? String { AppEvents.shared.setUserData(country, forType: .country) }
|
|
142
|
-
|
|
143
|
-
promise.resolve(true)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
AsyncFunction("clearMetaUserData") { (promise: Promise) in
|
|
148
|
-
guard self.metaInitialized else {
|
|
149
|
-
promise.reject("meta_not_initialized", "Meta SDK not initialized. Call initializeMetaSDK first.")
|
|
150
|
-
return
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
DispatchQueue.main.async {
|
|
154
|
-
AppEvents.shared.clearUserData()
|
|
155
|
-
promise.resolve(true)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
AsyncFunction("updateMetaTrackingAuthorization") { (enabled: Bool, promise: Promise) in
|
|
160
|
-
guard self.metaInitialized else {
|
|
161
|
-
promise.reject("meta_not_initialized", "Meta SDK not initialized. Call initializeMetaSDK first.")
|
|
162
|
-
return
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
DispatchQueue.main.async {
|
|
166
|
-
Settings.shared.isAdvertiserTrackingEnabled = enabled
|
|
167
|
-
Settings.shared.isAdvertiserIDCollectionEnabled = enabled
|
|
168
|
-
promise.resolve(true)
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// MARK: - TikTok SDK Methods
|
|
173
|
-
|
|
174
|
-
AsyncFunction("initializeTikTokSDK") { (appId: String, tiktokAppId: String, accessToken: String?, debug: Bool, promise: Promise) in
|
|
175
|
-
DispatchQueue.main.async { [weak self] in
|
|
176
|
-
guard let token = accessToken, !token.isEmpty else {
|
|
177
|
-
promise.reject("tiktok_init_error", "TikTok accessToken is required. The deprecated init without accessToken has been removed.")
|
|
178
|
-
return
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
let config = TikTokConfig(accessToken: token, appId: appId, tiktokAppId: tiktokAppId)
|
|
182
|
-
|
|
183
|
-
if debug {
|
|
184
|
-
config?.setLogLevel(TikTokLogLevelDebug)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
guard let validConfig = config else {
|
|
188
|
-
promise.reject("tiktok_init_error", "Failed to create TikTok config")
|
|
189
|
-
return
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
var initError: NSError?
|
|
193
|
-
let success = DatalyrObjCExceptionCatcher.tryBlock({
|
|
194
|
-
TikTokBusiness.initializeSdk(validConfig)
|
|
195
|
-
}, error: &initError)
|
|
196
|
-
|
|
197
|
-
if success {
|
|
198
|
-
self?.tiktokInitialized = true
|
|
199
|
-
promise.resolve(true)
|
|
200
|
-
} else {
|
|
201
|
-
let message = initError?.localizedDescription ?? "Unknown ObjC exception during TikTok SDK init"
|
|
202
|
-
promise.reject("tiktok_init_error", message)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
AsyncFunction("trackTikTokEvent") { (eventName: String, eventId: String?, properties: [String: Any]?, promise: Promise) in
|
|
208
|
-
guard self.tiktokInitialized else {
|
|
209
|
-
promise.reject("tiktok_not_initialized", "TikTok SDK not initialized. Call initializeTikTokSDK first.")
|
|
210
|
-
return
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
DispatchQueue.main.async {
|
|
214
|
-
let event: TikTokBaseEvent
|
|
215
|
-
|
|
216
|
-
if let eid = eventId, !eid.isEmpty {
|
|
217
|
-
event = TikTokBaseEvent(eventName: eventName, eventId: eid)
|
|
218
|
-
} else {
|
|
219
|
-
event = TikTokBaseEvent(eventName: eventName)
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if let dict = properties {
|
|
223
|
-
for (key, value) in dict {
|
|
224
|
-
event.addProperty(withKey: key, value: value)
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
var trackError: NSError?
|
|
229
|
-
DatalyrObjCExceptionCatcher.tryBlock({
|
|
230
|
-
TikTokBusiness.trackTTEvent(event)
|
|
231
|
-
}, error: &trackError)
|
|
232
|
-
|
|
233
|
-
if let trackError = trackError {
|
|
234
|
-
promise.reject("tiktok_event_error", trackError.localizedDescription)
|
|
235
|
-
} else {
|
|
236
|
-
promise.resolve(true)
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
AsyncFunction("identifyTikTokUser") { (externalId: String, externalUserName: String, phoneNumber: String, email: String, promise: Promise) in
|
|
242
|
-
guard self.tiktokInitialized else {
|
|
243
|
-
promise.reject("tiktok_not_initialized", "TikTok SDK not initialized. Call initializeTikTokSDK first.")
|
|
244
|
-
return
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
DispatchQueue.main.async {
|
|
248
|
-
TikTokBusiness.identify(
|
|
249
|
-
withExternalID: externalId.isEmpty ? nil : externalId,
|
|
250
|
-
externalUserName: externalUserName.isEmpty ? nil : externalUserName,
|
|
251
|
-
phoneNumber: phoneNumber.isEmpty ? nil : phoneNumber,
|
|
252
|
-
email: email.isEmpty ? nil : email
|
|
253
|
-
)
|
|
254
|
-
promise.resolve(true)
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
AsyncFunction("logoutTikTok") { (promise: Promise) in
|
|
259
|
-
guard self.tiktokInitialized else {
|
|
260
|
-
promise.reject("tiktok_not_initialized", "TikTok SDK not initialized. Call initializeTikTokSDK first.")
|
|
261
|
-
return
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
DispatchQueue.main.async {
|
|
265
|
-
TikTokBusiness.logout()
|
|
266
|
-
promise.resolve(true)
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
AsyncFunction("updateTikTokTrackingAuthorization") { (enabled: Bool, promise: Promise) in
|
|
271
|
-
// TikTok SDK handles ATT automatically, but we track the change
|
|
272
|
-
promise.resolve(true)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
11
|
// MARK: - SDK Availability Check
|
|
276
12
|
|
|
277
13
|
AsyncFunction("getSDKAvailability") { (promise: Promise) in
|
|
278
14
|
promise.resolve([
|
|
279
|
-
"meta": true,
|
|
280
|
-
"tiktok": true,
|
|
281
15
|
"appleSearchAds": true
|
|
282
16
|
])
|
|
283
17
|
}
|
package/lib/datalyr-sdk.d.ts
CHANGED
|
@@ -34,6 +34,13 @@ export declare class DatalyrSDK {
|
|
|
34
34
|
* Called automatically during identify() if email is provided
|
|
35
35
|
*/
|
|
36
36
|
private fetchAndMergeWebAttribution;
|
|
37
|
+
/**
|
|
38
|
+
* Fetch deferred web attribution on first app install.
|
|
39
|
+
* Uses IP-based matching (iOS) or Play Store referrer (Android) to recover
|
|
40
|
+
* attribution data (fbclid, utm_*, etc.) from a prelander web visit.
|
|
41
|
+
* Called automatically during initialize() when a fresh install is detected.
|
|
42
|
+
*/
|
|
43
|
+
private fetchDeferredWebAttribution;
|
|
37
44
|
/**
|
|
38
45
|
* Alias a user (connect anonymous user to known user)
|
|
39
46
|
*/
|
|
@@ -155,8 +162,6 @@ export declare class DatalyrSDK {
|
|
|
155
162
|
* Get platform integration status
|
|
156
163
|
*/
|
|
157
164
|
getPlatformIntegrationStatus(): {
|
|
158
|
-
meta: boolean;
|
|
159
|
-
tiktok: boolean;
|
|
160
165
|
appleSearchAds: boolean;
|
|
161
166
|
playInstallReferrer: boolean;
|
|
162
167
|
};
|
|
@@ -269,9 +274,8 @@ export declare class Datalyr {
|
|
|
269
274
|
static trackAddPaymentInfo(success?: boolean): Promise<void>;
|
|
270
275
|
static getDeferredAttributionData(): DeferredDeepLinkResult | null;
|
|
271
276
|
static getPlatformIntegrationStatus(): {
|
|
272
|
-
meta: boolean;
|
|
273
|
-
tiktok: boolean;
|
|
274
277
|
appleSearchAds: boolean;
|
|
278
|
+
playInstallReferrer: boolean;
|
|
275
279
|
};
|
|
276
280
|
static getAppleSearchAdsAttribution(): AppleSearchAdsAttribution | null;
|
|
277
281
|
static updateTrackingAuthorization(enabled: boolean): Promise<void>;
|
package/lib/datalyr-sdk.js
CHANGED
|
@@ -7,7 +7,7 @@ import { journeyManager } from './journey';
|
|
|
7
7
|
import { AutoEventsManager } from './auto-events';
|
|
8
8
|
import { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
9
9
|
import { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
10
|
-
import {
|
|
10
|
+
import { appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
|
|
11
11
|
import { AdvertiserInfoBridge } from './native/DatalyrNativeBridge';
|
|
12
12
|
import { networkStatusManager } from './network-status';
|
|
13
13
|
export class DatalyrSDK {
|
|
@@ -49,7 +49,7 @@ export class DatalyrSDK {
|
|
|
49
49
|
* Initialize the SDK with configuration
|
|
50
50
|
*/
|
|
51
51
|
async initialize(config) {
|
|
52
|
-
var _a
|
|
52
|
+
var _a;
|
|
53
53
|
try {
|
|
54
54
|
debugLog('Initializing Datalyr SDK...', { workspaceId: config.workspaceId });
|
|
55
55
|
// Validate configuration
|
|
@@ -152,22 +152,6 @@ export class DatalyrSDK {
|
|
|
152
152
|
// Google Play Install Referrer (Android only)
|
|
153
153
|
playInstallReferrerIntegration.initialize(),
|
|
154
154
|
];
|
|
155
|
-
// Add Meta initialization if configured
|
|
156
|
-
if ((_b = config.meta) === null || _b === void 0 ? void 0 : _b.appId) {
|
|
157
|
-
platformInitPromises.push(metaIntegration.initialize(config.meta, config.debug).then(async () => {
|
|
158
|
-
// After Meta initializes, fetch deferred deep link
|
|
159
|
-
if (config.enableAttribution !== false) {
|
|
160
|
-
const deferredLink = await metaIntegration.fetchDeferredDeepLink();
|
|
161
|
-
if (deferredLink) {
|
|
162
|
-
await this.handleDeferredDeepLink(deferredLink);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}));
|
|
166
|
-
}
|
|
167
|
-
// Add TikTok initialization if configured
|
|
168
|
-
if (((_c = config.tiktok) === null || _c === void 0 ? void 0 : _c.appId) && ((_d = config.tiktok) === null || _d === void 0 ? void 0 : _d.tiktokAppId)) {
|
|
169
|
-
platformInitPromises.push(tiktokIntegration.initialize(config.tiktok, config.debug));
|
|
170
|
-
}
|
|
171
155
|
// Wait for all platform integrations to complete
|
|
172
156
|
await Promise.all(platformInitPromises);
|
|
173
157
|
// Cache advertiser info (IDFA/GAID, ATT status) once at init to avoid per-event native bridge calls
|
|
@@ -178,8 +162,6 @@ export class DatalyrSDK {
|
|
|
178
162
|
errorLog('Failed to cache advertiser info:', error);
|
|
179
163
|
}
|
|
180
164
|
debugLog('Platform integrations initialized', {
|
|
181
|
-
meta: metaIntegration.isAvailable(),
|
|
182
|
-
tiktok: tiktokIntegration.isAvailable(),
|
|
183
165
|
appleSearchAds: appleSearchAdsIntegration.isAvailable(),
|
|
184
166
|
playInstallReferrer: playInstallReferrerIntegration.isAvailable(),
|
|
185
167
|
});
|
|
@@ -187,10 +169,15 @@ export class DatalyrSDK {
|
|
|
187
169
|
this.state.initialized = true;
|
|
188
170
|
// Check for app install (after SDK is marked as initialized)
|
|
189
171
|
if (attributionManager.isInstall()) {
|
|
172
|
+
// iOS: Attempt deferred web-to-app attribution via IP matching before tracking install
|
|
173
|
+
// Android: Play Store referrer is handled by playInstallReferrerIntegration
|
|
174
|
+
if (Platform.OS === 'ios') {
|
|
175
|
+
await this.fetchDeferredWebAttribution();
|
|
176
|
+
}
|
|
190
177
|
const installData = await attributionManager.trackInstall();
|
|
191
178
|
await this.track('app_install', {
|
|
192
179
|
platform: Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'android',
|
|
193
|
-
sdk_version: '1.
|
|
180
|
+
sdk_version: '1.6.0',
|
|
194
181
|
...installData,
|
|
195
182
|
});
|
|
196
183
|
}
|
|
@@ -282,28 +269,6 @@ export class DatalyrSDK {
|
|
|
282
269
|
const dateOfBirth = ((properties === null || properties === void 0 ? void 0 : properties.date_of_birth) || (properties === null || properties === void 0 ? void 0 : properties.dob) || (properties === null || properties === void 0 ? void 0 : properties.birthday));
|
|
283
270
|
const gender = properties === null || properties === void 0 ? void 0 : properties.gender;
|
|
284
271
|
const city = properties === null || properties === void 0 ? void 0 : properties.city;
|
|
285
|
-
const state = properties === null || properties === void 0 ? void 0 : properties.state;
|
|
286
|
-
const zip = ((properties === null || properties === void 0 ? void 0 : properties.zip) || (properties === null || properties === void 0 ? void 0 : properties.postal_code) || (properties === null || properties === void 0 ? void 0 : properties.zipcode));
|
|
287
|
-
const country = properties === null || properties === void 0 ? void 0 : properties.country;
|
|
288
|
-
// Meta Advanced Matching
|
|
289
|
-
if (metaIntegration.isAvailable()) {
|
|
290
|
-
metaIntegration.setUserData({
|
|
291
|
-
email,
|
|
292
|
-
firstName,
|
|
293
|
-
lastName,
|
|
294
|
-
phone,
|
|
295
|
-
dateOfBirth,
|
|
296
|
-
gender,
|
|
297
|
-
city,
|
|
298
|
-
state,
|
|
299
|
-
zip,
|
|
300
|
-
country,
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
// TikTok identification
|
|
304
|
-
if (tiktokIntegration.isAvailable()) {
|
|
305
|
-
tiktokIntegration.identify(email, phone, userId);
|
|
306
|
-
}
|
|
307
272
|
}
|
|
308
273
|
catch (error) {
|
|
309
274
|
errorLog('Error identifying user:', error);
|
|
@@ -368,6 +333,77 @@ export class DatalyrSDK {
|
|
|
368
333
|
// Non-blocking - continue even if attribution fetch fails
|
|
369
334
|
}
|
|
370
335
|
}
|
|
336
|
+
/**
|
|
337
|
+
* Fetch deferred web attribution on first app install.
|
|
338
|
+
* Uses IP-based matching (iOS) or Play Store referrer (Android) to recover
|
|
339
|
+
* attribution data (fbclid, utm_*, etc.) from a prelander web visit.
|
|
340
|
+
* Called automatically during initialize() when a fresh install is detected.
|
|
341
|
+
*/
|
|
342
|
+
async fetchDeferredWebAttribution() {
|
|
343
|
+
var _a;
|
|
344
|
+
if (!((_a = this.state.config) === null || _a === void 0 ? void 0 : _a.apiKey)) {
|
|
345
|
+
debugLog('API key not available for deferred attribution fetch');
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
debugLog('Fetching deferred web attribution via IP matching...');
|
|
350
|
+
const baseUrl = this.state.config.endpoint || 'https://api.datalyr.com';
|
|
351
|
+
const controller = new AbortController();
|
|
352
|
+
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
353
|
+
const response = await fetch(`${baseUrl}/attribution/deferred-lookup`, {
|
|
354
|
+
method: 'POST',
|
|
355
|
+
headers: {
|
|
356
|
+
'Content-Type': 'application/json',
|
|
357
|
+
'X-Datalyr-API-Key': this.state.config.apiKey,
|
|
358
|
+
},
|
|
359
|
+
body: JSON.stringify({ platform: Platform.OS }),
|
|
360
|
+
signal: controller.signal,
|
|
361
|
+
});
|
|
362
|
+
clearTimeout(timeout);
|
|
363
|
+
if (!response.ok) {
|
|
364
|
+
debugLog('Deferred attribution lookup failed:', response.status);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const result = await response.json();
|
|
368
|
+
if (!result.found || !result.attribution) {
|
|
369
|
+
debugLog('No deferred web attribution found for this IP');
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const webAttribution = result.attribution;
|
|
373
|
+
debugLog('Deferred web attribution found:', {
|
|
374
|
+
visitor_id: webAttribution.visitor_id,
|
|
375
|
+
has_fbclid: !!webAttribution.fbclid,
|
|
376
|
+
has_gclid: !!webAttribution.gclid,
|
|
377
|
+
utm_source: webAttribution.utm_source,
|
|
378
|
+
});
|
|
379
|
+
// Merge web attribution into current session
|
|
380
|
+
attributionManager.mergeWebAttribution(webAttribution);
|
|
381
|
+
// Track match event for analytics
|
|
382
|
+
await this.track('$web_attribution_matched', {
|
|
383
|
+
web_visitor_id: webAttribution.visitor_id,
|
|
384
|
+
web_user_id: webAttribution.user_id,
|
|
385
|
+
fbclid: webAttribution.fbclid,
|
|
386
|
+
gclid: webAttribution.gclid,
|
|
387
|
+
ttclid: webAttribution.ttclid,
|
|
388
|
+
gbraid: webAttribution.gbraid,
|
|
389
|
+
wbraid: webAttribution.wbraid,
|
|
390
|
+
fbp: webAttribution.fbp,
|
|
391
|
+
fbc: webAttribution.fbc,
|
|
392
|
+
utm_source: webAttribution.utm_source,
|
|
393
|
+
utm_medium: webAttribution.utm_medium,
|
|
394
|
+
utm_campaign: webAttribution.utm_campaign,
|
|
395
|
+
utm_content: webAttribution.utm_content,
|
|
396
|
+
utm_term: webAttribution.utm_term,
|
|
397
|
+
web_timestamp: webAttribution.timestamp,
|
|
398
|
+
match_method: 'ip',
|
|
399
|
+
});
|
|
400
|
+
debugLog('Successfully merged deferred web attribution');
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
errorLog('Error fetching deferred web attribution:', error);
|
|
404
|
+
// Non-blocking - email-based fallback will catch this on identify()
|
|
405
|
+
}
|
|
406
|
+
}
|
|
371
407
|
/**
|
|
372
408
|
* Alias a user (connect anonymous user to known user)
|
|
373
409
|
*/
|
|
@@ -407,10 +443,6 @@ export class DatalyrSDK {
|
|
|
407
443
|
await Storage.removeItem(STORAGE_KEYS.USER_PROPERTIES);
|
|
408
444
|
// Generate new session
|
|
409
445
|
this.state.sessionId = await getOrCreateSessionId();
|
|
410
|
-
// Clear user data from platform SDKs
|
|
411
|
-
if (metaIntegration.isAvailable()) {
|
|
412
|
-
metaIntegration.clearUserData();
|
|
413
|
-
}
|
|
414
446
|
debugLog('User data reset completed');
|
|
415
447
|
}
|
|
416
448
|
catch (error) {
|
|
@@ -556,14 +588,6 @@ export class DatalyrSDK {
|
|
|
556
588
|
if (productId)
|
|
557
589
|
properties.product_id = productId;
|
|
558
590
|
await this.trackWithSKAdNetwork('purchase', properties);
|
|
559
|
-
// Forward to Meta if available
|
|
560
|
-
if (metaIntegration.isAvailable()) {
|
|
561
|
-
metaIntegration.logPurchase(value, currency, { fb_content_id: productId });
|
|
562
|
-
}
|
|
563
|
-
// Forward to TikTok if available
|
|
564
|
-
if (tiktokIntegration.isAvailable()) {
|
|
565
|
-
tiktokIntegration.logPurchase(value, currency, productId, 'product');
|
|
566
|
-
}
|
|
567
591
|
}
|
|
568
592
|
/**
|
|
569
593
|
* Track subscription with automatic revenue encoding and platform forwarding
|
|
@@ -573,14 +597,6 @@ export class DatalyrSDK {
|
|
|
573
597
|
if (plan)
|
|
574
598
|
properties.plan = plan;
|
|
575
599
|
await this.trackWithSKAdNetwork('subscribe', properties);
|
|
576
|
-
// Forward to Meta if available
|
|
577
|
-
if (metaIntegration.isAvailable()) {
|
|
578
|
-
metaIntegration.logEvent('Subscribe', value, { subscription_plan: plan });
|
|
579
|
-
}
|
|
580
|
-
// Forward to TikTok if available
|
|
581
|
-
if (tiktokIntegration.isAvailable()) {
|
|
582
|
-
tiktokIntegration.logSubscription(value, currency, plan);
|
|
583
|
-
}
|
|
584
600
|
}
|
|
585
601
|
// MARK: - Standard E-commerce Events
|
|
586
602
|
/**
|
|
@@ -593,18 +609,6 @@ export class DatalyrSDK {
|
|
|
593
609
|
if (productName)
|
|
594
610
|
properties.product_name = productName;
|
|
595
611
|
await this.trackWithSKAdNetwork('add_to_cart', properties);
|
|
596
|
-
// Forward to Meta
|
|
597
|
-
if (metaIntegration.isAvailable()) {
|
|
598
|
-
metaIntegration.logEvent('AddToCart', value, {
|
|
599
|
-
currency,
|
|
600
|
-
content_ids: productId ? [productId] : undefined,
|
|
601
|
-
content_name: productName,
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
// Forward to TikTok
|
|
605
|
-
if (tiktokIntegration.isAvailable()) {
|
|
606
|
-
tiktokIntegration.logAddToCart(value, currency, productId, 'product');
|
|
607
|
-
}
|
|
608
612
|
}
|
|
609
613
|
/**
|
|
610
614
|
* Track view content/product event
|
|
@@ -620,19 +624,6 @@ export class DatalyrSDK {
|
|
|
620
624
|
if (currency)
|
|
621
625
|
properties.currency = currency;
|
|
622
626
|
await this.track('view_content', properties);
|
|
623
|
-
// Forward to Meta
|
|
624
|
-
if (metaIntegration.isAvailable()) {
|
|
625
|
-
metaIntegration.logEvent('ViewContent', value, {
|
|
626
|
-
content_ids: contentId ? [contentId] : undefined,
|
|
627
|
-
content_name: contentName,
|
|
628
|
-
content_type: contentType,
|
|
629
|
-
currency,
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
// Forward to TikTok
|
|
633
|
-
if (tiktokIntegration.isAvailable()) {
|
|
634
|
-
tiktokIntegration.logViewContent(contentId, contentName, contentType);
|
|
635
|
-
}
|
|
636
627
|
}
|
|
637
628
|
/**
|
|
638
629
|
* Track initiate checkout event
|
|
@@ -644,18 +635,6 @@ export class DatalyrSDK {
|
|
|
644
635
|
if (productIds)
|
|
645
636
|
properties.product_ids = productIds;
|
|
646
637
|
await this.trackWithSKAdNetwork('initiate_checkout', properties);
|
|
647
|
-
// Forward to Meta
|
|
648
|
-
if (metaIntegration.isAvailable()) {
|
|
649
|
-
metaIntegration.logEvent('InitiateCheckout', value, {
|
|
650
|
-
currency,
|
|
651
|
-
num_items: numItems,
|
|
652
|
-
content_ids: productIds,
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
// Forward to TikTok
|
|
656
|
-
if (tiktokIntegration.isAvailable()) {
|
|
657
|
-
tiktokIntegration.logInitiateCheckout(value, currency, numItems);
|
|
658
|
-
}
|
|
659
638
|
}
|
|
660
639
|
/**
|
|
661
640
|
* Track complete registration event
|
|
@@ -665,14 +644,6 @@ export class DatalyrSDK {
|
|
|
665
644
|
if (method)
|
|
666
645
|
properties.method = method;
|
|
667
646
|
await this.trackWithSKAdNetwork('complete_registration', properties);
|
|
668
|
-
// Forward to Meta
|
|
669
|
-
if (metaIntegration.isAvailable()) {
|
|
670
|
-
metaIntegration.logEvent('CompleteRegistration', undefined, { registration_method: method });
|
|
671
|
-
}
|
|
672
|
-
// Forward to TikTok
|
|
673
|
-
if (tiktokIntegration.isAvailable()) {
|
|
674
|
-
tiktokIntegration.logCompleteRegistration(method);
|
|
675
|
-
}
|
|
676
647
|
}
|
|
677
648
|
/**
|
|
678
649
|
* Track search event
|
|
@@ -682,17 +653,6 @@ export class DatalyrSDK {
|
|
|
682
653
|
if (resultIds)
|
|
683
654
|
properties.result_ids = resultIds;
|
|
684
655
|
await this.track('search', properties);
|
|
685
|
-
// Forward to Meta
|
|
686
|
-
if (metaIntegration.isAvailable()) {
|
|
687
|
-
metaIntegration.logEvent('Search', undefined, {
|
|
688
|
-
search_string: query,
|
|
689
|
-
content_ids: resultIds,
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
// Forward to TikTok
|
|
693
|
-
if (tiktokIntegration.isAvailable()) {
|
|
694
|
-
tiktokIntegration.logSearch(query);
|
|
695
|
-
}
|
|
696
656
|
}
|
|
697
657
|
/**
|
|
698
658
|
* Track lead/contact form submission
|
|
@@ -704,43 +664,25 @@ export class DatalyrSDK {
|
|
|
704
664
|
if (currency)
|
|
705
665
|
properties.currency = currency;
|
|
706
666
|
await this.trackWithSKAdNetwork('lead', properties);
|
|
707
|
-
// Forward to Meta
|
|
708
|
-
if (metaIntegration.isAvailable()) {
|
|
709
|
-
metaIntegration.logEvent('Lead', value, { currency });
|
|
710
|
-
}
|
|
711
|
-
// Forward to TikTok
|
|
712
|
-
if (tiktokIntegration.isAvailable()) {
|
|
713
|
-
tiktokIntegration.logLead(value, currency);
|
|
714
|
-
}
|
|
715
667
|
}
|
|
716
668
|
/**
|
|
717
669
|
* Track add payment info event
|
|
718
670
|
*/
|
|
719
671
|
async trackAddPaymentInfo(success = true) {
|
|
720
672
|
await this.track('add_payment_info', { success });
|
|
721
|
-
// Forward to Meta
|
|
722
|
-
if (metaIntegration.isAvailable()) {
|
|
723
|
-
metaIntegration.logEvent('AddPaymentInfo', undefined, { success: success ? 1 : 0 });
|
|
724
|
-
}
|
|
725
|
-
// Forward to TikTok
|
|
726
|
-
if (tiktokIntegration.isAvailable()) {
|
|
727
|
-
tiktokIntegration.logAddPaymentInfo(success);
|
|
728
|
-
}
|
|
729
673
|
}
|
|
730
674
|
// MARK: - Platform Integration Methods
|
|
731
675
|
/**
|
|
732
676
|
* Get deferred attribution data from platform SDKs
|
|
733
677
|
*/
|
|
734
678
|
getDeferredAttributionData() {
|
|
735
|
-
return
|
|
679
|
+
return null;
|
|
736
680
|
}
|
|
737
681
|
/**
|
|
738
682
|
* Get platform integration status
|
|
739
683
|
*/
|
|
740
684
|
getPlatformIntegrationStatus() {
|
|
741
685
|
return {
|
|
742
|
-
meta: metaIntegration.isAvailable(),
|
|
743
|
-
tiktok: tiktokIntegration.isAvailable(),
|
|
744
686
|
appleSearchAds: appleSearchAdsIntegration.isAvailable(),
|
|
745
687
|
playInstallReferrer: playInstallReferrerIntegration.isAvailable(),
|
|
746
688
|
};
|
|
@@ -769,8 +711,6 @@ export class DatalyrSDK {
|
|
|
769
711
|
errorLog('SDK not initialized. Call initialize() first.');
|
|
770
712
|
return;
|
|
771
713
|
}
|
|
772
|
-
metaIntegration.updateTrackingAuthorization(enabled);
|
|
773
|
-
tiktokIntegration.updateTrackingAuthorization(enabled);
|
|
774
714
|
// Refresh cached advertiser info after ATT status change
|
|
775
715
|
try {
|
|
776
716
|
this.cachedAdvertiserInfo = await AdvertiserInfoBridge.getAdvertiserInfo();
|
|
@@ -881,8 +821,8 @@ export class DatalyrSDK {
|
|
|
881
821
|
carrier: deviceInfo.carrier,
|
|
882
822
|
network_type: getNetworkType(),
|
|
883
823
|
timestamp: Date.now(),
|
|
884
|
-
sdk_version: '1.
|
|
885
|
-
// Advertiser data (IDFA/GAID, ATT status) for
|
|
824
|
+
sdk_version: '1.6.0',
|
|
825
|
+
// Advertiser data (IDFA/GAID, ATT status) for server-side postback
|
|
886
826
|
...(advertiserInfo ? {
|
|
887
827
|
idfa: advertiserInfo.idfa,
|
|
888
828
|
idfv: advertiserInfo.idfv,
|
|
@@ -1142,7 +1082,7 @@ export class Datalyr {
|
|
|
1142
1082
|
static updateAutoEventsConfig(config) {
|
|
1143
1083
|
datalyr.updateAutoEventsConfig(config);
|
|
1144
1084
|
}
|
|
1145
|
-
// Standard e-commerce events
|
|
1085
|
+
// Standard e-commerce events
|
|
1146
1086
|
static async trackAddToCart(value, currency = 'USD', productId, productName) {
|
|
1147
1087
|
await datalyr.trackAddToCart(value, currency, productId, productName);
|
|
1148
1088
|
}
|