@datalyr/react-native 1.2.0 → 1.3.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 +8 -0
- package/README.md +30 -1
- package/android/build.gradle +54 -0
- package/android/src/main/AndroidManifest.xml +14 -0
- package/android/src/main/java/com/datalyr/reactnative/DatalyrNativeModule.java +423 -0
- package/android/src/main/java/com/datalyr/reactnative/DatalyrPackage.java +30 -0
- package/android/src/main/java/com/datalyr/reactnative/DatalyrPlayInstallReferrerModule.java +229 -0
- package/datalyr-react-native.podspec +2 -2
- package/ios/DatalyrNative.m +4 -0
- package/ios/DatalyrNative.swift +58 -1
- package/ios/DatalyrSKAdNetwork.m +52 -1
- package/lib/ConversionValueEncoder.d.ts +13 -1
- package/lib/ConversionValueEncoder.js +57 -23
- package/lib/datalyr-sdk.d.ts +34 -2
- package/lib/datalyr-sdk.js +90 -8
- package/lib/index.d.ts +4 -1
- package/lib/index.js +2 -1
- package/lib/integrations/apple-search-ads-integration.d.ts +43 -0
- package/lib/integrations/apple-search-ads-integration.js +106 -0
- package/lib/integrations/index.d.ts +4 -1
- package/lib/integrations/index.js +3 -1
- package/lib/integrations/meta-integration.d.ts +1 -0
- package/lib/integrations/meta-integration.js +4 -3
- package/lib/integrations/play-install-referrer.d.ts +74 -0
- package/lib/integrations/play-install-referrer.js +156 -0
- package/lib/integrations/tiktok-integration.d.ts +1 -0
- package/lib/integrations/tiktok-integration.js +4 -3
- package/lib/journey.d.ts +106 -0
- package/lib/journey.js +258 -0
- package/lib/native/DatalyrNativeBridge.d.ts +67 -2
- package/lib/native/DatalyrNativeBridge.js +80 -7
- package/lib/native/SKAdNetworkBridge.d.ts +21 -0
- package/lib/native/SKAdNetworkBridge.js +54 -0
- package/package.json +9 -3
- package/src/ConversionValueEncoder.ts +67 -26
- package/src/datalyr-sdk-expo.ts +98 -9
- package/src/datalyr-sdk.ts +109 -14
- package/src/expo.ts +8 -0
- package/src/index.ts +6 -1
- package/src/integrations/apple-search-ads-integration.ts +119 -0
- package/src/integrations/index.ts +4 -1
- package/src/integrations/meta-integration.ts +4 -3
- package/src/integrations/play-install-referrer.ts +203 -0
- package/src/integrations/tiktok-integration.ts +4 -3
- package/src/journey.ts +338 -0
- package/src/native/DatalyrNativeBridge.ts +137 -9
- package/src/native/SKAdNetworkBridge.ts +86 -2
package/lib/datalyr-sdk.js
CHANGED
|
@@ -3,10 +3,11 @@ import { getOrCreateVisitorId, getOrCreateAnonymousId, getOrCreateSessionId, cre
|
|
|
3
3
|
import { createHttpClient, HttpClient } from './http-client';
|
|
4
4
|
import { createEventQueue, EventQueue } from './event-queue';
|
|
5
5
|
import { attributionManager } from './attribution';
|
|
6
|
+
import { journeyManager } from './journey';
|
|
6
7
|
import { AutoEventsManager } from './auto-events';
|
|
7
8
|
import { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
8
9
|
import { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
9
|
-
import { metaIntegration, tiktokIntegration } from './integrations';
|
|
10
|
+
import { metaIntegration, tiktokIntegration, appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
|
|
10
11
|
export class DatalyrSDK {
|
|
11
12
|
constructor() {
|
|
12
13
|
this.autoEventsManager = null;
|
|
@@ -82,6 +83,22 @@ export class DatalyrSDK {
|
|
|
82
83
|
if (this.state.config.enableAttribution) {
|
|
83
84
|
await attributionManager.initialize();
|
|
84
85
|
}
|
|
86
|
+
// Initialize journey tracking (for first-touch, last-touch, touchpoints)
|
|
87
|
+
await journeyManager.initialize();
|
|
88
|
+
// Record initial attribution to journey if this is a new session with attribution
|
|
89
|
+
const initialAttribution = attributionManager.getAttributionData();
|
|
90
|
+
if (initialAttribution.utm_source || initialAttribution.fbclid || initialAttribution.gclid || initialAttribution.lyr) {
|
|
91
|
+
await journeyManager.recordAttribution(this.state.sessionId, {
|
|
92
|
+
source: initialAttribution.utm_source || initialAttribution.campaign_source,
|
|
93
|
+
medium: initialAttribution.utm_medium || initialAttribution.campaign_medium,
|
|
94
|
+
campaign: initialAttribution.utm_campaign || initialAttribution.campaign_name,
|
|
95
|
+
fbclid: initialAttribution.fbclid,
|
|
96
|
+
gclid: initialAttribution.gclid,
|
|
97
|
+
ttclid: initialAttribution.ttclid,
|
|
98
|
+
clickIdType: initialAttribution.fbclid ? 'fbclid' : initialAttribution.gclid ? 'gclid' : initialAttribution.ttclid ? 'ttclid' : undefined,
|
|
99
|
+
lyr: initialAttribution.lyr,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
85
102
|
// Initialize auto-events manager (asynchronously to avoid blocking)
|
|
86
103
|
if (this.state.config.enableAutoEvents) {
|
|
87
104
|
this.autoEventsManager = new AutoEventsManager(this.track.bind(this), this.state.config.autoEventConfig);
|
|
@@ -132,9 +149,15 @@ export class DatalyrSDK {
|
|
|
132
149
|
if (((_c = config.tiktok) === null || _c === void 0 ? void 0 : _c.appId) && ((_d = config.tiktok) === null || _d === void 0 ? void 0 : _d.tiktokAppId)) {
|
|
133
150
|
await tiktokIntegration.initialize(config.tiktok, config.debug);
|
|
134
151
|
}
|
|
152
|
+
// Initialize Apple Search Ads attribution (iOS only, auto-fetches on init)
|
|
153
|
+
await appleSearchAdsIntegration.initialize(config.debug);
|
|
154
|
+
// Initialize Google Play Install Referrer (Android only)
|
|
155
|
+
await playInstallReferrerIntegration.initialize();
|
|
135
156
|
debugLog('Platform integrations initialized', {
|
|
136
157
|
meta: metaIntegration.isAvailable(),
|
|
137
158
|
tiktok: tiktokIntegration.isAvailable(),
|
|
159
|
+
appleSearchAds: appleSearchAdsIntegration.isAvailable(),
|
|
160
|
+
playInstallReferrer: playInstallReferrerIntegration.isAvailable(),
|
|
138
161
|
});
|
|
139
162
|
// SDK initialized successfully - set state before tracking install event
|
|
140
163
|
this.state.initialized = true;
|
|
@@ -395,6 +418,7 @@ export class DatalyrSDK {
|
|
|
395
418
|
currentUserId: this.state.currentUserId,
|
|
396
419
|
queueStats: this.eventQueue.getStats(),
|
|
397
420
|
attribution: attributionManager.getAttributionSummary(),
|
|
421
|
+
journey: journeyManager.getJourneySummary(),
|
|
398
422
|
};
|
|
399
423
|
}
|
|
400
424
|
/**
|
|
@@ -404,10 +428,28 @@ export class DatalyrSDK {
|
|
|
404
428
|
return this.state.anonymousId;
|
|
405
429
|
}
|
|
406
430
|
/**
|
|
407
|
-
* Get detailed attribution data
|
|
431
|
+
* Get detailed attribution data (includes journey tracking data)
|
|
408
432
|
*/
|
|
409
433
|
getAttributionData() {
|
|
410
|
-
|
|
434
|
+
const attribution = attributionManager.getAttributionData();
|
|
435
|
+
const journeyData = journeyManager.getAttributionData();
|
|
436
|
+
// Merge attribution with journey data
|
|
437
|
+
return {
|
|
438
|
+
...attribution,
|
|
439
|
+
...journeyData,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Get journey tracking summary
|
|
444
|
+
*/
|
|
445
|
+
getJourneySummary() {
|
|
446
|
+
return journeyManager.getJourneySummary();
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Get full customer journey (all touchpoints)
|
|
450
|
+
*/
|
|
451
|
+
getJourney() {
|
|
452
|
+
return journeyManager.getJourney();
|
|
411
453
|
}
|
|
412
454
|
/**
|
|
413
455
|
* Set custom attribution data (for testing or manual attribution)
|
|
@@ -457,22 +499,25 @@ export class DatalyrSDK {
|
|
|
457
499
|
// MARK: - SKAdNetwork Enhanced Methods
|
|
458
500
|
/**
|
|
459
501
|
* Track event with automatic SKAdNetwork conversion value encoding
|
|
502
|
+
* Uses SKAN 4.0 on iOS 16.1+ with coarse values and lock window support
|
|
460
503
|
*/
|
|
461
504
|
async trackWithSKAdNetwork(event, properties) {
|
|
462
505
|
// Existing tracking (keep exactly as-is)
|
|
463
506
|
await this.track(event, properties);
|
|
464
|
-
//
|
|
507
|
+
// Automatic SKAdNetwork encoding with SKAN 4.0 support
|
|
465
508
|
if (!DatalyrSDK.conversionEncoder) {
|
|
466
509
|
if (DatalyrSDK.debugEnabled) {
|
|
467
510
|
errorLog('SKAdNetwork encoder not initialized. Pass skadTemplate in initialize()');
|
|
468
511
|
}
|
|
469
512
|
return;
|
|
470
513
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
514
|
+
// Use SKAN 4.0 encoding (includes coarse value and lock window)
|
|
515
|
+
const result = DatalyrSDK.conversionEncoder.encodeWithSKAN4(event, properties);
|
|
516
|
+
if (result.fineValue > 0 || result.priority > 0) {
|
|
517
|
+
// Use SKAN 4.0 method (automatically falls back to SKAN 3.0 on older iOS)
|
|
518
|
+
const success = await SKAdNetworkBridge.updatePostbackConversionValue(result);
|
|
474
519
|
if (DatalyrSDK.debugEnabled) {
|
|
475
|
-
debugLog(`
|
|
520
|
+
debugLog(`SKAN: event=${event}, fine=${result.fineValue}, coarse=${result.coarseValue}, lock=${result.lockWindow}, success=${success}`, properties);
|
|
476
521
|
}
|
|
477
522
|
}
|
|
478
523
|
else if (DatalyrSDK.debugEnabled) {
|
|
@@ -672,8 +717,25 @@ export class DatalyrSDK {
|
|
|
672
717
|
return {
|
|
673
718
|
meta: metaIntegration.isAvailable(),
|
|
674
719
|
tiktok: tiktokIntegration.isAvailable(),
|
|
720
|
+
appleSearchAds: appleSearchAdsIntegration.isAvailable(),
|
|
721
|
+
playInstallReferrer: playInstallReferrerIntegration.isAvailable(),
|
|
675
722
|
};
|
|
676
723
|
}
|
|
724
|
+
/**
|
|
725
|
+
* Get Apple Search Ads attribution data
|
|
726
|
+
* Returns attribution if user installed via Apple Search Ads, null otherwise
|
|
727
|
+
*/
|
|
728
|
+
getAppleSearchAdsAttribution() {
|
|
729
|
+
return appleSearchAdsIntegration.getAttributionData();
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Get Google Play Install Referrer attribution data (Android only)
|
|
733
|
+
* Returns referrer data if available, null otherwise
|
|
734
|
+
*/
|
|
735
|
+
getPlayInstallReferrer() {
|
|
736
|
+
const data = playInstallReferrerIntegration.getReferrerData();
|
|
737
|
+
return data ? playInstallReferrerIntegration.getAttributionData() : null;
|
|
738
|
+
}
|
|
677
739
|
/**
|
|
678
740
|
* Update tracking authorization status on all platform SDKs
|
|
679
741
|
* Call this AFTER the user responds to the ATT permission dialog
|
|
@@ -745,6 +807,21 @@ export class DatalyrSDK {
|
|
|
745
807
|
const deviceInfo = await getDeviceInfo();
|
|
746
808
|
const fingerprintData = await createFingerprintData();
|
|
747
809
|
const attributionData = attributionManager.getAttributionData();
|
|
810
|
+
// Get Apple Search Ads attribution if available
|
|
811
|
+
const asaAttribution = appleSearchAdsIntegration.getAttributionData();
|
|
812
|
+
const asaData = (asaAttribution === null || asaAttribution === void 0 ? void 0 : asaAttribution.attribution) ? {
|
|
813
|
+
asa_campaign_id: asaAttribution.campaignId,
|
|
814
|
+
asa_campaign_name: asaAttribution.campaignName,
|
|
815
|
+
asa_ad_group_id: asaAttribution.adGroupId,
|
|
816
|
+
asa_ad_group_name: asaAttribution.adGroupName,
|
|
817
|
+
asa_keyword_id: asaAttribution.keywordId,
|
|
818
|
+
asa_keyword: asaAttribution.keyword,
|
|
819
|
+
asa_org_id: asaAttribution.orgId,
|
|
820
|
+
asa_org_name: asaAttribution.orgName,
|
|
821
|
+
asa_click_date: asaAttribution.clickDate,
|
|
822
|
+
asa_conversion_type: asaAttribution.conversionType,
|
|
823
|
+
asa_country_or_region: asaAttribution.countryOrRegion,
|
|
824
|
+
} : {};
|
|
748
825
|
const payload = {
|
|
749
826
|
workspaceId: this.state.config.workspaceId || 'mobile_sdk',
|
|
750
827
|
visitorId: this.state.visitorId,
|
|
@@ -766,6 +843,8 @@ export class DatalyrSDK {
|
|
|
766
843
|
timestamp: Date.now(),
|
|
767
844
|
// Attribution data
|
|
768
845
|
...attributionData,
|
|
846
|
+
// Apple Search Ads attribution
|
|
847
|
+
...asaData,
|
|
769
848
|
},
|
|
770
849
|
fingerprintData,
|
|
771
850
|
source: 'mobile_app',
|
|
@@ -999,6 +1078,9 @@ export class Datalyr {
|
|
|
999
1078
|
static getPlatformIntegrationStatus() {
|
|
1000
1079
|
return datalyr.getPlatformIntegrationStatus();
|
|
1001
1080
|
}
|
|
1081
|
+
static getAppleSearchAdsAttribution() {
|
|
1082
|
+
return datalyr.getAppleSearchAdsAttribution();
|
|
1083
|
+
}
|
|
1002
1084
|
static async updateTrackingAuthorization(enabled) {
|
|
1003
1085
|
await datalyr.updateTrackingAuthorization(enabled);
|
|
1004
1086
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export declare const datalyr: DatalyrSDK;
|
|
|
3
3
|
export { Datalyr };
|
|
4
4
|
export * from './types';
|
|
5
5
|
export { attributionManager } from './attribution';
|
|
6
|
+
export { journeyManager } from './journey';
|
|
7
|
+
export type { TouchAttribution, TouchPoint } from './journey';
|
|
6
8
|
export { createAutoEventsManager, AutoEventsManager } from './auto-events';
|
|
7
9
|
export * from './utils';
|
|
8
10
|
export * from './http-client';
|
|
@@ -10,5 +12,6 @@ export * from './event-queue';
|
|
|
10
12
|
export { DatalyrSDK };
|
|
11
13
|
export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
12
14
|
export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
13
|
-
export { metaIntegration, tiktokIntegration } from './integrations';
|
|
15
|
+
export { metaIntegration, tiktokIntegration, appleSearchAdsIntegration } from './integrations';
|
|
16
|
+
export type { AppleSearchAdsAttribution } from './native/DatalyrNativeBridge';
|
|
14
17
|
export default DatalyrSDK;
|
package/lib/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export { Datalyr };
|
|
|
8
8
|
// Export types and utilities
|
|
9
9
|
export * from './types';
|
|
10
10
|
export { attributionManager } from './attribution';
|
|
11
|
+
export { journeyManager } from './journey';
|
|
11
12
|
export { createAutoEventsManager, AutoEventsManager } from './auto-events';
|
|
12
13
|
// Re-export utilities for advanced usage
|
|
13
14
|
export * from './utils';
|
|
@@ -19,6 +20,6 @@ export { DatalyrSDK };
|
|
|
19
20
|
export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
20
21
|
export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
21
22
|
// Export platform integrations
|
|
22
|
-
export { metaIntegration, tiktokIntegration } from './integrations';
|
|
23
|
+
export { metaIntegration, tiktokIntegration, appleSearchAdsIntegration } from './integrations';
|
|
23
24
|
// Default export for compatibility
|
|
24
25
|
export default DatalyrSDK;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apple Search Ads Attribution Integration
|
|
3
|
+
* Uses AdServices framework (iOS 14.3+) to capture attribution from App Store search ads
|
|
4
|
+
*/
|
|
5
|
+
import { AppleSearchAdsAttribution } from '../native/DatalyrNativeBridge';
|
|
6
|
+
/**
|
|
7
|
+
* Apple Search Ads Integration class
|
|
8
|
+
* Fetches attribution data for users who installed via Apple Search Ads
|
|
9
|
+
*/
|
|
10
|
+
export declare class AppleSearchAdsIntegration {
|
|
11
|
+
private attributionData;
|
|
12
|
+
private fetched;
|
|
13
|
+
private available;
|
|
14
|
+
private debug;
|
|
15
|
+
/**
|
|
16
|
+
* Initialize and fetch Apple Search Ads attribution
|
|
17
|
+
*/
|
|
18
|
+
initialize(debug?: boolean): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Fetch attribution data from Apple's AdServices API
|
|
21
|
+
* Call this during app initialization
|
|
22
|
+
*/
|
|
23
|
+
fetchAttribution(): Promise<AppleSearchAdsAttribution | null>;
|
|
24
|
+
/**
|
|
25
|
+
* Get cached attribution data
|
|
26
|
+
*/
|
|
27
|
+
getAttributionData(): AppleSearchAdsAttribution | null;
|
|
28
|
+
/**
|
|
29
|
+
* Check if user came from Apple Search Ads
|
|
30
|
+
*/
|
|
31
|
+
hasAttribution(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Check if Apple Search Ads is available (iOS 14.3+)
|
|
34
|
+
*/
|
|
35
|
+
isAvailable(): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Check if attribution has been fetched
|
|
38
|
+
*/
|
|
39
|
+
hasFetched(): boolean;
|
|
40
|
+
private log;
|
|
41
|
+
private logError;
|
|
42
|
+
}
|
|
43
|
+
export declare const appleSearchAdsIntegration: AppleSearchAdsIntegration;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apple Search Ads Attribution Integration
|
|
3
|
+
* Uses AdServices framework (iOS 14.3+) to capture attribution from App Store search ads
|
|
4
|
+
*/
|
|
5
|
+
import { Platform } from 'react-native';
|
|
6
|
+
import { AppleSearchAdsNativeBridge, isNativeModuleAvailable } from '../native/DatalyrNativeBridge';
|
|
7
|
+
/**
|
|
8
|
+
* Apple Search Ads Integration class
|
|
9
|
+
* Fetches attribution data for users who installed via Apple Search Ads
|
|
10
|
+
*/
|
|
11
|
+
export class AppleSearchAdsIntegration {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.attributionData = null;
|
|
14
|
+
this.fetched = false;
|
|
15
|
+
this.available = false;
|
|
16
|
+
this.debug = false;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Initialize and fetch Apple Search Ads attribution
|
|
20
|
+
*/
|
|
21
|
+
async initialize(debug = false) {
|
|
22
|
+
this.debug = debug;
|
|
23
|
+
// Only available on iOS via native module
|
|
24
|
+
if (Platform.OS !== 'ios') {
|
|
25
|
+
this.log('Apple Search Ads only available on iOS');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
this.available = isNativeModuleAvailable();
|
|
29
|
+
if (!this.available) {
|
|
30
|
+
this.log('Apple Search Ads native module not available');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Automatically fetch attribution on init
|
|
34
|
+
await this.fetchAttribution();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Fetch attribution data from Apple's AdServices API
|
|
38
|
+
* Call this during app initialization
|
|
39
|
+
*/
|
|
40
|
+
async fetchAttribution() {
|
|
41
|
+
var _a;
|
|
42
|
+
if (!this.available) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
// Only fetch once
|
|
46
|
+
if (this.fetched) {
|
|
47
|
+
return this.attributionData;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
this.attributionData = await AppleSearchAdsNativeBridge.getAttribution();
|
|
51
|
+
this.fetched = true;
|
|
52
|
+
if ((_a = this.attributionData) === null || _a === void 0 ? void 0 : _a.attribution) {
|
|
53
|
+
this.log('Apple Search Ads attribution found:', {
|
|
54
|
+
campaignId: this.attributionData.campaignId,
|
|
55
|
+
campaignName: this.attributionData.campaignName,
|
|
56
|
+
adGroupId: this.attributionData.adGroupId,
|
|
57
|
+
keyword: this.attributionData.keyword,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
this.log('No Apple Search Ads attribution (user did not come from search ad)');
|
|
62
|
+
}
|
|
63
|
+
return this.attributionData;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
this.logError('Failed to fetch Apple Search Ads attribution:', error);
|
|
67
|
+
this.fetched = true;
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get cached attribution data
|
|
73
|
+
*/
|
|
74
|
+
getAttributionData() {
|
|
75
|
+
return this.attributionData;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Check if user came from Apple Search Ads
|
|
79
|
+
*/
|
|
80
|
+
hasAttribution() {
|
|
81
|
+
var _a;
|
|
82
|
+
return ((_a = this.attributionData) === null || _a === void 0 ? void 0 : _a.attribution) === true;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Check if Apple Search Ads is available (iOS 14.3+)
|
|
86
|
+
*/
|
|
87
|
+
isAvailable() {
|
|
88
|
+
return this.available;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check if attribution has been fetched
|
|
92
|
+
*/
|
|
93
|
+
hasFetched() {
|
|
94
|
+
return this.fetched;
|
|
95
|
+
}
|
|
96
|
+
log(message, data) {
|
|
97
|
+
if (this.debug) {
|
|
98
|
+
console.log(`[Datalyr/AppleSearchAds] ${message}`, data || '');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
logError(message, error) {
|
|
102
|
+
console.error(`[Datalyr/AppleSearchAds] ${message}`, error);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Export singleton instance
|
|
106
|
+
export const appleSearchAdsIntegration = new AppleSearchAdsIntegration();
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Platform SDK Integrations
|
|
3
|
-
* Meta (Facebook)
|
|
3
|
+
* Meta (Facebook), TikTok, Apple Search Ads, and Google Play Install Referrer SDK wrappers
|
|
4
4
|
*/
|
|
5
5
|
export { MetaIntegration, metaIntegration } from './meta-integration';
|
|
6
6
|
export { TikTokIntegration, tiktokIntegration } from './tiktok-integration';
|
|
7
|
+
export { AppleSearchAdsIntegration, appleSearchAdsIntegration } from './apple-search-ads-integration';
|
|
8
|
+
export { playInstallReferrerIntegration } from './play-install-referrer';
|
|
9
|
+
export type { PlayInstallReferrer } from './play-install-referrer';
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Platform SDK Integrations
|
|
3
|
-
* Meta (Facebook)
|
|
3
|
+
* Meta (Facebook), TikTok, Apple Search Ads, and Google Play Install Referrer SDK wrappers
|
|
4
4
|
*/
|
|
5
5
|
export { MetaIntegration, metaIntegration } from './meta-integration';
|
|
6
6
|
export { TikTokIntegration, tiktokIntegration } from './tiktok-integration';
|
|
7
|
+
export { AppleSearchAdsIntegration, appleSearchAdsIntegration } from './apple-search-ads-integration';
|
|
8
|
+
export { playInstallReferrerIntegration } from './play-install-referrer';
|
|
@@ -18,14 +18,15 @@ export class MetaIntegration {
|
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Initialize Meta SDK with configuration
|
|
21
|
+
* Supported on both iOS and Android via native modules
|
|
21
22
|
*/
|
|
22
23
|
async initialize(config, debug = false) {
|
|
23
24
|
var _a;
|
|
24
25
|
this.debug = debug;
|
|
25
26
|
this.config = config;
|
|
26
|
-
// Only available on iOS via native
|
|
27
|
-
if (Platform.OS !== 'ios') {
|
|
28
|
-
this.log('Meta SDK only available on iOS');
|
|
27
|
+
// Only available on iOS and Android via native modules
|
|
28
|
+
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
|
|
29
|
+
this.log('Meta SDK only available on iOS and Android');
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
32
|
this.available = isNativeModuleAvailable();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Play Install Referrer Integration
|
|
3
|
+
*
|
|
4
|
+
* Provides Android install attribution via the Google Play Install Referrer API.
|
|
5
|
+
* This captures UTM parameters and click IDs passed through Google Play Store.
|
|
6
|
+
*
|
|
7
|
+
* How it works:
|
|
8
|
+
* 1. User clicks ad/link with UTM parameters
|
|
9
|
+
* 2. Google Play Store stores the referrer URL
|
|
10
|
+
* 3. On first app launch, SDK retrieves the referrer
|
|
11
|
+
* 4. Attribution data (utm_source, utm_medium, gclid, etc.) is extracted
|
|
12
|
+
*
|
|
13
|
+
* Requirements:
|
|
14
|
+
* - Android only (returns null on iOS)
|
|
15
|
+
* - Requires Google Play Install Referrer Library in build.gradle:
|
|
16
|
+
* implementation 'com.android.installreferrer:installreferrer:2.2'
|
|
17
|
+
*
|
|
18
|
+
* Attribution data captured:
|
|
19
|
+
* - referrer_url: Full referrer URL from Play Store
|
|
20
|
+
* - referrer_click_timestamp: When the referrer link was clicked
|
|
21
|
+
* - install_begin_timestamp: When the install began
|
|
22
|
+
* - gclid: Google Ads click ID (if present)
|
|
23
|
+
* - utm_source, utm_medium, utm_campaign, etc.
|
|
24
|
+
*/
|
|
25
|
+
export interface PlayInstallReferrer {
|
|
26
|
+
referrerUrl: string;
|
|
27
|
+
referrerClickTimestamp: number;
|
|
28
|
+
installBeginTimestamp: number;
|
|
29
|
+
installCompleteTimestamp?: number;
|
|
30
|
+
gclid?: string;
|
|
31
|
+
utmSource?: string;
|
|
32
|
+
utmMedium?: string;
|
|
33
|
+
utmCampaign?: string;
|
|
34
|
+
utmTerm?: string;
|
|
35
|
+
utmContent?: string;
|
|
36
|
+
[key: string]: any;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Google Play Install Referrer Integration
|
|
40
|
+
*
|
|
41
|
+
* Retrieves install attribution data from Google Play Store.
|
|
42
|
+
* Only available on Android.
|
|
43
|
+
*/
|
|
44
|
+
declare class PlayInstallReferrerIntegration {
|
|
45
|
+
private referrerData;
|
|
46
|
+
private initialized;
|
|
47
|
+
/**
|
|
48
|
+
* Check if Play Install Referrer is available
|
|
49
|
+
*/
|
|
50
|
+
isAvailable(): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Initialize and fetch install referrer data
|
|
53
|
+
* Should be called once on first app launch
|
|
54
|
+
*/
|
|
55
|
+
initialize(): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Fetch install referrer from Play Store
|
|
58
|
+
*/
|
|
59
|
+
fetchInstallReferrer(): Promise<PlayInstallReferrer | null>;
|
|
60
|
+
/**
|
|
61
|
+
* Parse referrer URL to extract UTM parameters and click IDs
|
|
62
|
+
*/
|
|
63
|
+
private parseReferrerUrl;
|
|
64
|
+
/**
|
|
65
|
+
* Get cached install referrer data
|
|
66
|
+
*/
|
|
67
|
+
getReferrerData(): PlayInstallReferrer | null;
|
|
68
|
+
/**
|
|
69
|
+
* Get attribution data in standard format
|
|
70
|
+
*/
|
|
71
|
+
getAttributionData(): Record<string, any>;
|
|
72
|
+
}
|
|
73
|
+
export declare const playInstallReferrerIntegration: PlayInstallReferrerIntegration;
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Play Install Referrer Integration
|
|
3
|
+
*
|
|
4
|
+
* Provides Android install attribution via the Google Play Install Referrer API.
|
|
5
|
+
* This captures UTM parameters and click IDs passed through Google Play Store.
|
|
6
|
+
*
|
|
7
|
+
* How it works:
|
|
8
|
+
* 1. User clicks ad/link with UTM parameters
|
|
9
|
+
* 2. Google Play Store stores the referrer URL
|
|
10
|
+
* 3. On first app launch, SDK retrieves the referrer
|
|
11
|
+
* 4. Attribution data (utm_source, utm_medium, gclid, etc.) is extracted
|
|
12
|
+
*
|
|
13
|
+
* Requirements:
|
|
14
|
+
* - Android only (returns null on iOS)
|
|
15
|
+
* - Requires Google Play Install Referrer Library in build.gradle:
|
|
16
|
+
* implementation 'com.android.installreferrer:installreferrer:2.2'
|
|
17
|
+
*
|
|
18
|
+
* Attribution data captured:
|
|
19
|
+
* - referrer_url: Full referrer URL from Play Store
|
|
20
|
+
* - referrer_click_timestamp: When the referrer link was clicked
|
|
21
|
+
* - install_begin_timestamp: When the install began
|
|
22
|
+
* - gclid: Google Ads click ID (if present)
|
|
23
|
+
* - utm_source, utm_medium, utm_campaign, etc.
|
|
24
|
+
*/
|
|
25
|
+
import { Platform, NativeModules } from 'react-native';
|
|
26
|
+
import { debugLog, errorLog } from '../utils';
|
|
27
|
+
const { DatalyrPlayInstallReferrer } = NativeModules;
|
|
28
|
+
/**
|
|
29
|
+
* Google Play Install Referrer Integration
|
|
30
|
+
*
|
|
31
|
+
* Retrieves install attribution data from Google Play Store.
|
|
32
|
+
* Only available on Android.
|
|
33
|
+
*/
|
|
34
|
+
class PlayInstallReferrerIntegration {
|
|
35
|
+
constructor() {
|
|
36
|
+
this.referrerData = null;
|
|
37
|
+
this.initialized = false;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Check if Play Install Referrer is available
|
|
41
|
+
*/
|
|
42
|
+
isAvailable() {
|
|
43
|
+
return Platform.OS === 'android' && !!DatalyrPlayInstallReferrer;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Initialize and fetch install referrer data
|
|
47
|
+
* Should be called once on first app launch
|
|
48
|
+
*/
|
|
49
|
+
async initialize() {
|
|
50
|
+
if (this.initialized)
|
|
51
|
+
return;
|
|
52
|
+
if (!this.isAvailable()) {
|
|
53
|
+
debugLog('[PlayInstallReferrer] Not available (iOS or native module missing)');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
this.referrerData = await this.fetchInstallReferrer();
|
|
58
|
+
this.initialized = true;
|
|
59
|
+
if (this.referrerData) {
|
|
60
|
+
debugLog('[PlayInstallReferrer] Install referrer fetched:', {
|
|
61
|
+
utmSource: this.referrerData.utmSource,
|
|
62
|
+
utmMedium: this.referrerData.utmMedium,
|
|
63
|
+
hasGclid: !!this.referrerData.gclid,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
errorLog('[PlayInstallReferrer] Failed to initialize:', error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Fetch install referrer from Play Store
|
|
73
|
+
*/
|
|
74
|
+
async fetchInstallReferrer() {
|
|
75
|
+
if (!this.isAvailable()) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const referrer = await DatalyrPlayInstallReferrer.getInstallReferrer();
|
|
80
|
+
if (!referrer) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
// Parse UTM parameters from referrer URL
|
|
84
|
+
const parsed = this.parseReferrerUrl(referrer.referrerUrl);
|
|
85
|
+
return {
|
|
86
|
+
...referrer,
|
|
87
|
+
...parsed,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
errorLog('[PlayInstallReferrer] Error fetching referrer:', error);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Parse referrer URL to extract UTM parameters and click IDs
|
|
97
|
+
*/
|
|
98
|
+
parseReferrerUrl(referrerUrl) {
|
|
99
|
+
const params = {};
|
|
100
|
+
if (!referrerUrl)
|
|
101
|
+
return params;
|
|
102
|
+
try {
|
|
103
|
+
// Referrer URL is URL-encoded, decode it first
|
|
104
|
+
const decoded = decodeURIComponent(referrerUrl);
|
|
105
|
+
const searchParams = new URLSearchParams(decoded);
|
|
106
|
+
// Extract UTM parameters
|
|
107
|
+
params.utmSource = searchParams.get('utm_source') || undefined;
|
|
108
|
+
params.utmMedium = searchParams.get('utm_medium') || undefined;
|
|
109
|
+
params.utmCampaign = searchParams.get('utm_campaign') || undefined;
|
|
110
|
+
params.utmTerm = searchParams.get('utm_term') || undefined;
|
|
111
|
+
params.utmContent = searchParams.get('utm_content') || undefined;
|
|
112
|
+
// Extract click IDs
|
|
113
|
+
params.gclid = searchParams.get('gclid') || undefined;
|
|
114
|
+
// Store any additional parameters
|
|
115
|
+
searchParams.forEach((value, key) => {
|
|
116
|
+
if (!key.startsWith('utm_') && key !== 'gclid') {
|
|
117
|
+
params[key] = value;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
debugLog('[PlayInstallReferrer] Parsed referrer URL:', params);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
errorLog('[PlayInstallReferrer] Error parsing referrer URL:', error);
|
|
124
|
+
}
|
|
125
|
+
return params;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get cached install referrer data
|
|
129
|
+
*/
|
|
130
|
+
getReferrerData() {
|
|
131
|
+
return this.referrerData;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get attribution data in standard format
|
|
135
|
+
*/
|
|
136
|
+
getAttributionData() {
|
|
137
|
+
if (!this.referrerData)
|
|
138
|
+
return {};
|
|
139
|
+
return {
|
|
140
|
+
// Install referrer specific
|
|
141
|
+
install_referrer_url: this.referrerData.referrerUrl,
|
|
142
|
+
referrer_click_timestamp: this.referrerData.referrerClickTimestamp,
|
|
143
|
+
install_begin_timestamp: this.referrerData.installBeginTimestamp,
|
|
144
|
+
// Standard attribution fields
|
|
145
|
+
gclid: this.referrerData.gclid,
|
|
146
|
+
utm_source: this.referrerData.utmSource,
|
|
147
|
+
utm_medium: this.referrerData.utmMedium,
|
|
148
|
+
utm_campaign: this.referrerData.utmCampaign,
|
|
149
|
+
utm_term: this.referrerData.utmTerm,
|
|
150
|
+
utm_content: this.referrerData.utmContent,
|
|
151
|
+
// Source indicators
|
|
152
|
+
attribution_source: 'play_install_referrer',
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
export const playInstallReferrerIntegration = new PlayInstallReferrerIntegration();
|