@datalyr/react-native 1.1.1 → 1.2.1
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 +31 -140
- package/LICENSE +21 -0
- package/README.md +434 -217
- package/datalyr-react-native.podspec +31 -0
- package/ios/DatalyrNative.m +74 -0
- package/ios/DatalyrNative.swift +332 -0
- package/ios/DatalyrSKAdNetwork.m +26 -0
- package/lib/datalyr-sdk.d.ts +73 -3
- package/lib/datalyr-sdk.js +353 -3
- package/lib/index.d.ts +2 -0
- package/lib/index.js +4 -2
- 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 +7 -0
- package/lib/integrations/index.js +7 -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 +57 -0
- package/lib/native/DatalyrNativeBridge.js +187 -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 +11 -5
- package/src/datalyr-sdk-expo.ts +997 -0
- package/src/datalyr-sdk.ts +455 -19
- package/src/expo.ts +42 -18
- package/src/index.ts +8 -2
- package/src/integrations/apple-search-ads-integration.ts +119 -0
- package/src/integrations/index.ts +8 -0
- package/src/integrations/meta-integration.ts +238 -0
- package/src/integrations/tiktok-integration.ts +360 -0
- package/src/native/DatalyrNativeBridge.ts +313 -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
package/lib/datalyr-sdk.js
CHANGED
|
@@ -6,6 +6,7 @@ import { attributionManager } from './attribution';
|
|
|
6
6
|
import { AutoEventsManager } from './auto-events';
|
|
7
7
|
import { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
8
8
|
import { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
9
|
+
import { metaIntegration, tiktokIntegration, appleSearchAdsIntegration } from './integrations';
|
|
9
10
|
export class DatalyrSDK {
|
|
10
11
|
constructor() {
|
|
11
12
|
this.autoEventsManager = null;
|
|
@@ -41,7 +42,7 @@ export class DatalyrSDK {
|
|
|
41
42
|
* Initialize the SDK with configuration
|
|
42
43
|
*/
|
|
43
44
|
async initialize(config) {
|
|
44
|
-
var _a;
|
|
45
|
+
var _a, _b, _c, _d;
|
|
45
46
|
try {
|
|
46
47
|
debugLog('Initializing Datalyr SDK...', { workspaceId: config.workspaceId });
|
|
47
48
|
// Validate configuration
|
|
@@ -116,6 +117,28 @@ export class DatalyrSDK {
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
}
|
|
120
|
+
// Initialize Meta SDK if configured
|
|
121
|
+
if ((_b = config.meta) === null || _b === void 0 ? void 0 : _b.appId) {
|
|
122
|
+
await metaIntegration.initialize(config.meta, config.debug);
|
|
123
|
+
// Fetch deferred deep link and merge with attribution
|
|
124
|
+
if (config.enableAttribution !== false) {
|
|
125
|
+
const deferredLink = await metaIntegration.fetchDeferredDeepLink();
|
|
126
|
+
if (deferredLink) {
|
|
127
|
+
await this.handleDeferredDeepLink(deferredLink);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Initialize TikTok SDK if configured
|
|
132
|
+
if (((_c = config.tiktok) === null || _c === void 0 ? void 0 : _c.appId) && ((_d = config.tiktok) === null || _d === void 0 ? void 0 : _d.tiktokAppId)) {
|
|
133
|
+
await tiktokIntegration.initialize(config.tiktok, config.debug);
|
|
134
|
+
}
|
|
135
|
+
// Initialize Apple Search Ads attribution (iOS only, auto-fetches on init)
|
|
136
|
+
await appleSearchAdsIntegration.initialize(config.debug);
|
|
137
|
+
debugLog('Platform integrations initialized', {
|
|
138
|
+
meta: metaIntegration.isAvailable(),
|
|
139
|
+
tiktok: tiktokIntegration.isAvailable(),
|
|
140
|
+
appleSearchAds: appleSearchAdsIntegration.isAvailable(),
|
|
141
|
+
});
|
|
119
142
|
// SDK initialized successfully - set state before tracking install event
|
|
120
143
|
this.state.initialized = true;
|
|
121
144
|
// Check for app install (after SDK is marked as initialized)
|
|
@@ -207,6 +230,36 @@ export class DatalyrSDK {
|
|
|
207
230
|
await this.fetchAndMergeWebAttribution(email);
|
|
208
231
|
}
|
|
209
232
|
}
|
|
233
|
+
// Forward user data to platform SDKs for Advanced Matching
|
|
234
|
+
const email = properties === null || properties === void 0 ? void 0 : properties.email;
|
|
235
|
+
const phone = properties === null || properties === void 0 ? void 0 : properties.phone;
|
|
236
|
+
const firstName = ((properties === null || properties === void 0 ? void 0 : properties.first_name) || (properties === null || properties === void 0 ? void 0 : properties.firstName));
|
|
237
|
+
const lastName = ((properties === null || properties === void 0 ? void 0 : properties.last_name) || (properties === null || properties === void 0 ? void 0 : properties.lastName));
|
|
238
|
+
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));
|
|
239
|
+
const gender = properties === null || properties === void 0 ? void 0 : properties.gender;
|
|
240
|
+
const city = properties === null || properties === void 0 ? void 0 : properties.city;
|
|
241
|
+
const state = properties === null || properties === void 0 ? void 0 : properties.state;
|
|
242
|
+
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));
|
|
243
|
+
const country = properties === null || properties === void 0 ? void 0 : properties.country;
|
|
244
|
+
// Meta Advanced Matching
|
|
245
|
+
if (metaIntegration.isAvailable()) {
|
|
246
|
+
metaIntegration.setUserData({
|
|
247
|
+
email,
|
|
248
|
+
firstName,
|
|
249
|
+
lastName,
|
|
250
|
+
phone,
|
|
251
|
+
dateOfBirth,
|
|
252
|
+
gender,
|
|
253
|
+
city,
|
|
254
|
+
state,
|
|
255
|
+
zip,
|
|
256
|
+
country,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
// TikTok identification
|
|
260
|
+
if (tiktokIntegration.isAvailable()) {
|
|
261
|
+
tiktokIntegration.identify(email, phone, userId);
|
|
262
|
+
}
|
|
210
263
|
}
|
|
211
264
|
catch (error) {
|
|
212
265
|
errorLog('Error identifying user:', error);
|
|
@@ -310,6 +363,10 @@ export class DatalyrSDK {
|
|
|
310
363
|
await Storage.removeItem(STORAGE_KEYS.USER_PROPERTIES);
|
|
311
364
|
// Generate new session
|
|
312
365
|
this.state.sessionId = await getOrCreateSessionId();
|
|
366
|
+
// Clear user data from platform SDKs
|
|
367
|
+
if (metaIntegration.isAvailable()) {
|
|
368
|
+
metaIntegration.clearUserData();
|
|
369
|
+
}
|
|
313
370
|
debugLog('User data reset completed');
|
|
314
371
|
}
|
|
315
372
|
catch (error) {
|
|
@@ -426,22 +483,263 @@ export class DatalyrSDK {
|
|
|
426
483
|
}
|
|
427
484
|
}
|
|
428
485
|
/**
|
|
429
|
-
* Track purchase with automatic revenue encoding
|
|
486
|
+
* Track purchase with automatic revenue encoding and platform forwarding
|
|
430
487
|
*/
|
|
431
488
|
async trackPurchase(value, currency = 'USD', productId) {
|
|
432
489
|
const properties = { revenue: value, currency };
|
|
433
490
|
if (productId)
|
|
434
491
|
properties.product_id = productId;
|
|
435
492
|
await this.trackWithSKAdNetwork('purchase', properties);
|
|
493
|
+
// Forward to Meta if available
|
|
494
|
+
if (metaIntegration.isAvailable()) {
|
|
495
|
+
metaIntegration.logPurchase(value, currency, { fb_content_id: productId });
|
|
496
|
+
}
|
|
497
|
+
// Forward to TikTok if available
|
|
498
|
+
if (tiktokIntegration.isAvailable()) {
|
|
499
|
+
tiktokIntegration.logPurchase(value, currency, productId, 'product');
|
|
500
|
+
}
|
|
436
501
|
}
|
|
437
502
|
/**
|
|
438
|
-
* Track subscription with automatic revenue encoding
|
|
503
|
+
* Track subscription with automatic revenue encoding and platform forwarding
|
|
439
504
|
*/
|
|
440
505
|
async trackSubscription(value, currency = 'USD', plan) {
|
|
441
506
|
const properties = { revenue: value, currency };
|
|
442
507
|
if (plan)
|
|
443
508
|
properties.plan = plan;
|
|
444
509
|
await this.trackWithSKAdNetwork('subscribe', properties);
|
|
510
|
+
// Forward to Meta if available
|
|
511
|
+
if (metaIntegration.isAvailable()) {
|
|
512
|
+
metaIntegration.logEvent('Subscribe', value, { subscription_plan: plan });
|
|
513
|
+
}
|
|
514
|
+
// Forward to TikTok if available
|
|
515
|
+
if (tiktokIntegration.isAvailable()) {
|
|
516
|
+
tiktokIntegration.logSubscription(value, currency, plan);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
// MARK: - Standard E-commerce Events
|
|
520
|
+
/**
|
|
521
|
+
* Track add to cart event
|
|
522
|
+
*/
|
|
523
|
+
async trackAddToCart(value, currency = 'USD', productId, productName) {
|
|
524
|
+
const properties = { value, currency };
|
|
525
|
+
if (productId)
|
|
526
|
+
properties.product_id = productId;
|
|
527
|
+
if (productName)
|
|
528
|
+
properties.product_name = productName;
|
|
529
|
+
await this.trackWithSKAdNetwork('add_to_cart', properties);
|
|
530
|
+
// Forward to Meta
|
|
531
|
+
if (metaIntegration.isAvailable()) {
|
|
532
|
+
metaIntegration.logEvent('AddToCart', value, {
|
|
533
|
+
currency,
|
|
534
|
+
content_ids: productId ? [productId] : undefined,
|
|
535
|
+
content_name: productName,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
// Forward to TikTok
|
|
539
|
+
if (tiktokIntegration.isAvailable()) {
|
|
540
|
+
tiktokIntegration.logAddToCart(value, currency, productId, 'product');
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Track view content/product event
|
|
545
|
+
*/
|
|
546
|
+
async trackViewContent(contentId, contentName, contentType = 'product', value, currency) {
|
|
547
|
+
const properties = { content_type: contentType };
|
|
548
|
+
if (contentId)
|
|
549
|
+
properties.content_id = contentId;
|
|
550
|
+
if (contentName)
|
|
551
|
+
properties.content_name = contentName;
|
|
552
|
+
if (value !== undefined)
|
|
553
|
+
properties.value = value;
|
|
554
|
+
if (currency)
|
|
555
|
+
properties.currency = currency;
|
|
556
|
+
await this.track('view_content', properties);
|
|
557
|
+
// Forward to Meta
|
|
558
|
+
if (metaIntegration.isAvailable()) {
|
|
559
|
+
metaIntegration.logEvent('ViewContent', value, {
|
|
560
|
+
content_ids: contentId ? [contentId] : undefined,
|
|
561
|
+
content_name: contentName,
|
|
562
|
+
content_type: contentType,
|
|
563
|
+
currency,
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
// Forward to TikTok
|
|
567
|
+
if (tiktokIntegration.isAvailable()) {
|
|
568
|
+
tiktokIntegration.logViewContent(contentId, contentName, contentType);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Track initiate checkout event
|
|
573
|
+
*/
|
|
574
|
+
async trackInitiateCheckout(value, currency = 'USD', numItems, productIds) {
|
|
575
|
+
const properties = { value, currency };
|
|
576
|
+
if (numItems !== undefined)
|
|
577
|
+
properties.num_items = numItems;
|
|
578
|
+
if (productIds)
|
|
579
|
+
properties.product_ids = productIds;
|
|
580
|
+
await this.trackWithSKAdNetwork('initiate_checkout', properties);
|
|
581
|
+
// Forward to Meta
|
|
582
|
+
if (metaIntegration.isAvailable()) {
|
|
583
|
+
metaIntegration.logEvent('InitiateCheckout', value, {
|
|
584
|
+
currency,
|
|
585
|
+
num_items: numItems,
|
|
586
|
+
content_ids: productIds,
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
// Forward to TikTok
|
|
590
|
+
if (tiktokIntegration.isAvailable()) {
|
|
591
|
+
tiktokIntegration.logInitiateCheckout(value, currency, numItems);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Track complete registration event
|
|
596
|
+
*/
|
|
597
|
+
async trackCompleteRegistration(method) {
|
|
598
|
+
const properties = {};
|
|
599
|
+
if (method)
|
|
600
|
+
properties.method = method;
|
|
601
|
+
await this.trackWithSKAdNetwork('complete_registration', properties);
|
|
602
|
+
// Forward to Meta
|
|
603
|
+
if (metaIntegration.isAvailable()) {
|
|
604
|
+
metaIntegration.logEvent('CompleteRegistration', undefined, { registration_method: method });
|
|
605
|
+
}
|
|
606
|
+
// Forward to TikTok
|
|
607
|
+
if (tiktokIntegration.isAvailable()) {
|
|
608
|
+
tiktokIntegration.logCompleteRegistration(method);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Track search event
|
|
613
|
+
*/
|
|
614
|
+
async trackSearch(query, resultIds) {
|
|
615
|
+
const properties = { query };
|
|
616
|
+
if (resultIds)
|
|
617
|
+
properties.result_ids = resultIds;
|
|
618
|
+
await this.track('search', properties);
|
|
619
|
+
// Forward to Meta
|
|
620
|
+
if (metaIntegration.isAvailable()) {
|
|
621
|
+
metaIntegration.logEvent('Search', undefined, {
|
|
622
|
+
search_string: query,
|
|
623
|
+
content_ids: resultIds,
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
// Forward to TikTok
|
|
627
|
+
if (tiktokIntegration.isAvailable()) {
|
|
628
|
+
tiktokIntegration.logSearch(query);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Track lead/contact form submission
|
|
633
|
+
*/
|
|
634
|
+
async trackLead(value, currency) {
|
|
635
|
+
const properties = {};
|
|
636
|
+
if (value !== undefined)
|
|
637
|
+
properties.value = value;
|
|
638
|
+
if (currency)
|
|
639
|
+
properties.currency = currency;
|
|
640
|
+
await this.trackWithSKAdNetwork('lead', properties);
|
|
641
|
+
// Forward to Meta
|
|
642
|
+
if (metaIntegration.isAvailable()) {
|
|
643
|
+
metaIntegration.logEvent('Lead', value, { currency });
|
|
644
|
+
}
|
|
645
|
+
// Forward to TikTok
|
|
646
|
+
if (tiktokIntegration.isAvailable()) {
|
|
647
|
+
tiktokIntegration.logLead(value, currency);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Track add payment info event
|
|
652
|
+
*/
|
|
653
|
+
async trackAddPaymentInfo(success = true) {
|
|
654
|
+
await this.track('add_payment_info', { success });
|
|
655
|
+
// Forward to Meta
|
|
656
|
+
if (metaIntegration.isAvailable()) {
|
|
657
|
+
metaIntegration.logEvent('AddPaymentInfo', undefined, { success: success ? 1 : 0 });
|
|
658
|
+
}
|
|
659
|
+
// Forward to TikTok
|
|
660
|
+
if (tiktokIntegration.isAvailable()) {
|
|
661
|
+
tiktokIntegration.logAddPaymentInfo(success);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
// MARK: - Platform Integration Methods
|
|
665
|
+
/**
|
|
666
|
+
* Get deferred attribution data from platform SDKs
|
|
667
|
+
*/
|
|
668
|
+
getDeferredAttributionData() {
|
|
669
|
+
return metaIntegration.getDeferredDeepLinkData();
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Get platform integration status
|
|
673
|
+
*/
|
|
674
|
+
getPlatformIntegrationStatus() {
|
|
675
|
+
return {
|
|
676
|
+
meta: metaIntegration.isAvailable(),
|
|
677
|
+
tiktok: tiktokIntegration.isAvailable(),
|
|
678
|
+
appleSearchAds: appleSearchAdsIntegration.isAvailable(),
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Get Apple Search Ads attribution data
|
|
683
|
+
* Returns attribution if user installed via Apple Search Ads, null otherwise
|
|
684
|
+
*/
|
|
685
|
+
getAppleSearchAdsAttribution() {
|
|
686
|
+
return appleSearchAdsIntegration.getAttributionData();
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Update tracking authorization status on all platform SDKs
|
|
690
|
+
* Call this AFTER the user responds to the ATT permission dialog
|
|
691
|
+
*/
|
|
692
|
+
async updateTrackingAuthorization(enabled) {
|
|
693
|
+
if (!this.state.initialized) {
|
|
694
|
+
errorLog('SDK not initialized. Call initialize() first.');
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
metaIntegration.updateTrackingAuthorization(enabled);
|
|
698
|
+
tiktokIntegration.updateTrackingAuthorization(enabled);
|
|
699
|
+
// Track ATT status event
|
|
700
|
+
await this.track('$att_status', {
|
|
701
|
+
authorized: enabled,
|
|
702
|
+
status: enabled ? 3 : 2,
|
|
703
|
+
status_name: enabled ? 'authorized' : 'denied',
|
|
704
|
+
});
|
|
705
|
+
debugLog(`ATT status updated: ${enabled ? 'authorized' : 'denied'}`);
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Handle deferred deep link data from platform SDKs
|
|
709
|
+
*/
|
|
710
|
+
async handleDeferredDeepLink(data) {
|
|
711
|
+
try {
|
|
712
|
+
debugLog('Processing deferred deep link:', data);
|
|
713
|
+
// Track deferred attribution event
|
|
714
|
+
await this.track('$deferred_deep_link', {
|
|
715
|
+
url: data.url,
|
|
716
|
+
source: data.source,
|
|
717
|
+
fbclid: data.fbclid,
|
|
718
|
+
ttclid: data.ttclid,
|
|
719
|
+
utm_source: data.utmSource,
|
|
720
|
+
utm_medium: data.utmMedium,
|
|
721
|
+
utm_campaign: data.utmCampaign,
|
|
722
|
+
utm_content: data.utmContent,
|
|
723
|
+
utm_term: data.utmTerm,
|
|
724
|
+
campaign_id: data.campaignId,
|
|
725
|
+
adset_id: data.adsetId,
|
|
726
|
+
ad_id: data.adId,
|
|
727
|
+
});
|
|
728
|
+
// Merge into attribution manager
|
|
729
|
+
attributionManager.mergeWebAttribution({
|
|
730
|
+
fbclid: data.fbclid,
|
|
731
|
+
ttclid: data.ttclid,
|
|
732
|
+
utm_source: data.utmSource,
|
|
733
|
+
utm_medium: data.utmMedium,
|
|
734
|
+
utm_campaign: data.utmCampaign,
|
|
735
|
+
utm_content: data.utmContent,
|
|
736
|
+
utm_term: data.utmTerm,
|
|
737
|
+
});
|
|
738
|
+
debugLog('Deferred deep link processed successfully');
|
|
739
|
+
}
|
|
740
|
+
catch (error) {
|
|
741
|
+
errorLog('Error processing deferred deep link:', error);
|
|
742
|
+
}
|
|
445
743
|
}
|
|
446
744
|
/**
|
|
447
745
|
* Get conversion value for testing (doesn't send to Apple)
|
|
@@ -458,6 +756,21 @@ export class DatalyrSDK {
|
|
|
458
756
|
const deviceInfo = await getDeviceInfo();
|
|
459
757
|
const fingerprintData = await createFingerprintData();
|
|
460
758
|
const attributionData = attributionManager.getAttributionData();
|
|
759
|
+
// Get Apple Search Ads attribution if available
|
|
760
|
+
const asaAttribution = appleSearchAdsIntegration.getAttributionData();
|
|
761
|
+
const asaData = (asaAttribution === null || asaAttribution === void 0 ? void 0 : asaAttribution.attribution) ? {
|
|
762
|
+
asa_campaign_id: asaAttribution.campaignId,
|
|
763
|
+
asa_campaign_name: asaAttribution.campaignName,
|
|
764
|
+
asa_ad_group_id: asaAttribution.adGroupId,
|
|
765
|
+
asa_ad_group_name: asaAttribution.adGroupName,
|
|
766
|
+
asa_keyword_id: asaAttribution.keywordId,
|
|
767
|
+
asa_keyword: asaAttribution.keyword,
|
|
768
|
+
asa_org_id: asaAttribution.orgId,
|
|
769
|
+
asa_org_name: asaAttribution.orgName,
|
|
770
|
+
asa_click_date: asaAttribution.clickDate,
|
|
771
|
+
asa_conversion_type: asaAttribution.conversionType,
|
|
772
|
+
asa_country_or_region: asaAttribution.countryOrRegion,
|
|
773
|
+
} : {};
|
|
461
774
|
const payload = {
|
|
462
775
|
workspaceId: this.state.config.workspaceId || 'mobile_sdk',
|
|
463
776
|
visitorId: this.state.visitorId,
|
|
@@ -479,6 +792,8 @@ export class DatalyrSDK {
|
|
|
479
792
|
timestamp: Date.now(),
|
|
480
793
|
// Attribution data
|
|
481
794
|
...attributionData,
|
|
795
|
+
// Apple Search Ads attribution
|
|
796
|
+
...asaData,
|
|
482
797
|
},
|
|
483
798
|
fingerprintData,
|
|
484
799
|
source: 'mobile_app',
|
|
@@ -683,6 +998,41 @@ export class Datalyr {
|
|
|
683
998
|
static updateAutoEventsConfig(config) {
|
|
684
999
|
datalyr.updateAutoEventsConfig(config);
|
|
685
1000
|
}
|
|
1001
|
+
// Standard e-commerce events (all forward to Meta and TikTok)
|
|
1002
|
+
static async trackAddToCart(value, currency = 'USD', productId, productName) {
|
|
1003
|
+
await datalyr.trackAddToCart(value, currency, productId, productName);
|
|
1004
|
+
}
|
|
1005
|
+
static async trackViewContent(contentId, contentName, contentType = 'product', value, currency) {
|
|
1006
|
+
await datalyr.trackViewContent(contentId, contentName, contentType, value, currency);
|
|
1007
|
+
}
|
|
1008
|
+
static async trackInitiateCheckout(value, currency = 'USD', numItems, productIds) {
|
|
1009
|
+
await datalyr.trackInitiateCheckout(value, currency, numItems, productIds);
|
|
1010
|
+
}
|
|
1011
|
+
static async trackCompleteRegistration(method) {
|
|
1012
|
+
await datalyr.trackCompleteRegistration(method);
|
|
1013
|
+
}
|
|
1014
|
+
static async trackSearch(query, resultIds) {
|
|
1015
|
+
await datalyr.trackSearch(query, resultIds);
|
|
1016
|
+
}
|
|
1017
|
+
static async trackLead(value, currency) {
|
|
1018
|
+
await datalyr.trackLead(value, currency);
|
|
1019
|
+
}
|
|
1020
|
+
static async trackAddPaymentInfo(success = true) {
|
|
1021
|
+
await datalyr.trackAddPaymentInfo(success);
|
|
1022
|
+
}
|
|
1023
|
+
// Platform integration methods
|
|
1024
|
+
static getDeferredAttributionData() {
|
|
1025
|
+
return datalyr.getDeferredAttributionData();
|
|
1026
|
+
}
|
|
1027
|
+
static getPlatformIntegrationStatus() {
|
|
1028
|
+
return datalyr.getPlatformIntegrationStatus();
|
|
1029
|
+
}
|
|
1030
|
+
static getAppleSearchAdsAttribution() {
|
|
1031
|
+
return datalyr.getAppleSearchAdsAttribution();
|
|
1032
|
+
}
|
|
1033
|
+
static async updateTrackingAuthorization(enabled) {
|
|
1034
|
+
await datalyr.updateTrackingAuthorization(enabled);
|
|
1035
|
+
}
|
|
686
1036
|
}
|
|
687
1037
|
// Export default instance for backward compatibility
|
|
688
1038
|
export default datalyr;
|
package/lib/index.d.ts
CHANGED
|
@@ -10,4 +10,6 @@ export * from './event-queue';
|
|
|
10
10
|
export { DatalyrSDK };
|
|
11
11
|
export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
12
12
|
export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
13
|
+
export { metaIntegration, tiktokIntegration, appleSearchAdsIntegration } from './integrations';
|
|
14
|
+
export type { AppleSearchAdsAttribution } from './native/DatalyrNativeBridge';
|
|
13
15
|
export default DatalyrSDK;
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Main entry point for React Native CLI
|
|
2
|
-
// Import
|
|
2
|
+
// Import with: import { Datalyr } from '@datalyr/react-native';
|
|
3
3
|
import { DatalyrSDK, Datalyr } from './datalyr-sdk';
|
|
4
4
|
// Create singleton instance for easy usage
|
|
5
5
|
export const datalyr = new DatalyrSDK();
|
|
@@ -18,5 +18,7 @@ export { DatalyrSDK };
|
|
|
18
18
|
// Export SKAdNetwork components
|
|
19
19
|
export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
20
20
|
export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
21
|
-
//
|
|
21
|
+
// Export platform integrations
|
|
22
|
+
export { metaIntegration, tiktokIntegration, appleSearchAdsIntegration } from './integrations';
|
|
23
|
+
// Default export for compatibility
|
|
22
24
|
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();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform SDK Integrations
|
|
3
|
+
* Meta (Facebook), TikTok, and Apple Search Ads SDK wrappers
|
|
4
|
+
*/
|
|
5
|
+
export { MetaIntegration, metaIntegration } from './meta-integration';
|
|
6
|
+
export { TikTokIntegration, tiktokIntegration } from './tiktok-integration';
|
|
7
|
+
export { AppleSearchAdsIntegration, appleSearchAdsIntegration } from './apple-search-ads-integration';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform SDK Integrations
|
|
3
|
+
* Meta (Facebook), TikTok, and Apple Search Ads SDK wrappers
|
|
4
|
+
*/
|
|
5
|
+
export { MetaIntegration, metaIntegration } from './meta-integration';
|
|
6
|
+
export { TikTokIntegration, tiktokIntegration } from './tiktok-integration';
|
|
7
|
+
export { AppleSearchAdsIntegration, appleSearchAdsIntegration } from './apple-search-ads-integration';
|