@datalyr/react-native 1.3.0 → 1.4.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 +19 -0
- package/README.md +145 -9
- package/datalyr-react-native.podspec +1 -1
- package/expo-module.config.json +6 -0
- package/ios/DatalyrNativeModule.swift +221 -0
- package/ios/DatalyrSKAdNetworkModule.swift +333 -0
- package/ios/PrivacyInfo.xcprivacy +48 -0
- package/lib/datalyr-sdk.d.ts +6 -0
- package/lib/datalyr-sdk.js +84 -27
- package/lib/index.d.ts +3 -1
- package/lib/index.js +3 -1
- package/lib/integrations/play-install-referrer.d.ts +5 -1
- package/lib/integrations/play-install-referrer.js +14 -4
- package/lib/native/DatalyrNativeBridge.js +20 -4
- package/lib/native/SKAdNetworkBridge.d.ts +121 -0
- package/lib/native/SKAdNetworkBridge.js +288 -4
- package/lib/network-status.d.ts +84 -0
- package/lib/network-status.js +281 -0
- package/lib/types.d.ts +51 -0
- package/lib/utils.d.ts +6 -1
- package/lib/utils.js +52 -2
- package/package.json +12 -2
- package/src/datalyr-sdk.ts +96 -32
- package/src/index.ts +5 -1
- package/src/integrations/play-install-referrer.ts +19 -4
- package/src/native/DatalyrNativeBridge.ts +17 -4
- package/src/native/SKAdNetworkBridge.ts +411 -9
- package/src/network-status.ts +312 -0
- package/src/types.ts +74 -6
- package/src/utils.ts +62 -6
- package/ios/DatalyrNative.m +0 -74
- package/ios/DatalyrNative.swift +0 -332
- package/ios/DatalyrSKAdNetwork.m +0 -77
package/ios/DatalyrNative.swift
DELETED
|
@@ -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
|
-
}
|
package/ios/DatalyrSKAdNetwork.m
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
#import <React/RCTBridgeModule.h>
|
|
2
|
-
#import <StoreKit/StoreKit.h>
|
|
3
|
-
|
|
4
|
-
@interface DatalyrSKAdNetwork : NSObject <RCTBridgeModule>
|
|
5
|
-
@end
|
|
6
|
-
|
|
7
|
-
@implementation DatalyrSKAdNetwork
|
|
8
|
-
|
|
9
|
-
RCT_EXPORT_MODULE();
|
|
10
|
-
|
|
11
|
-
// SKAN 3.0 - Legacy method for iOS 14.0-16.0
|
|
12
|
-
RCT_EXPORT_METHOD(updateConversionValue:(NSInteger)value
|
|
13
|
-
resolve:(RCTPromiseResolveBlock)resolve
|
|
14
|
-
reject:(RCTPromiseRejectBlock)reject) {
|
|
15
|
-
if (@available(iOS 14.0, *)) {
|
|
16
|
-
@try {
|
|
17
|
-
[SKAdNetwork updateConversionValue:value];
|
|
18
|
-
resolve(@(YES));
|
|
19
|
-
} @catch (NSException *exception) {
|
|
20
|
-
reject(@"skadnetwork_error", exception.reason, nil);
|
|
21
|
-
}
|
|
22
|
-
} else {
|
|
23
|
-
reject(@"ios_version_error", @"SKAdNetwork requires iOS 14.0+", nil);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
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
|