@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.
Files changed (45) hide show
  1. package/CHANGELOG.md +31 -140
  2. package/LICENSE +21 -0
  3. package/README.md +434 -217
  4. package/datalyr-react-native.podspec +31 -0
  5. package/ios/DatalyrNative.m +74 -0
  6. package/ios/DatalyrNative.swift +332 -0
  7. package/ios/DatalyrSKAdNetwork.m +26 -0
  8. package/lib/datalyr-sdk.d.ts +73 -3
  9. package/lib/datalyr-sdk.js +353 -3
  10. package/lib/index.d.ts +2 -0
  11. package/lib/index.js +4 -2
  12. package/lib/integrations/apple-search-ads-integration.d.ts +43 -0
  13. package/lib/integrations/apple-search-ads-integration.js +106 -0
  14. package/lib/integrations/index.d.ts +7 -0
  15. package/lib/integrations/index.js +7 -0
  16. package/lib/integrations/meta-integration.d.ts +76 -0
  17. package/lib/integrations/meta-integration.js +218 -0
  18. package/lib/integrations/tiktok-integration.d.ts +82 -0
  19. package/lib/integrations/tiktok-integration.js +356 -0
  20. package/lib/native/DatalyrNativeBridge.d.ts +57 -0
  21. package/lib/native/DatalyrNativeBridge.js +187 -0
  22. package/lib/native/index.d.ts +5 -0
  23. package/lib/native/index.js +5 -0
  24. package/lib/types.d.ts +29 -0
  25. package/package.json +11 -5
  26. package/src/datalyr-sdk-expo.ts +997 -0
  27. package/src/datalyr-sdk.ts +455 -19
  28. package/src/expo.ts +42 -18
  29. package/src/index.ts +8 -2
  30. package/src/integrations/apple-search-ads-integration.ts +119 -0
  31. package/src/integrations/index.ts +8 -0
  32. package/src/integrations/meta-integration.ts +238 -0
  33. package/src/integrations/tiktok-integration.ts +360 -0
  34. package/src/native/DatalyrNativeBridge.ts +313 -0
  35. package/src/native/index.ts +11 -0
  36. package/src/types.ts +39 -0
  37. package/src/utils-expo.ts +25 -3
  38. package/src/utils-interface.ts +38 -0
  39. package/EXPO_INSTALL.md +0 -297
  40. package/INSTALL.md +0 -402
  41. package/examples/attribution-example.tsx +0 -377
  42. package/examples/auto-events-example.tsx +0 -403
  43. package/examples/example.tsx +0 -250
  44. package/examples/skadnetwork-example.tsx +0 -380
  45. package/examples/test-implementation.tsx +0 -163
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Apple Search Ads Attribution Integration
3
+ * Uses AdServices framework (iOS 14.3+) to capture attribution from App Store search ads
4
+ */
5
+
6
+ import { Platform } from 'react-native';
7
+ import { AppleSearchAdsNativeBridge, AppleSearchAdsAttribution, isNativeModuleAvailable } from '../native/DatalyrNativeBridge';
8
+
9
+ /**
10
+ * Apple Search Ads Integration class
11
+ * Fetches attribution data for users who installed via Apple Search Ads
12
+ */
13
+ export class AppleSearchAdsIntegration {
14
+ private attributionData: AppleSearchAdsAttribution | null = null;
15
+ private fetched: boolean = false;
16
+ private available: boolean = false;
17
+ private debug: boolean = false;
18
+
19
+ /**
20
+ * Initialize and fetch Apple Search Ads attribution
21
+ */
22
+ async initialize(debug: boolean = false): Promise<void> {
23
+ this.debug = debug;
24
+
25
+ // Only available on iOS via native module
26
+ if (Platform.OS !== 'ios') {
27
+ this.log('Apple Search Ads only available on iOS');
28
+ return;
29
+ }
30
+
31
+ this.available = isNativeModuleAvailable();
32
+
33
+ if (!this.available) {
34
+ this.log('Apple Search Ads native module not available');
35
+ return;
36
+ }
37
+
38
+ // Automatically fetch attribution on init
39
+ await this.fetchAttribution();
40
+ }
41
+
42
+ /**
43
+ * Fetch attribution data from Apple's AdServices API
44
+ * Call this during app initialization
45
+ */
46
+ async fetchAttribution(): Promise<AppleSearchAdsAttribution | null> {
47
+ if (!this.available) {
48
+ return null;
49
+ }
50
+
51
+ // Only fetch once
52
+ if (this.fetched) {
53
+ return this.attributionData;
54
+ }
55
+
56
+ try {
57
+ this.attributionData = await AppleSearchAdsNativeBridge.getAttribution();
58
+ this.fetched = true;
59
+
60
+ if (this.attributionData?.attribution) {
61
+ this.log('Apple Search Ads attribution found:', {
62
+ campaignId: this.attributionData.campaignId,
63
+ campaignName: this.attributionData.campaignName,
64
+ adGroupId: this.attributionData.adGroupId,
65
+ keyword: this.attributionData.keyword,
66
+ });
67
+ } else {
68
+ this.log('No Apple Search Ads attribution (user did not come from search ad)');
69
+ }
70
+
71
+ return this.attributionData;
72
+ } catch (error) {
73
+ this.logError('Failed to fetch Apple Search Ads attribution:', error);
74
+ this.fetched = true;
75
+ return null;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Get cached attribution data
81
+ */
82
+ getAttributionData(): AppleSearchAdsAttribution | null {
83
+ return this.attributionData;
84
+ }
85
+
86
+ /**
87
+ * Check if user came from Apple Search Ads
88
+ */
89
+ hasAttribution(): boolean {
90
+ return this.attributionData?.attribution === true;
91
+ }
92
+
93
+ /**
94
+ * Check if Apple Search Ads is available (iOS 14.3+)
95
+ */
96
+ isAvailable(): boolean {
97
+ return this.available;
98
+ }
99
+
100
+ /**
101
+ * Check if attribution has been fetched
102
+ */
103
+ hasFetched(): boolean {
104
+ return this.fetched;
105
+ }
106
+
107
+ private log(message: string, data?: any): void {
108
+ if (this.debug) {
109
+ console.log(`[Datalyr/AppleSearchAds] ${message}`, data || '');
110
+ }
111
+ }
112
+
113
+ private logError(message: string, error: any): void {
114
+ console.error(`[Datalyr/AppleSearchAds] ${message}`, error);
115
+ }
116
+ }
117
+
118
+ // Export singleton instance
119
+ export const appleSearchAdsIntegration = new AppleSearchAdsIntegration();
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Platform SDK Integrations
3
+ * Meta (Facebook), TikTok, and Apple Search Ads SDK wrappers
4
+ */
5
+
6
+ export { MetaIntegration, metaIntegration } from './meta-integration';
7
+ export { TikTokIntegration, tiktokIntegration } from './tiktok-integration';
8
+ export { AppleSearchAdsIntegration, appleSearchAdsIntegration } from './apple-search-ads-integration';
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Meta (Facebook) SDK Integration
3
+ * Uses bundled native iOS SDK for deferred deep linking, event forwarding, and Advanced Matching
4
+ */
5
+
6
+ import { Platform } from 'react-native';
7
+ import { MetaConfig, DeferredDeepLinkResult } from '../types';
8
+ import { MetaNativeBridge, isNativeModuleAvailable } from '../native/DatalyrNativeBridge';
9
+
10
+ /**
11
+ * Meta Integration class for handling Facebook SDK operations
12
+ * Uses native iOS SDK bundled via CocoaPods (no additional npm packages required)
13
+ */
14
+ export class MetaIntegration {
15
+ private config: MetaConfig | null = null;
16
+ private initialized: boolean = false;
17
+ private available: boolean = false;
18
+ private debug: boolean = false;
19
+ private deferredDeepLinkData: DeferredDeepLinkResult | null = null;
20
+
21
+ /**
22
+ * Initialize Meta SDK with configuration
23
+ */
24
+ async initialize(config: MetaConfig, debug: boolean = false): Promise<void> {
25
+ this.debug = debug;
26
+ this.config = config;
27
+
28
+ // Only available on iOS via native module
29
+ if (Platform.OS !== 'ios') {
30
+ this.log('Meta SDK only available on iOS');
31
+ return;
32
+ }
33
+
34
+ this.available = isNativeModuleAvailable();
35
+
36
+ if (!this.available) {
37
+ this.log('Meta native module not available');
38
+ return;
39
+ }
40
+
41
+ try {
42
+ const success = await MetaNativeBridge.initialize(
43
+ config.appId,
44
+ config.clientToken,
45
+ config.advertiserTrackingEnabled ?? false
46
+ );
47
+
48
+ if (success) {
49
+ this.initialized = true;
50
+ this.log(`Meta SDK initialized with App ID: ${config.appId}`);
51
+ }
52
+ } catch (error) {
53
+ this.logError('Failed to initialize Meta SDK:', error);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Update tracking authorization status (call after ATT prompt)
59
+ */
60
+ async updateTrackingAuthorization(enabled: boolean): Promise<void> {
61
+ if (!this.available || !this.initialized) return;
62
+
63
+ try {
64
+ await MetaNativeBridge.updateTrackingAuthorization(enabled);
65
+ this.log(`Meta ATT status updated: ${enabled ? 'authorized' : 'not authorized'}`);
66
+ } catch (error) {
67
+ this.logError('Failed to update Meta tracking authorization:', error);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Fetch deferred deep link from Meta SDK
73
+ * This captures fbclid for installs that went through App Store
74
+ */
75
+ async fetchDeferredDeepLink(): Promise<DeferredDeepLinkResult | null> {
76
+ if (!this.available || !this.initialized) {
77
+ return null;
78
+ }
79
+
80
+ if (this.config?.enableDeferredDeepLink === false) {
81
+ return null;
82
+ }
83
+
84
+ try {
85
+ const url = await MetaNativeBridge.fetchDeferredAppLink();
86
+
87
+ if (!url) {
88
+ this.log('No deferred deep link available from Meta');
89
+ return null;
90
+ }
91
+
92
+ // Parse the URL for attribution parameters
93
+ const result = this.parseDeepLinkUrl(url);
94
+ this.deferredDeepLinkData = result;
95
+
96
+ this.log(`Meta deferred deep link fetched: ${url}`);
97
+ return result;
98
+ } catch (error) {
99
+ // This is expected to fail in some scenarios - log but don't treat as error
100
+ this.log('Could not fetch Meta deferred deep link (may not be available)');
101
+ return null;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Parse deep link URL to extract attribution parameters
107
+ */
108
+ private parseDeepLinkUrl(urlString: string): DeferredDeepLinkResult {
109
+ const result: DeferredDeepLinkResult = {
110
+ url: urlString,
111
+ source: 'meta',
112
+ };
113
+
114
+ try {
115
+ const url = new URL(urlString);
116
+ const params = url.searchParams;
117
+
118
+ // Extract known parameters
119
+ if (params.get('fbclid')) result.fbclid = params.get('fbclid')!;
120
+ if (params.get('utm_source')) result.utmSource = params.get('utm_source')!;
121
+ if (params.get('utm_medium')) result.utmMedium = params.get('utm_medium')!;
122
+ if (params.get('utm_campaign')) result.utmCampaign = params.get('utm_campaign')!;
123
+ if (params.get('utm_content')) result.utmContent = params.get('utm_content')!;
124
+ if (params.get('utm_term')) result.utmTerm = params.get('utm_term')!;
125
+ if (params.get('campaign_id')) result.campaignId = params.get('campaign_id')!;
126
+ if (params.get('adset_id')) result.adsetId = params.get('adset_id')!;
127
+ if (params.get('ad_id')) result.adId = params.get('ad_id')!;
128
+ } catch (error) {
129
+ this.logError('Failed to parse deep link URL:', error);
130
+ }
131
+
132
+ return result;
133
+ }
134
+
135
+ /**
136
+ * Log purchase event to Meta
137
+ */
138
+ async logPurchase(value: number, currency: string, parameters?: Record<string, any>): Promise<void> {
139
+ if (!this.available || !this.initialized) return;
140
+ if (this.config?.enableAppEvents === false) return;
141
+
142
+ try {
143
+ await MetaNativeBridge.logPurchase(value, currency, parameters);
144
+ this.log(`Meta purchase event logged: ${value} ${currency}`);
145
+ } catch (error) {
146
+ this.logError('Failed to log Meta purchase:', error);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Log custom event to Meta
152
+ */
153
+ async logEvent(eventName: string, valueToSum?: number, parameters?: Record<string, any>): Promise<void> {
154
+ if (!this.available || !this.initialized) return;
155
+ if (this.config?.enableAppEvents === false) return;
156
+
157
+ try {
158
+ await MetaNativeBridge.logEvent(eventName, valueToSum, parameters);
159
+ this.log(`Meta event logged: ${eventName}`);
160
+ } catch (error) {
161
+ this.logError('Failed to log Meta event:', error);
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Set user data for Advanced Matching (improves conversion attribution)
167
+ * Note: Meta's Advanced Matching uses these specific fields - externalId is not supported
168
+ */
169
+ async setUserData(userData: {
170
+ email?: string;
171
+ firstName?: string;
172
+ lastName?: string;
173
+ phone?: string;
174
+ dateOfBirth?: string;
175
+ gender?: string;
176
+ city?: string;
177
+ state?: string;
178
+ zip?: string;
179
+ country?: string;
180
+ }): Promise<void> {
181
+ if (!this.available || !this.initialized) return;
182
+
183
+ try {
184
+ await MetaNativeBridge.setUserData(userData);
185
+ this.log('Meta user data set for Advanced Matching');
186
+ } catch (error) {
187
+ this.logError('Failed to set Meta user data:', error);
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Clear user data (call on logout)
193
+ */
194
+ async clearUserData(): Promise<void> {
195
+ if (!this.available || !this.initialized) return;
196
+
197
+ try {
198
+ await MetaNativeBridge.clearUserData();
199
+ this.log('Meta user data cleared');
200
+ } catch (error) {
201
+ this.logError('Failed to clear Meta user data:', error);
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Get cached deferred deep link data
207
+ */
208
+ getDeferredDeepLinkData(): DeferredDeepLinkResult | null {
209
+ return this.deferredDeepLinkData;
210
+ }
211
+
212
+ /**
213
+ * Check if Meta SDK is available and initialized
214
+ */
215
+ isAvailable(): boolean {
216
+ return this.available && this.initialized;
217
+ }
218
+
219
+ /**
220
+ * Check if Meta SDK native module is installed
221
+ */
222
+ isInstalled(): boolean {
223
+ return this.available;
224
+ }
225
+
226
+ private log(message: string, data?: any): void {
227
+ if (this.debug) {
228
+ console.log(`[Datalyr/Meta] ${message}`, data || '');
229
+ }
230
+ }
231
+
232
+ private logError(message: string, error: any): void {
233
+ console.error(`[Datalyr/Meta] ${message}`, error);
234
+ }
235
+ }
236
+
237
+ // Export singleton instance
238
+ export const metaIntegration = new MetaIntegration();