@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.
Files changed (47) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +30 -1
  3. package/android/build.gradle +54 -0
  4. package/android/src/main/AndroidManifest.xml +14 -0
  5. package/android/src/main/java/com/datalyr/reactnative/DatalyrNativeModule.java +423 -0
  6. package/android/src/main/java/com/datalyr/reactnative/DatalyrPackage.java +30 -0
  7. package/android/src/main/java/com/datalyr/reactnative/DatalyrPlayInstallReferrerModule.java +229 -0
  8. package/datalyr-react-native.podspec +2 -2
  9. package/ios/DatalyrNative.m +4 -0
  10. package/ios/DatalyrNative.swift +58 -1
  11. package/ios/DatalyrSKAdNetwork.m +52 -1
  12. package/lib/ConversionValueEncoder.d.ts +13 -1
  13. package/lib/ConversionValueEncoder.js +57 -23
  14. package/lib/datalyr-sdk.d.ts +34 -2
  15. package/lib/datalyr-sdk.js +90 -8
  16. package/lib/index.d.ts +4 -1
  17. package/lib/index.js +2 -1
  18. package/lib/integrations/apple-search-ads-integration.d.ts +43 -0
  19. package/lib/integrations/apple-search-ads-integration.js +106 -0
  20. package/lib/integrations/index.d.ts +4 -1
  21. package/lib/integrations/index.js +3 -1
  22. package/lib/integrations/meta-integration.d.ts +1 -0
  23. package/lib/integrations/meta-integration.js +4 -3
  24. package/lib/integrations/play-install-referrer.d.ts +74 -0
  25. package/lib/integrations/play-install-referrer.js +156 -0
  26. package/lib/integrations/tiktok-integration.d.ts +1 -0
  27. package/lib/integrations/tiktok-integration.js +4 -3
  28. package/lib/journey.d.ts +106 -0
  29. package/lib/journey.js +258 -0
  30. package/lib/native/DatalyrNativeBridge.d.ts +67 -2
  31. package/lib/native/DatalyrNativeBridge.js +80 -7
  32. package/lib/native/SKAdNetworkBridge.d.ts +21 -0
  33. package/lib/native/SKAdNetworkBridge.js +54 -0
  34. package/package.json +9 -3
  35. package/src/ConversionValueEncoder.ts +67 -26
  36. package/src/datalyr-sdk-expo.ts +98 -9
  37. package/src/datalyr-sdk.ts +109 -14
  38. package/src/expo.ts +8 -0
  39. package/src/index.ts +6 -1
  40. package/src/integrations/apple-search-ads-integration.ts +119 -0
  41. package/src/integrations/index.ts +4 -1
  42. package/src/integrations/meta-integration.ts +4 -3
  43. package/src/integrations/play-install-referrer.ts +203 -0
  44. package/src/integrations/tiktok-integration.ts +4 -3
  45. package/src/journey.ts +338 -0
  46. package/src/native/DatalyrNativeBridge.ts +137 -9
  47. package/src/native/SKAdNetworkBridge.ts +86 -2
