@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
- ApplicationDelegate.shared.application(
24
- UIApplication.shared,
25
- didFinishLaunchingWithOptions: nil
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
- promise.resolve(true)
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
- AppLinkUtility.fetchDeferredAppLink { url, error in
34
- if error != nil {
35
- promise.resolve(nil)
36
- return
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
- if let url = url {
40
- promise.resolve(url.absoluteString)
41
- } else {
42
- promise.resolve(nil)
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
- var params: [AppEvents.ParameterName: Any] = [:]
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
- if let dict = parameters {
51
- for (key, value) in dict {
52
- params[AppEvents.ParameterName(key)] = value
70
+ if let dict = parameters {
71
+ for (key, value) in dict {
72
+ params[AppEvents.ParameterName(key)] = value
73
+ }
53
74
  }
54
- }
55
75
 
56
- if let value = valueToSum {
57
- AppEvents.shared.logEvent(AppEvents.Name(eventName), valueToSum: value, parameters: params)
58
- } else if params.isEmpty {
59
- AppEvents.shared.logEvent(AppEvents.Name(eventName))
60
- } else {
61
- AppEvents.shared.logEvent(AppEvents.Name(eventName), parameters: params)
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
- promise.resolve(true)
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
- var params: [AppEvents.ParameterName: Any] = [:]
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
- if let dict = parameters {
71
- for (key, value) in dict {
72
- params[AppEvents.ParameterName(key)] = value
104
+ if let dict = parameters {
105
+ for (key, value) in dict {
106
+ params[AppEvents.ParameterName(key)] = value
107
+ }
73
108
  }
74
- }
75
109
 
76
- AppEvents.shared.logPurchase(amount: amount, currency: currency, parameters: params)
77
- promise.resolve(true)
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
- AppEvents.shared.setUserData(userData["email"] as? String, forType: .email)
82
- AppEvents.shared.setUserData(userData["firstName"] as? String, forType: .firstName)
83
- AppEvents.shared.setUserData(userData["lastName"] as? String, forType: .lastName)
84
- AppEvents.shared.setUserData(userData["phone"] as? String, forType: .phone)
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
- promise.resolve(true)
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
- AppEvents.shared.clearUserData()
97
- promise.resolve(true)
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
- Settings.shared.isAdvertiserTrackingEnabled = enabled
102
- Settings.shared.isAdvertiserIDCollectionEnabled = enabled
103
- promise.resolve(true)
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 config: TikTokConfig?
111
- if let token = accessToken, !token.isEmpty {
112
- config = TikTokConfig(accessToken: token, appId: appId, tiktokAppId: tiktokAppId)
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
- if let validConfig = config {
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
- promise.reject("tiktok_init_error", "Failed to create TikTok config")
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
- let event: TikTokBaseEvent
132
-
133
- if let eid = eventId, !eid.isEmpty {
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
- if let dict = properties {
140
- for (key, value) in dict {
141
- event.addProperty(withKey: key, value: value)
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
- TikTokBusiness.trackTTEvent(event)
146
- promise.resolve(true)
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
- TikTokBusiness.identify(
151
- withExternalID: externalId.isEmpty ? nil : externalId,
152
- externalUserName: externalUserName.isEmpty ? nil : externalUserName,
153
- phoneNumber: phoneNumber.isEmpty ? nil : phoneNumber,
154
- email: email.isEmpty ? nil : email
155
- )
156
- promise.resolve(true)
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
- TikTokBusiness.logout()
161
- promise.resolve(true)
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.7",
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",