@datalyr/react-native 1.3.1 → 1.4.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.
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { NativeModules, Platform } from 'react-native';
11
+ import { requireNativeModule } from 'expo-modules-core';
11
12
 
12
13
  /**
13
14
  * Apple Search Ads attribution data returned from AdServices API (iOS only)
@@ -107,11 +108,23 @@ interface PlayInstallReferrerModule {
107
108
  }
108
109
 
109
110
  // Native modules - available on both iOS and Android
110
- const DatalyrNative: DatalyrNativeModule | null = NativeModules.DatalyrNative ?? null;
111
+ // iOS uses Expo Modules (new arch compatible), Android uses NativeModules (interop layer)
112
+ let DatalyrNative: DatalyrNativeModule | null = null;
113
+ if (Platform.OS === 'ios') {
114
+ try {
115
+ DatalyrNative = requireNativeModule<DatalyrNativeModule>('DatalyrNative');
116
+ } catch {
117
+ // Native module not available
118
+ }
119
+ } else if (Platform.OS === 'android') {
120
+ DatalyrNative = NativeModules.DatalyrNative ?? null;
121
+ }
111
122
 
112
- // Play Install Referrer - Android only
113
- const DatalyrPlayInstallReferrer: PlayInstallReferrerModule | null =
114
- Platform.OS === 'android' ? NativeModules.DatalyrPlayInstallReferrer : null;
123
+ // Play Install Referrer - Android only (stays on NativeModules)
124
+ let DatalyrPlayInstallReferrer: PlayInstallReferrerModule | null = null;
125
+ if (Platform.OS === 'android') {
126
+ DatalyrPlayInstallReferrer = NativeModules.DatalyrPlayInstallReferrer ?? null;
127
+ }
115
128
 
116
129
  /**
117
130
  * Check if native module is available
@@ -1,4 +1,5 @@
1
- import { NativeModules, Platform } from 'react-native';
1
+ import { Platform } from 'react-native';
2
+ import { requireNativeModule } from 'expo-modules-core';
2
3
 
3
4
  // SKAN 4.0 / AdAttributionKit coarse value type
4
5
  export type SKANCoarseValue = 'low' | 'medium' | 'high';
@@ -87,9 +88,15 @@ interface SKAdNetworkModule {
87
88
  ): Promise<OverlappingWindowPostbackResponse>;
88
89
  }
89
90
 
90
- const { DatalyrSKAdNetwork } = NativeModules as {
91
- DatalyrSKAdNetwork?: SKAdNetworkModule
92
- };
91
+ // SKAdNetwork is iOS-only, use Expo Modules for new arch compatibility
92
+ let DatalyrSKAdNetwork: SKAdNetworkModule | undefined;
93
+ if (Platform.OS === 'ios') {
94
+ try {
95
+ DatalyrSKAdNetwork = requireNativeModule<SKAdNetworkModule>('DatalyrSKAdNetwork');
96
+ } catch {
97
+ // Module not available
98
+ }
99
+ }
93
100
 
94
101
  export class SKAdNetworkBridge {
95
102
  private static _isSKAN4Available: boolean | null = null;
@@ -1,74 +0,0 @@
1
- #import <React/RCTBridgeModule.h>
2
-
3
- @interface RCT_EXTERN_MODULE(DatalyrNative, NSObject)
4
-
5
- // Meta SDK Methods
6
- RCT_EXTERN_METHOD(initializeMetaSDK:(NSString *)appId
7
- clientToken:(NSString *)clientToken
8
- advertiserTrackingEnabled:(BOOL)advertiserTrackingEnabled
9
- resolve:(RCTPromiseResolveBlock)resolve
10
- reject:(RCTPromiseRejectBlock)reject)
11
-
12
- RCT_EXTERN_METHOD(fetchDeferredAppLink:(RCTPromiseResolveBlock)resolve
13
- reject:(RCTPromiseRejectBlock)reject)
14
-
15
- RCT_EXTERN_METHOD(logMetaEvent:(NSString *)eventName
16
- valueToSum:(NSNumber *)valueToSum
17
- parameters:(NSDictionary *)parameters
18
- resolve:(RCTPromiseResolveBlock)resolve
19
- reject:(RCTPromiseRejectBlock)reject)
20
-
21
- RCT_EXTERN_METHOD(logMetaPurchase:(double)amount
22
- currency:(NSString *)currency
23
- parameters:(NSDictionary *)parameters
24
- resolve:(RCTPromiseResolveBlock)resolve
25
- reject:(RCTPromiseRejectBlock)reject)
26
-
27
- RCT_EXTERN_METHOD(setMetaUserData:(NSDictionary *)userData
28
- resolve:(RCTPromiseResolveBlock)resolve
29
- reject:(RCTPromiseRejectBlock)reject)
30
-
31
- RCT_EXTERN_METHOD(clearMetaUserData:(RCTPromiseResolveBlock)resolve
32
- reject:(RCTPromiseRejectBlock)reject)
33
-
34
- RCT_EXTERN_METHOD(updateMetaTrackingAuthorization:(BOOL)enabled
35
- resolve:(RCTPromiseResolveBlock)resolve
36
- reject:(RCTPromiseRejectBlock)reject)
37
-
38
- // TikTok SDK Methods
39
- RCT_EXTERN_METHOD(initializeTikTokSDK:(NSString *)appId
40
- tiktokAppId:(NSString *)tiktokAppId
41
- accessToken:(NSString *)accessToken
42
- debug:(BOOL)debug
43
- resolve:(RCTPromiseResolveBlock)resolve
44
- reject:(RCTPromiseRejectBlock)reject)
45
-
46
- RCT_EXTERN_METHOD(trackTikTokEvent:(NSString *)eventName
47
- eventId:(NSString *)eventId
48
- properties:(NSDictionary *)properties
49
- resolve:(RCTPromiseResolveBlock)resolve
50
- reject:(RCTPromiseRejectBlock)reject)
51
-
52
- RCT_EXTERN_METHOD(identifyTikTokUser:(NSString *)externalId
53
- externalUserName:(NSString *)externalUserName
54
- phoneNumber:(NSString *)phoneNumber
55
- email:(NSString *)email
56
- resolve:(RCTPromiseResolveBlock)resolve
57
- reject:(RCTPromiseRejectBlock)reject)
58
-
59
- RCT_EXTERN_METHOD(logoutTikTok:(RCTPromiseResolveBlock)resolve
60
- reject:(RCTPromiseRejectBlock)reject)
61
-
62
- RCT_EXTERN_METHOD(updateTikTokTrackingAuthorization:(BOOL)enabled
63
- resolve:(RCTPromiseResolveBlock)resolve
64
- reject:(RCTPromiseRejectBlock)reject)
65
-
66
- // SDK Availability Check
67
- RCT_EXTERN_METHOD(getSDKAvailability:(RCTPromiseResolveBlock)resolve
68
- reject:(RCTPromiseRejectBlock)reject)
69
-
70
- // Apple Search Ads Attribution
71
- RCT_EXTERN_METHOD(getAppleSearchAdsAttribution:(RCTPromiseResolveBlock)resolve
72
- reject:(RCTPromiseRejectBlock)reject)
73
-
74
- @end
@@ -1,332 +0,0 @@
1
- import Foundation
2
- import FBSDKCoreKit
3
- import TikTokBusinessSDK
4
- import AdServices
5
-
6
- @objc(DatalyrNative)
7
- class DatalyrNative: NSObject {
8
-
9
- @objc static func requiresMainQueueSetup() -> Bool {
10
- return false
11
- }
12
-
13
- // MARK: - Meta (Facebook) SDK Methods
14
-
15
- @objc func initializeMetaSDK(
16
- _ appId: String,
17
- clientToken: String?,
18
- advertiserTrackingEnabled: Bool,
19
- resolve: @escaping RCTPromiseResolveBlock,
20
- reject: @escaping RCTPromiseRejectBlock
21
- ) {
22
- DispatchQueue.main.async {
23
- Settings.shared.appID = appId
24
-
25
- if let token = clientToken, !token.isEmpty {
26
- Settings.shared.clientToken = token
27
- }
28
-
29
- Settings.shared.isAdvertiserTrackingEnabled = advertiserTrackingEnabled
30
- Settings.shared.isAdvertiserIDCollectionEnabled = advertiserTrackingEnabled
31
-
32
- ApplicationDelegate.shared.application(
33
- UIApplication.shared,
34
- didFinishLaunchingWithOptions: nil
35
- )
36
-
37
- resolve(true)
38
- }
39
- }
40
-
41
- @objc func fetchDeferredAppLink(
42
- _ resolve: @escaping RCTPromiseResolveBlock,
43
- reject: @escaping RCTPromiseRejectBlock
44
- ) {
45
- AppLinkUtility.fetchDeferredAppLink { url, error in
46
- if error != nil {
47
- // Don't reject - deferred deep link not available is expected in many cases
48
- // Error is normal when no deferred link exists
49
- resolve(nil)
50
- return
51
- }
52
-
53
- if let url = url {
54
- resolve(url.absoluteString)
55
- } else {
56
- resolve(nil)
57
- }
58
- }
59
- }
60
-
61
- @objc func logMetaEvent(
62
- _ eventName: String,
63
- valueToSum: NSNumber?,
64
- parameters: NSDictionary?,
65
- resolve: @escaping RCTPromiseResolveBlock,
66
- reject: @escaping RCTPromiseRejectBlock
67
- ) {
68
- var params: [AppEvents.ParameterName: Any] = [:]
69
-
70
- if let dict = parameters as? [String: Any] {
71
- for (key, value) in dict {
72
- params[AppEvents.ParameterName(key)] = value
73
- }
74
- }
75
-
76
- if let value = valueToSum?.doubleValue {
77
- AppEvents.shared.logEvent(AppEvents.Name(eventName), valueToSum: value, parameters: params)
78
- } else if params.isEmpty {
79
- AppEvents.shared.logEvent(AppEvents.Name(eventName))
80
- } else {
81
- AppEvents.shared.logEvent(AppEvents.Name(eventName), parameters: params)
82
- }
83
-
84
- resolve(true)
85
- }
86
-
87
- @objc func logMetaPurchase(
88
- _ amount: Double,
89
- currency: String,
90
- parameters: NSDictionary?,
91
- resolve: @escaping RCTPromiseResolveBlock,
92
- reject: @escaping RCTPromiseRejectBlock
93
- ) {
94
- var params: [AppEvents.ParameterName: Any] = [:]
95
-
96
- if let dict = parameters as? [String: Any] {
97
- for (key, value) in dict {
98
- params[AppEvents.ParameterName(key)] = value
99
- }
100
- }
101
-
102
- AppEvents.shared.logPurchase(amount: amount, currency: currency, parameters: params)
103
- resolve(true)
104
- }
105
-
106
- @objc func setMetaUserData(
107
- _ userData: NSDictionary,
108
- resolve: @escaping RCTPromiseResolveBlock,
109
- reject: @escaping RCTPromiseRejectBlock
110
- ) {
111
- AppEvents.shared.setUserData(
112
- userData["email"] as? String,
113
- forType: .email
114
- )
115
- AppEvents.shared.setUserData(
116
- userData["firstName"] as? String,
117
- forType: .firstName
118
- )
119
- AppEvents.shared.setUserData(
120
- userData["lastName"] as? String,
121
- forType: .lastName
122
- )
123
- AppEvents.shared.setUserData(
124
- userData["phone"] as? String,
125
- forType: .phone
126
- )
127
- AppEvents.shared.setUserData(
128
- userData["dateOfBirth"] as? String,
129
- forType: .dateOfBirth
130
- )
131
- AppEvents.shared.setUserData(
132
- userData["gender"] as? String,
133
- forType: .gender
134
- )
135
- AppEvents.shared.setUserData(
136
- userData["city"] as? String,
137
- forType: .city
138
- )
139
- AppEvents.shared.setUserData(
140
- userData["state"] as? String,
141
- forType: .state
142
- )
143
- AppEvents.shared.setUserData(
144
- userData["zip"] as? String,
145
- forType: .zip
146
- )
147
- AppEvents.shared.setUserData(
148
- userData["country"] as? String,
149
- forType: .country
150
- )
151
-
152
- resolve(true)
153
- }
154
-
155
- @objc func clearMetaUserData(
156
- _ resolve: @escaping RCTPromiseResolveBlock,
157
- reject: @escaping RCTPromiseRejectBlock
158
- ) {
159
- AppEvents.shared.clearUserData()
160
- resolve(true)
161
- }
162
-
163
- @objc func updateMetaTrackingAuthorization(
164
- _ enabled: Bool,
165
- resolve: @escaping RCTPromiseResolveBlock,
166
- reject: @escaping RCTPromiseRejectBlock
167
- ) {
168
- Settings.shared.isAdvertiserTrackingEnabled = enabled
169
- Settings.shared.isAdvertiserIDCollectionEnabled = enabled
170
- resolve(true)
171
- }
172
-
173
- // MARK: - TikTok SDK Methods
174
-
175
- @objc func initializeTikTokSDK(
176
- _ appId: String,
177
- tiktokAppId: String,
178
- accessToken: String?,
179
- debug: Bool,
180
- resolve: @escaping RCTPromiseResolveBlock,
181
- reject: @escaping RCTPromiseRejectBlock
182
- ) {
183
- DispatchQueue.main.async {
184
- let config = TikTokConfig(appId: appId, tiktokAppId: tiktokAppId)
185
-
186
- if let token = accessToken, !token.isEmpty {
187
- config?.accessToken = token
188
- }
189
-
190
- if debug {
191
- config?.setLogLevel(.debug)
192
- }
193
-
194
- if let validConfig = config {
195
- TikTokBusiness.initializeSdk(validConfig)
196
- resolve(true)
197
- } else {
198
- reject("tiktok_init_error", "Failed to create TikTok config", nil)
199
- }
200
- }
201
- }
202
-
203
- @objc func trackTikTokEvent(
204
- _ eventName: String,
205
- eventId: String?,
206
- properties: NSDictionary?,
207
- resolve: @escaping RCTPromiseResolveBlock,
208
- reject: @escaping RCTPromiseRejectBlock
209
- ) {
210
- // Use TikTokBaseEvent for modern API (trackEvent methods are deprecated)
211
- let event: TikTokBaseEvent
212
-
213
- if let eid = eventId, !eid.isEmpty {
214
- event = TikTokBaseEvent(eventName: eventName, eventId: eid)
215
- } else {
216
- event = TikTokBaseEvent(eventName: eventName)
217
- }
218
-
219
- // Add properties to the event
220
- if let dict = properties as? [String: Any] {
221
- for (key, value) in dict {
222
- event.addProperty(withKey: key, value: value)
223
- }
224
- }
225
-
226
- TikTokBusiness.trackTTEvent(event)
227
- resolve(true)
228
- }
229
-
230
- @objc func identifyTikTokUser(
231
- _ externalId: String,
232
- externalUserName: String,
233
- phoneNumber: String,
234
- email: String,
235
- resolve: @escaping RCTPromiseResolveBlock,
236
- reject: @escaping RCTPromiseRejectBlock
237
- ) {
238
- // Method signature: identifyWithExternalID:externalUserName:phoneNumber:email:
239
- TikTokBusiness.identify(
240
- withExternalID: externalId.isEmpty ? nil : externalId,
241
- externalUserName: externalUserName.isEmpty ? nil : externalUserName,
242
- phoneNumber: phoneNumber.isEmpty ? nil : phoneNumber,
243
- email: email.isEmpty ? nil : email
244
- )
245
- resolve(true)
246
- }
247
-
248
- @objc func logoutTikTok(
249
- _ resolve: @escaping RCTPromiseResolveBlock,
250
- reject: @escaping RCTPromiseRejectBlock
251
- ) {
252
- TikTokBusiness.logout()
253
- resolve(true)
254
- }
255
-
256
- @objc func updateTikTokTrackingAuthorization(
257
- _ enabled: Bool,
258
- resolve: @escaping RCTPromiseResolveBlock,
259
- reject: @escaping RCTPromiseRejectBlock
260
- ) {
261
- // TikTok SDK handles ATT automatically, but we track the change
262
- resolve(true)
263
- }
264
-
265
- // MARK: - SDK Availability Check
266
-
267
- @objc func getSDKAvailability(
268
- _ resolve: @escaping RCTPromiseResolveBlock,
269
- reject: @escaping RCTPromiseRejectBlock
270
- ) {
271
- resolve([
272
- "meta": true,
273
- "tiktok": true,
274
- "appleSearchAds": true
275
- ])
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
- }
332
- }