@datalyr/react-native 1.4.7 → 1.4.8
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.
|
@@ -4,13 +4,16 @@ import TikTokBusinessSDK
|
|
|
4
4
|
import AdServices
|
|
5
5
|
|
|
6
6
|
public class DatalyrNativeModule: Module {
|
|
7
|
+
private var tiktokInitialized = false
|
|
8
|
+
private var metaInitialized = false
|
|
9
|
+
|
|
7
10
|
public func definition() -> ModuleDefinition {
|
|
8
11
|
Name("DatalyrNative")
|
|
9
12
|
|
|
10
13
|
// MARK: - Meta (Facebook) SDK Methods
|
|
11
14
|
|
|
12
15
|
AsyncFunction("initializeMetaSDK") { (appId: String, clientToken: String?, advertiserTrackingEnabled: Bool, promise: Promise) in
|
|
13
|
-
DispatchQueue.main.async {
|
|
16
|
+
DispatchQueue.main.async { [weak self] in
|
|
14
17
|
Settings.shared.appID = appId
|
|
15
18
|
|
|
16
19
|
if let token = clientToken, !token.isEmpty {
|
|
@@ -20,145 +23,246 @@ public class DatalyrNativeModule: Module {
|
|
|
20
23
|
Settings.shared.isAdvertiserTrackingEnabled = advertiserTrackingEnabled
|
|
21
24
|
Settings.shared.isAdvertiserIDCollectionEnabled = advertiserTrackingEnabled
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
var initError: NSError?
|
|
27
|
+
let success = DatalyrObjCExceptionCatcher.tryBlock({
|
|
28
|
+
ApplicationDelegate.shared.application(
|
|
29
|
+
UIApplication.shared,
|
|
30
|
+
didFinishLaunchingWithOptions: nil
|
|
31
|
+
)
|
|
32
|
+
}, error: &initError)
|
|
27
33
|
|
|
28
|
-
|
|
34
|
+
if success {
|
|
35
|
+
self?.metaInitialized = true
|
|
36
|
+
promise.resolve(true)
|
|
37
|
+
} else {
|
|
38
|
+
let message = initError?.localizedDescription ?? "Unknown ObjC exception during Meta SDK init"
|
|
39
|
+
promise.reject("meta_init_error", message)
|
|
40
|
+
}
|
|
29
41
|
}
|
|
30
42
|
}
|
|
31
43
|
|
|
32
44
|
AsyncFunction("fetchDeferredAppLink") { (promise: Promise) in
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
45
|
+
DispatchQueue.main.async {
|
|
46
|
+
AppLinkUtility.fetchDeferredAppLink { url, error in
|
|
47
|
+
if error != nil {
|
|
48
|
+
promise.resolve(nil)
|
|
49
|
+
return
|
|
50
|
+
}
|
|
38
51
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
if let url = url {
|
|
53
|
+
promise.resolve(url.absoluteString)
|
|
54
|
+
} else {
|
|
55
|
+
promise.resolve(nil)
|
|
56
|
+
}
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
AsyncFunction("logMetaEvent") { (eventName: String, valueToSum: Double?, parameters: [String: Any]?, promise: Promise) in
|
|
48
|
-
|
|
62
|
+
guard self.metaInitialized else {
|
|
63
|
+
promise.reject("meta_not_initialized", "Meta SDK not initialized. Call initializeMetaSDK first.")
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
DispatchQueue.main.async {
|
|
68
|
+
var params: [AppEvents.ParameterName: Any] = [:]
|
|
49
69
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
if let dict = parameters {
|
|
71
|
+
for (key, value) in dict {
|
|
72
|
+
params[AppEvents.ParameterName(key)] = value
|
|
73
|
+
}
|
|
53
74
|
}
|
|
54
|
-
}
|
|
55
75
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
var logError: NSError?
|
|
77
|
+
DatalyrObjCExceptionCatcher.tryBlock({
|
|
78
|
+
if let value = valueToSum {
|
|
79
|
+
AppEvents.shared.logEvent(AppEvents.Name(eventName), valueToSum: value, parameters: params)
|
|
80
|
+
} else if params.isEmpty {
|
|
81
|
+
AppEvents.shared.logEvent(AppEvents.Name(eventName))
|
|
82
|
+
} else {
|
|
83
|
+
AppEvents.shared.logEvent(AppEvents.Name(eventName), parameters: params)
|
|
84
|
+
}
|
|
85
|
+
}, error: &logError)
|
|
63
86
|
|
|
64
|
-
|
|
87
|
+
if let logError = logError {
|
|
88
|
+
promise.reject("meta_event_error", logError.localizedDescription)
|
|
89
|
+
} else {
|
|
90
|
+
promise.resolve(true)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
65
93
|
}
|
|
66
94
|
|
|
67
95
|
AsyncFunction("logMetaPurchase") { (amount: Double, currency: String, parameters: [String: Any]?, promise: Promise) in
|
|
68
|
-
|
|
96
|
+
guard self.metaInitialized else {
|
|
97
|
+
promise.reject("meta_not_initialized", "Meta SDK not initialized. Call initializeMetaSDK first.")
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
DispatchQueue.main.async {
|
|
102
|
+
var params: [AppEvents.ParameterName: Any] = [:]
|
|
69
103
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
104
|
+
if let dict = parameters {
|
|
105
|
+
for (key, value) in dict {
|
|
106
|
+
params[AppEvents.ParameterName(key)] = value
|
|
107
|
+
}
|
|
73
108
|
}
|
|
74
|
-
}
|
|
75
109
|
|
|
76
|
-
|
|
77
|
-
|
|
110
|
+
var logError: NSError?
|
|
111
|
+
DatalyrObjCExceptionCatcher.tryBlock({
|
|
112
|
+
AppEvents.shared.logPurchase(amount: amount, currency: currency, parameters: params)
|
|
113
|
+
}, error: &logError)
|
|
114
|
+
|
|
115
|
+
if let logError = logError {
|
|
116
|
+
promise.reject("meta_event_error", logError.localizedDescription)
|
|
117
|
+
} else {
|
|
118
|
+
promise.resolve(true)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
78
121
|
}
|
|
79
122
|
|
|
80
123
|
AsyncFunction("setMetaUserData") { (userData: [String: Any], promise: Promise) in
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
AppEvents.shared.setUserData(userData["dateOfBirth"] as? String, forType: .dateOfBirth)
|
|
86
|
-
AppEvents.shared.setUserData(userData["gender"] as? String, forType: .gender)
|
|
87
|
-
AppEvents.shared.setUserData(userData["city"] as? String, forType: .city)
|
|
88
|
-
AppEvents.shared.setUserData(userData["state"] as? String, forType: .state)
|
|
89
|
-
AppEvents.shared.setUserData(userData["zip"] as? String, forType: .zip)
|
|
90
|
-
AppEvents.shared.setUserData(userData["country"] as? String, forType: .country)
|
|
124
|
+
guard self.metaInitialized else {
|
|
125
|
+
promise.reject("meta_not_initialized", "Meta SDK not initialized. Call initializeMetaSDK first.")
|
|
126
|
+
return
|
|
127
|
+
}
|
|
91
128
|
|
|
92
|
-
|
|
129
|
+
DispatchQueue.main.async {
|
|
130
|
+
if let email = userData["email"] as? String { AppEvents.shared.setUserData(email, forType: .email) }
|
|
131
|
+
if let firstName = userData["firstName"] as? String { AppEvents.shared.setUserData(firstName, forType: .firstName) }
|
|
132
|
+
if let lastName = userData["lastName"] as? String { AppEvents.shared.setUserData(lastName, forType: .lastName) }
|
|
133
|
+
if let phone = userData["phone"] as? String { AppEvents.shared.setUserData(phone, forType: .phone) }
|
|
134
|
+
if let dateOfBirth = userData["dateOfBirth"] as? String { AppEvents.shared.setUserData(dateOfBirth, forType: .dateOfBirth) }
|
|
135
|
+
if let gender = userData["gender"] as? String { AppEvents.shared.setUserData(gender, forType: .gender) }
|
|
136
|
+
if let city = userData["city"] as? String { AppEvents.shared.setUserData(city, forType: .city) }
|
|
137
|
+
if let state = userData["state"] as? String { AppEvents.shared.setUserData(state, forType: .state) }
|
|
138
|
+
if let zip = userData["zip"] as? String { AppEvents.shared.setUserData(zip, forType: .zip) }
|
|
139
|
+
if let country = userData["country"] as? String { AppEvents.shared.setUserData(country, forType: .country) }
|
|
140
|
+
|
|
141
|
+
promise.resolve(true)
|
|
142
|
+
}
|
|
93
143
|
}
|
|
94
144
|
|
|
95
145
|
AsyncFunction("clearMetaUserData") { (promise: Promise) in
|
|
96
|
-
|
|
97
|
-
|
|
146
|
+
guard self.metaInitialized else {
|
|
147
|
+
promise.reject("meta_not_initialized", "Meta SDK not initialized. Call initializeMetaSDK first.")
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
DispatchQueue.main.async {
|
|
152
|
+
AppEvents.shared.clearUserData()
|
|
153
|
+
promise.resolve(true)
|
|
154
|
+
}
|
|
98
155
|
}
|
|
99
156
|
|
|
100
157
|
AsyncFunction("updateMetaTrackingAuthorization") { (enabled: Bool, promise: Promise) in
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
158
|
+
guard self.metaInitialized else {
|
|
159
|
+
promise.reject("meta_not_initialized", "Meta SDK not initialized. Call initializeMetaSDK first.")
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
DispatchQueue.main.async {
|
|
164
|
+
Settings.shared.isAdvertiserTrackingEnabled = enabled
|
|
165
|
+
Settings.shared.isAdvertiserIDCollectionEnabled = enabled
|
|
166
|
+
promise.resolve(true)
|
|
167
|
+
}
|
|
104
168
|
}
|
|
105
169
|
|
|
106
170
|
// MARK: - TikTok SDK Methods
|
|
107
171
|
|
|
108
172
|
AsyncFunction("initializeTikTokSDK") { (appId: String, tiktokAppId: String, accessToken: String?, debug: Bool, promise: Promise) in
|
|
109
|
-
DispatchQueue.main.async {
|
|
110
|
-
let
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
} else {
|
|
114
|
-
config = TikTokConfig(appId: appId, tiktokAppId: tiktokAppId)
|
|
173
|
+
DispatchQueue.main.async { [weak self] in
|
|
174
|
+
guard let token = accessToken, !token.isEmpty else {
|
|
175
|
+
promise.reject("tiktok_init_error", "TikTok accessToken is required. The deprecated init without accessToken has been removed.")
|
|
176
|
+
return
|
|
115
177
|
}
|
|
116
178
|
|
|
179
|
+
let config = TikTokConfig(accessToken: token, appId: appId, tiktokAppId: tiktokAppId)
|
|
180
|
+
|
|
117
181
|
if debug {
|
|
118
182
|
config?.setLogLevel(TikTokLogLevelDebug)
|
|
119
183
|
}
|
|
120
184
|
|
|
121
|
-
|
|
185
|
+
guard let validConfig = config else {
|
|
186
|
+
promise.reject("tiktok_init_error", "Failed to create TikTok config")
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
var initError: NSError?
|
|
191
|
+
let success = DatalyrObjCExceptionCatcher.tryBlock({
|
|
122
192
|
TikTokBusiness.initializeSdk(validConfig)
|
|
193
|
+
}, error: &initError)
|
|
194
|
+
|
|
195
|
+
if success {
|
|
196
|
+
self?.tiktokInitialized = true
|
|
123
197
|
promise.resolve(true)
|
|
124
198
|
} else {
|
|
125
|
-
|
|
199
|
+
let message = initError?.localizedDescription ?? "Unknown ObjC exception during TikTok SDK init"
|
|
200
|
+
promise.reject("tiktok_init_error", message)
|
|
126
201
|
}
|
|
127
202
|
}
|
|
128
203
|
}
|
|
129
204
|
|
|
130
205
|
AsyncFunction("trackTikTokEvent") { (eventName: String, eventId: String?, properties: [String: Any]?, promise: Promise) in
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
event = TikTokBaseEvent(eventName: eventName, eventId: eid)
|
|
135
|
-
} else {
|
|
136
|
-
event = TikTokBaseEvent(eventName: eventName)
|
|
206
|
+
guard self.tiktokInitialized else {
|
|
207
|
+
promise.reject("tiktok_not_initialized", "TikTok SDK not initialized. Call initializeTikTokSDK first.")
|
|
208
|
+
return
|
|
137
209
|
}
|
|
138
210
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
211
|
+
DispatchQueue.main.async {
|
|
212
|
+
let event: TikTokBaseEvent
|
|
213
|
+
|
|
214
|
+
if let eid = eventId, !eid.isEmpty {
|
|
215
|
+
event = TikTokBaseEvent(eventName: eventName, eventId: eid)
|
|
216
|
+
} else {
|
|
217
|
+
event = TikTokBaseEvent(eventName: eventName)
|
|
142
218
|
}
|
|
143
|
-
}
|
|
144
219
|
|
|
145
|
-
|
|
146
|
-
|
|
220
|
+
if let dict = properties {
|
|
221
|
+
for (key, value) in dict {
|
|
222
|
+
event.addProperty(withKey: key, value: value)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
var trackError: NSError?
|
|
227
|
+
DatalyrObjCExceptionCatcher.tryBlock({
|
|
228
|
+
TikTokBusiness.trackTTEvent(event)
|
|
229
|
+
}, error: &trackError)
|
|
230
|
+
|
|
231
|
+
if let trackError = trackError {
|
|
232
|
+
promise.reject("tiktok_event_error", trackError.localizedDescription)
|
|
233
|
+
} else {
|
|
234
|
+
promise.resolve(true)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
147
237
|
}
|
|
148
238
|
|
|
149
239
|
AsyncFunction("identifyTikTokUser") { (externalId: String, externalUserName: String, phoneNumber: String, email: String, promise: Promise) in
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
240
|
+
guard self.tiktokInitialized else {
|
|
241
|
+
promise.reject("tiktok_not_initialized", "TikTok SDK not initialized. Call initializeTikTokSDK first.")
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
DispatchQueue.main.async {
|
|
246
|
+
TikTokBusiness.identify(
|
|
247
|
+
withExternalID: externalId.isEmpty ? nil : externalId,
|
|
248
|
+
externalUserName: externalUserName.isEmpty ? nil : externalUserName,
|
|
249
|
+
phoneNumber: phoneNumber.isEmpty ? nil : phoneNumber,
|
|
250
|
+
email: email.isEmpty ? nil : email
|
|
251
|
+
)
|
|
252
|
+
promise.resolve(true)
|
|
253
|
+
}
|
|
157
254
|
}
|
|
158
255
|
|
|
159
256
|
AsyncFunction("logoutTikTok") { (promise: Promise) in
|
|
160
|
-
|
|
161
|
-
|
|
257
|
+
guard self.tiktokInitialized else {
|
|
258
|
+
promise.reject("tiktok_not_initialized", "TikTok SDK not initialized. Call initializeTikTokSDK first.")
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
DispatchQueue.main.async {
|
|
263
|
+
TikTokBusiness.logout()
|
|
264
|
+
promise.resolve(true)
|
|
265
|
+
}
|
|
162
266
|
}
|
|
163
267
|
|
|
164
268
|
AsyncFunction("updateTikTokTrackingAuthorization") { (enabled: Bool, promise: Promise) in
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
/// Catches ObjC NSExceptions (from Meta/TikTok SDKs) and converts them to NSErrors.
|
|
6
|
+
/// Swift's do/try/catch cannot catch NSExceptions — they propagate through Hermes
|
|
7
|
+
/// and cause EXC_BAD_ACCESS (SIGSEGV) crashes.
|
|
8
|
+
@interface DatalyrObjCExceptionCatcher : NSObject
|
|
9
|
+
|
|
10
|
+
+ (BOOL)tryBlock:(void(NS_NOESCAPE ^)(void))block error:(NSError *_Nullable *_Nullable)error;
|
|
11
|
+
|
|
12
|
+
@end
|
|
13
|
+
|
|
14
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#import "DatalyrObjCExceptionCatcher.h"
|
|
2
|
+
|
|
3
|
+
@implementation DatalyrObjCExceptionCatcher
|
|
4
|
+
|
|
5
|
+
+ (BOOL)tryBlock:(void(NS_NOESCAPE ^)(void))block error:(NSError **)error {
|
|
6
|
+
@try {
|
|
7
|
+
block();
|
|
8
|
+
return YES;
|
|
9
|
+
}
|
|
10
|
+
@catch (NSException *exception) {
|
|
11
|
+
if (error) {
|
|
12
|
+
NSString *description = exception.reason ?: exception.name;
|
|
13
|
+
NSDictionary *userInfo = @{
|
|
14
|
+
NSLocalizedDescriptionKey: description,
|
|
15
|
+
@"ExceptionName": exception.name ?: @"Unknown",
|
|
16
|
+
};
|
|
17
|
+
if (exception.userInfo) {
|
|
18
|
+
NSMutableDictionary *merged = [userInfo mutableCopy];
|
|
19
|
+
[merged addEntriesFromDictionary:exception.userInfo];
|
|
20
|
+
userInfo = merged;
|
|
21
|
+
}
|
|
22
|
+
*error = [NSError errorWithDomain:@"com.datalyr.objc-exception"
|
|
23
|
+
code:-1
|
|
24
|
+
userInfo:userInfo];
|
|
25
|
+
}
|
|
26
|
+
return NO;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@end
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalyr/react-native",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.8",
|
|
4
4
|
"description": "Datalyr SDK for React Native & Expo - Server-side attribution tracking with bundled Meta and TikTok SDKs for iOS and Android",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|