@@ -0,0 +1,229 @@
1
+ package com.datalyr.reactnative;
2
+
3
+ import android.os.RemoteException;
4
+ import android.util.Log;
5
+
6
+ import com.facebook.react.bridge.Arguments;
7
+ import com.facebook.react.bridge.Promise;
8
+ import com.facebook.react.bridge.ReactApplicationContext;
9
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
10
+ import com.facebook.react.bridge.ReactMethod;
11
+ import com.facebook.react.bridge.WritableMap;
12
+
13
+ import com.android.installreferrer.api.InstallReferrerClient;
14
+ import com.android.installreferrer.api.InstallReferrerStateListener;
15
+ import com.android.installreferrer.api.ReferrerDetails;
16
+
17
+ import java.io.UnsupportedEncodingException;
18
+ import java.net.URLDecoder;
19
+ import java.util.HashMap;
20
+ import java.util.Map;
21
+
22
+ /**
23
+ * Google Play Install Referrer Module for Android
24
+ *
25
+ * Captures install attribution data from Google Play Store:
26
+ * - UTM parameters (utm_source, utm_medium, utm_campaign, etc.)
27
+ * - Google Ads click ID (gclid)
28
+ * - Referrer timestamps
29
+ *
30
+ * This data is critical for attributing installs to marketing campaigns.
31
+ */
32
+ public class DatalyrPlayInstallReferrerModule extends ReactContextBaseJavaModule {
33
+ private static final String TAG = "DatalyrPlayReferrer";
34
+ private static final String MODULE_NAME = "DatalyrPlayInstallReferrer";
35
+
36
+ private final ReactApplicationContext reactContext;
37
+ private InstallReferrerClient referrerClient;
38
+
39
+ public DatalyrPlayInstallReferrerModule(ReactApplicationContext context) {
40
+ super(context);
41
+ this.reactContext = context;
42
+ }
43
+
44
+ @Override
45
+ public String getName() {
46
+ return MODULE_NAME;
47
+ }
48
+
49
+ /**
50
+ * Check if Play Install Referrer is available
51
+ */
52
+ @ReactMethod
53
+ public void isAvailable(Promise promise) {
54
+ try {
55
+ // Check if the Play Install Referrer library is available
56
+ Class.forName("com.android.installreferrer.api.InstallReferrerClient");
57
+ promise.resolve(true);
58
+ } catch (ClassNotFoundException e) {
59
+ promise.resolve(false);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Get install referrer data from Google Play
65
+ *
66
+ * Returns an object with:
67
+ * - referrerUrl: The full referrer URL
68
+ * - referrerClickTimestamp: When the referrer link was clicked (ms)
69
+ * - installBeginTimestamp: When the install began (ms)
70
+ * - installCompleteTimestamp: When install was completed (ms) - Android 10+
71
+ * - gclid: Google Ads click ID (if present)
72
+ * - utmSource, utmMedium, utmCampaign, etc.
73
+ */
74
+ @ReactMethod
75
+ public void getInstallReferrer(final Promise promise) {
76
+ try {
77
+ referrerClient = InstallReferrerClient.newBuilder(reactContext.getApplicationContext()).build();
78
+
79
+ referrerClient.startConnection(new InstallReferrerStateListener() {
80
+ @Override
81
+ public void onInstallReferrerSetupFinished(int responseCode) {
82
+ switch (responseCode) {
83
+ case InstallReferrerClient.InstallReferrerResponse.OK:
84
+ try {
85
+ ReferrerDetails details = referrerClient.getInstallReferrer();
86
+ WritableMap result = parseReferrerDetails(details);
87
+ promise.resolve(result);
88
+ } catch (RemoteException e) {
89
+ Log.e(TAG, "Failed to get install referrer", e);
90
+ promise.resolve(null);
91
+ } finally {
92
+ referrerClient.endConnection();
93
+ }
94
+ break;
95
+
96
+ case InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED:
97
+ Log.d(TAG, "Install referrer not supported on this device");
98
+ promise.resolve(null);
99
+ referrerClient.endConnection();
100
+ break;
101
+
102
+ case InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE:
103
+ Log.d(TAG, "Install referrer service unavailable");
104
+ promise.resolve(null);
105
+ referrerClient.endConnection();
106
+ break;
107
+
108
+ default:
109
+ Log.d(TAG, "Install referrer unknown response: " + responseCode);
110
+ promise.resolve(null);
111
+ referrerClient.endConnection();
112
+ break;
113
+ }
114
+ }
115
+
116
+ @Override
117
+ public void onInstallReferrerServiceDisconnected() {
118
+ Log.d(TAG, "Install referrer service disconnected");
119
+ // Connection lost - can try to reconnect if needed
120
+ }
121
+ });
122
+ } catch (Exception e) {
123
+ Log.e(TAG, "Failed to start install referrer client", e);
124
+ promise.resolve(null);
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Parse ReferrerDetails into a WritableMap with UTM parameters extracted
130
+ */
131
+ private WritableMap parseReferrerDetails(ReferrerDetails details) {
132
+ WritableMap result = Arguments.createMap();
133
+
134
+ try {
135
+ String referrerUrl = details.getInstallReferrer();
136
+ result.putString("referrerUrl", referrerUrl);
137
+ result.putDouble("referrerClickTimestamp", details.getReferrerClickTimestampSeconds() * 1000.0);
138
+ result.putDouble("installBeginTimestamp", details.getInstallBeginTimestampSeconds() * 1000.0);
139
+
140
+ // Android 10+ has install complete timestamp
141
+ try {
142
+ long installCompleteTs = details.getInstallBeginTimestampServerSeconds();
143
+ if (installCompleteTs > 0) {
144
+ result.putDouble("installCompleteTimestamp", installCompleteTs * 1000.0);
145
+ }
146
+ } catch (NoSuchMethodError e) {
147
+ // Method not available on older Android versions
148
+ }
149
+
150
+ // Parse UTM parameters from referrer URL
151
+ if (referrerUrl != null && !referrerUrl.isEmpty()) {
152
+ Map<String, String> params = parseReferrerUrl(referrerUrl);
153
+
154
+ // UTM parameters
155
+ if (params.containsKey("utm_source")) {
156
+ result.putString("utmSource", params.get("utm_source"));
157
+ }
158
+ if (params.containsKey("utm_medium")) {
159
+ result.putString("utmMedium", params.get("utm_medium"));
160
+ }
161
+ if (params.containsKey("utm_campaign")) {
162
+ result.putString("utmCampaign", params.get("utm_campaign"));
163
+ }
164
+ if (params.containsKey("utm_term")) {
165
+ result.putString("utmTerm", params.get("utm_term"));
166
+ }
167
+ if (params.containsKey("utm_content")) {
168
+ result.putString("utmContent", params.get("utm_content"));
169
+ }
170
+
171
+ // Google Ads click ID
172
+ if (params.containsKey("gclid")) {
173
+ result.putString("gclid", params.get("gclid"));
174
+ }
175
+
176
+ // Other potential click IDs
177
+ if (params.containsKey("fbclid")) {
178
+ result.putString("fbclid", params.get("fbclid"));
179
+ }
180
+ if (params.containsKey("ttclid")) {
181
+ result.putString("ttclid", params.get("ttclid"));
182
+ }
183
+
184
+ // App referrer (used by some attribution providers)
185
+ if (params.containsKey("referrer")) {
186
+ result.putString("referrer", params.get("referrer"));
187
+ }
188
+ }
189
+
190
+ Log.d(TAG, "Install referrer parsed successfully");
191
+
192
+ } catch (Exception e) {
193
+ Log.e(TAG, "Failed to parse referrer details", e);
194
+ }
195
+
196
+ return result;
197
+ }
198
+
199
+ /**
200
+ * Parse URL-encoded referrer string into key-value pairs
201
+ */
202
+ private Map<String, String> parseReferrerUrl(String referrerUrl) {
203
+ Map<String, String> params = new HashMap<>();
204
+
205
+ if (referrerUrl == null || referrerUrl.isEmpty()) {
206
+ return params;
207
+ }
208
+
209
+ try {
210
+ // Decode the URL-encoded referrer
211
+ String decoded = URLDecoder.decode(referrerUrl, "UTF-8");
212
+
213
+ // Split by & to get key=value pairs
214
+ String[] pairs = decoded.split("&");
215
+ for (String pair : pairs) {
216
+ int idx = pair.indexOf('=');
217
+ if (idx > 0 && idx < pair.length() - 1) {
218
+ String key = pair.substring(0, idx);
219
+ String value = pair.substring(idx + 1);
220
+ params.put(key, value);
221
+ }
222
+ }
223
+ } catch (UnsupportedEncodingException e) {
224
+ Log.e(TAG, "Failed to decode referrer URL", e);
225
+ }
226
+
227
+ return params;
228
+ }
229
+ }
@@ -20,8 +20,8 @@ Pod::Spec.new do |s|
20
20
  s.swift_version = "5.0"
21
21
 
22
22
  s.dependency "React-Core"
23
- s.dependency "FBSDKCoreKit", "~> 17.0"
24
- s.dependency "TikTokBusinessSDK", "~> 1.4"
23
+ s.dependency "FBSDKCoreKit", "~> 18.0"
24
+ s.dependency "TikTokBusinessSDK", "~> 1.6"
25
25
 
26
26
  # Disable bitcode (required for TikTok SDK)
27
27
  s.pod_target_xcconfig = {
@@ -67,4 +67,8 @@ RCT_EXTERN_METHOD(updateTikTokTrackingAuthorization:(BOOL)enabled
67
67
  RCT_EXTERN_METHOD(getSDKAvailability:(RCTPromiseResolveBlock)resolve
68
68
  reject:(RCTPromiseRejectBlock)reject)
69
69
 
70
+ // Apple Search Ads Attribution
71
+ RCT_EXTERN_METHOD(getAppleSearchAdsAttribution:(RCTPromiseResolveBlock)resolve
72
+ reject:(RCTPromiseRejectBlock)reject)
73
+
70
74
  @end
@@ -1,6 +1,7 @@
1
1
  import Foundation
2
2
  import FBSDKCoreKit
3
3
  import TikTokBusinessSDK
4
+ import AdServices
4
5
 
5
6
  @objc(DatalyrNative)
6
7
  class DatalyrNative: NSObject {
@@ -269,7 +270,63 @@ class DatalyrNative: NSObject {
269
270
  ) {
270
271
  resolve([
271
272
  "meta": true,
272
- "tiktok": true
273
+ "tiktok": true,
274
+ "appleSearchAds": true
273
275
  ])
274
276
  }
277
+
278
+ // MARK: - Apple Search Ads Attribution
279
+
280
+ @objc func getAppleSearchAdsAttribution(
281
+ _ resolve: @escaping RCTPromiseResolveBlock,
282
+ reject: @escaping RCTPromiseRejectBlock
283
+ ) {
284
+ // AdServices is available on iOS 14.3+
285
+ if #available(iOS 14.3, *) {
286
+ do {
287
+ // Get the attribution token from AdServices
288
+ let token = try AAAttribution.attributionToken()
289
+
290
+ // Send token to Apple's API to get attribution data
291
+ var request = URLRequest(url: URL(string: "https://api-adservices.apple.com/api/v1/")!)
292
+ request.httpMethod = "POST"
293
+ request.setValue("text/plain", forHTTPHeaderField: "Content-Type")
294
+ request.httpBody = token.data(using: .utf8)
295
+
296
+ let task = URLSession.shared.dataTask(with: request) { data, response, error in
297
+ if let error = error {
298
+ // Network error - resolve with nil instead of rejecting
299
+ resolve(nil)
300
+ return
301
+ }
302
+
303
+ guard let data = data else {
304
+ resolve(nil)
305
+ return
306
+ }
307
+
308
+ do {
309
+ if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] {
310
+ // Return attribution data
311
+ // Includes: attribution, orgId, orgName, campaignId, campaignName,
312
+ // adGroupId, adGroupName, clickDate, conversionType, etc.
313
+ resolve(json)
314
+ } else {
315
+ resolve(nil)
316
+ }
317
+ } catch {
318
+ resolve(nil)
319
+ }
320
+ }
321
+ task.resume()
322
+
323
+ } catch {
324
+ // Attribution token not available (user didn't come from Apple Search Ads)
325
+ resolve(nil)
326
+ }
327
+ } else {
328
+ // iOS version too old
329
+ resolve(nil)
330
+ }
331
+ }
275
332
  }
@@ -8,6 +8,7 @@
8
8
 
9
9
  RCT_EXPORT_MODULE();
10
10
 
11
+ // SKAN 3.0 - Legacy method for iOS 14.0-16.0
11
12
  RCT_EXPORT_METHOD(updateConversionValue:(NSInteger)value
12
13
  resolve:(RCTPromiseResolveBlock)resolve
13
14
  reject:(RCTPromiseRejectBlock)reject) {
@@ -23,4 +24,54 @@ RCT_EXPORT_METHOD(updateConversionValue:(NSInteger)value
23
24
  }
24
25
  }
25
26
 
26
- @end
27
+ // SKAN 4.0 - New method for iOS 16.1+ with coarse value and lock window support
28
+ RCT_EXPORT_METHOD(updatePostbackConversionValue:(NSInteger)fineValue
29
+ coarseValue:(NSString *)coarseValue
30
+ lockWindow:(BOOL)lockWindow
31
+ resolve:(RCTPromiseResolveBlock)resolve
32
+ reject:(RCTPromiseRejectBlock)reject) {
33
+ if (@available(iOS 16.1, *)) {
34
+ // Convert string to SKAdNetwork.CoarseConversionValue
35
+ SKAdNetworkCoarseConversionValue coarse;
36
+ if ([coarseValue isEqualToString:@"high"]) {
37
+ coarse = SKAdNetworkCoarseConversionValueHigh;
38
+ } else if ([coarseValue isEqualToString:@"medium"]) {
39
+ coarse = SKAdNetworkCoarseConversionValueMedium;
40
+ } else {
41
+ coarse = SKAdNetworkCoarseConversionValueLow;
42
+ }
43
+
44
+ [SKAdNetwork updatePostbackConversionValue:fineValue
45
+ coarseValue:coarse
46
+ lockWindow:lockWindow
47
+ completionHandler:^(NSError * _Nullable error) {
48
+ if (error) {
49
+ reject(@"skadnetwork_error", error.localizedDescription, error);
50
+ } else {
51
+ resolve(@(YES));
52
+ }
53
+ }];
54
+ } else if (@available(iOS 14.0, *)) {
55
+ // Fallback to SKAN 3.0 for iOS 14.0-16.0
56
+ @try {
57
+ [SKAdNetwork updateConversionValue:fineValue];
58
+ resolve(@(YES));
59
+ } @catch (NSException *exception) {
60
+ reject(@"skadnetwork_error", exception.reason, nil);
61
+ }
62
+ } else {
63
+ reject(@"ios_version_error", @"SKAdNetwork requires iOS 14.0+", nil);
64
+ }
65
+ }
66
+
67
+ // Check if SKAN 4.0 is available (iOS 16.1+)
68
+ RCT_EXPORT_METHOD(isSKAN4Available:(RCTPromiseResolveBlock)resolve
69
+ reject:(RCTPromiseRejectBlock)reject) {
70
+ if (@available(iOS 16.1, *)) {
71
+ resolve(@(YES));
72
+ } else {
73
+ resolve(@(NO));
74
+ }
75
+ }
76
+
77
+ @end
@@ -1,7 +1,10 @@
1
+ import { SKANCoarseValue, SKANConversionResult } from './native/SKAdNetworkBridge';
1
2
  interface EventMapping {
2
3
  bits: number[];
3
4
  revenueBits?: number[];
4
5
  priority: number;
6
+ coarseValue?: SKANCoarseValue;
7
+ lockWindow?: boolean;
5
8
  }
6
9
  interface ConversionTemplate {
7
10
  name: string;
@@ -11,13 +14,22 @@ export declare class ConversionValueEncoder {
11
14
  private template;
12
15
  constructor(template: ConversionTemplate);
13
16
  /**
14
- * Encode an event into Apple's 0-63 conversion value format
17
+ * Encode an event into Apple's 0-63 conversion value format (SKAN 3.0 compatible)
18
+ * @deprecated Use encodeWithSKAN4 for iOS 16.1+
15
19
  */
16
20
  encode(event: string, properties?: Record<string, any>): number;
21
+ /**
22
+ * Encode an event with full SKAN 4.0 support (fine value, coarse value, lock window)
23
+ */
24
+ encodeWithSKAN4(event: string, properties?: Record<string, any>): SKANConversionResult;
17
25
  /**
18
26
  * Map revenue amount to 3-bit tier (0-7)
19
27
  */
20
28
  private getRevenueTier;
29
+ /**
30
+ * Map revenue to SKAN 4.0 coarse value
31
+ */
32
+ private getCoarseValueForRevenue;
21
33
  }
22
34
  export declare const ConversionTemplates: {
23
35
  ecommerce: ConversionTemplate;
@@ -3,18 +3,27 @@ export class ConversionValueEncoder {
3
3
  this.template = template;
4
4
  }
5
5
  /**
6
- * Encode an event into Apple's 0-63 conversion value format
6
+ * Encode an event into Apple's 0-63 conversion value format (SKAN 3.0 compatible)
7
+ * @deprecated Use encodeWithSKAN4 for iOS 16.1+
7
8
  */
8
9
  encode(event, properties) {
10
+ return this.encodeWithSKAN4(event, properties).fineValue;
11
+ }
12
+ /**
13
+ * Encode an event with full SKAN 4.0 support (fine value, coarse value, lock window)
14
+ */
15
+ encodeWithSKAN4(event, properties) {
9
16
  const mapping = this.template.events[event];
10
- if (!mapping)
11
- return 0;
17
+ if (!mapping) {
18
+ return { fineValue: 0, coarseValue: 'low', lockWindow: false, priority: 0 };
19
+ }
12
20
  let conversionValue = 0;
13
21
  // Set event bits
14
22
  for (const bit of mapping.bits) {
15
23
  conversionValue |= (1 << bit);
16
24
  }
17
25
  // Set revenue bits if revenue is provided
26
+ let coarseValue = mapping.coarseValue || 'medium';
18
27
  if (mapping.revenueBits && properties) {
19
28
  const revenue = properties.revenue || properties.value || 0;
20
29
  const revenueTier = this.getRevenueTier(revenue);
@@ -23,8 +32,17 @@ export class ConversionValueEncoder {
23
32
  conversionValue |= (1 << mapping.revenueBits[i]);
24
33
  }
25
34
  }
35
+ // Upgrade coarse value based on revenue
36
+ coarseValue = this.getCoarseValueForRevenue(revenue);
26
37
  }
27
- return Math.min(conversionValue, 63);
38
+ // Ensure value is within 0-63 range
39
+ const fineValue = Math.min(conversionValue, 63);
40
+ return {
41
+ fineValue,
42
+ coarseValue,
43
+ lockWindow: mapping.lockWindow || false,
44
+ priority: mapping.priority
45
+ };
28
46
  }
29
47
  /**
30
48
  * Map revenue amount to 3-bit tier (0-7)
@@ -46,40 +64,56 @@ export class ConversionValueEncoder {
46
64
  return 6; // $100-250
47
65
  return 7; // $250+
48
66
  }
67
+ /**
68
+ * Map revenue to SKAN 4.0 coarse value
69
+ */
70
+ getCoarseValueForRevenue(revenue) {
71
+ if (revenue < 10)
72
+ return 'low'; // $0-10 = low value
73
+ if (revenue < 50)
74
+ return 'medium'; // $10-50 = medium value
75
+ return 'high'; // $50+ = high value
76
+ }
49
77
  }
50
- // Industry templates
78
+ // Industry templates with SKAN 4.0 support
51
79
  export const ConversionTemplates = {
80
+ // E-commerce template - optimized for online stores
81
+ // SKAN 4.0: purchase locks window, high-value events get "high" coarse value
52
82
  ecommerce: {
53
83
  name: 'ecommerce',
54
84
  events: {
55
- purchase: { bits: [0], revenueBits: [1, 2, 3], priority: 100 },
56
- add_to_cart: { bits: [4], priority: 30 },
57
- begin_checkout: { bits: [5], priority: 50 },
58
- signup: { bits: [6], priority: 20 },
59
- subscribe: { bits: [0, 1], revenueBits: [2, 3, 4], priority: 90 },
60
- view_item: { bits: [7], priority: 10 }
85
+ purchase: { bits: [0], revenueBits: [1, 2, 3], priority: 100, coarseValue: 'high', lockWindow: true },
86
+ add_to_cart: { bits: [4], priority: 30, coarseValue: 'low' },
87
+ begin_checkout: { bits: [5], priority: 50, coarseValue: 'medium' },
88
+ signup: { bits: [6], priority: 20, coarseValue: 'low' },
89
+ subscribe: { bits: [0, 1], revenueBits: [2, 3, 4], priority: 90, coarseValue: 'high', lockWindow: true },
90
+ view_item: { bits: [7], priority: 10, coarseValue: 'low' }
61
91
  }
62
92
  },
93
+ // Gaming template - optimized for mobile games
94
+ // SKAN 4.0: purchase locks window, tutorial completion is medium value
63
95
  gaming: {
64
96
  name: 'gaming',
65
97
  events: {
66
- level_complete: { bits: [0], priority: 40 },
67
- tutorial_complete: { bits: [1], priority: 60 },
68
- purchase: { bits: [2], revenueBits: [3, 4, 5], priority: 100 },
69
- achievement_unlocked: { bits: [6], priority: 30 },
70
- session_start: { bits: [7], priority: 10 },
71
- ad_watched: { bits: [0, 6], priority: 20 }
98
+ level_complete: { bits: [0], priority: 40, coarseValue: 'medium' },
99
+ tutorial_complete: { bits: [1], priority: 60, coarseValue: 'medium' },
100
+ purchase: { bits: [2], revenueBits: [3, 4, 5], priority: 100, coarseValue: 'high', lockWindow: true },
101
+ achievement_unlocked: { bits: [6], priority: 30, coarseValue: 'low' },
102
+ session_start: { bits: [7], priority: 10, coarseValue: 'low' },
103
+ ad_watched: { bits: [0, 6], priority: 20, coarseValue: 'low' }
72
104
  }
73
105
  },
106
+ // Subscription template - optimized for subscription apps
107
+ // SKAN 4.0: subscribe/upgrade lock window, trial is medium value
74
108
  subscription: {
75
109
  name: 'subscription',
76
110
  events: {
77
- trial_start: { bits: [0], priority: 70 },
78
- subscribe: { bits: [1], revenueBits: [2, 3, 4], priority: 100 },
79
- upgrade: { bits: [1, 5], revenueBits: [2, 3, 4], priority: 90 },
80
- cancel: { bits: [6], priority: 20 },
81
- signup: { bits: [7], priority: 30 },
82
- payment_method_added: { bits: [0, 7], priority: 50 }
111
+ trial_start: { bits: [0], priority: 70, coarseValue: 'medium' },
112
+ subscribe: { bits: [1], revenueBits: [2, 3, 4], priority: 100, coarseValue: 'high', lockWindow: true },
113
+ upgrade: { bits: [1, 5], revenueBits: [2, 3, 4], priority: 90, coarseValue: 'high', lockWindow: true },
114
+ cancel: { bits: [6], priority: 20, coarseValue: 'low' },
115
+ signup: { bits: [7], priority: 30, coarseValue: 'low' },
116
+ payment_method_added: { bits: [0, 7], priority: 50, coarseValue: 'medium' }
83
117
  }
84
118
  }
85
119
  };
@@ -1,6 +1,7 @@
1
1
  import { DatalyrConfig, EventData, UserProperties, AutoEventConfig, DeferredDeepLinkResult } from './types';
2
2
  import { AttributionData } from './attribution';
3
3
  import { SessionData } from './auto-events';
4
+ import { AppleSearchAdsAttribution } from './native/DatalyrNativeBridge';
4
5
  export declare class DatalyrSDK {
5
6
  private state;
6
7
  private httpClient;
@@ -55,15 +56,30 @@ export declare class DatalyrSDK {
55
56
  currentUserId?: string;
56
57
  queueStats: any;
57
58
  attribution: any;
59
+ journey: any;
58
60
  };
59
61
  /**
60
62
  * Get the persistent anonymous ID
61
63
  */
62
64
  getAnonymousId(): string;
63
65
  /**
64
- * Get detailed attribution data
66
+ * Get detailed attribution data (includes journey tracking data)
65
67
  */
66
- getAttributionData(): AttributionData;
68
+ getAttributionData(): AttributionData & Record<string, any>;
69
+ /**
70
+ * Get journey tracking summary
71
+ */
72
+ getJourneySummary(): {
73
+ hasFirstTouch: boolean;
74
+ hasLastTouch: boolean;
75
+ touchpointCount: number;
76
+ daysSinceFirstTouch: number;
77
+ sources: string[];
78
+ };
79
+ /**
80
+ * Get full customer journey (all touchpoints)
81
+ */
82
+ getJourney(): import("./journey").TouchPoint[];
67
83
  /**
68
84
  * Set custom attribution data (for testing or manual attribution)
69
85
  */
@@ -90,6 +106,7 @@ export declare class DatalyrSDK {
90
106
  updateAutoEventsConfig(config: Partial<AutoEventConfig>): void;
91
107
  /**
92
108
  * Track event with automatic SKAdNetwork conversion value encoding
109
+ * Uses SKAN 4.0 on iOS 16.1+ with coarse values and lock window support
93
110
  */
94
111
  trackWithSKAdNetwork(event: string, properties?: EventData): Promise<void>;
95
112
  /**
@@ -138,7 +155,19 @@ export declare class DatalyrSDK {
138
155
  getPlatformIntegrationStatus(): {
139
156
  meta: boolean;
140
157
  tiktok: boolean;
158
+ appleSearchAds: boolean;
159
+ playInstallReferrer: boolean;
141
160
  };
161
+ /**
162
+ * Get Apple Search Ads attribution data
163
+ * Returns attribution if user installed via Apple Search Ads, null otherwise
164
+ */
165
+ getAppleSearchAdsAttribution(): AppleSearchAdsAttribution | null;
166
+ /**
167
+ * Get Google Play Install Referrer attribution data (Android only)
168
+ * Returns referrer data if available, null otherwise
169
+ */
170
+ getPlayInstallReferrer(): Record<string, any> | null;
142
171
  /**
143
172
  * Update tracking authorization status on all platform SDKs
144
173
  * Call this AFTER the user responds to the ATT permission dialog
@@ -214,6 +243,7 @@ export declare class Datalyr {
214
243
  currentUserId?: string;
215
244
  queueStats: any;
216
245
  attribution: any;
246
+ journey: any;
217
247
  };
218
248
  static getAnonymousId(): string;
219
249
  static getAttributionData(): AttributionData;
@@ -234,7 +264,9 @@ export declare class Datalyr {
234
264
  static getPlatformIntegrationStatus(): {
235
265
  meta: boolean;
236
266
  tiktok: boolean;
267
+ appleSearchAds: boolean;
237
268
  };
269
+ static getAppleSearchAdsAttribution(): AppleSearchAdsAttribution | null;
238
270
  static updateTrackingAuthorization(enabled: boolean): Promise<void>;
239
271
  }
240
272
  export default datalyr;