@react-native-firebase/analytics 26.3.3 → 27.0.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 (40) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/RNFBAnalytics.podspec +49 -2
  3. package/android/src/main/java/io/invertase/firebase/analytics/UniversalFirebaseAnalyticsModule.java +17 -15
  4. package/android/src/reactnative/java/io/invertase/firebase/analytics/NativeRNFBTurboAnalytics.java +4 -3
  5. package/dist/module/index.js +26 -11
  6. package/dist/module/index.js.map +1 -1
  7. package/dist/module/version.js +1 -1
  8. package/dist/module/web/api.js +42 -19
  9. package/dist/module/web/api.js.map +1 -1
  10. package/dist/typescript/lib/index.d.ts +2 -2
  11. package/dist/typescript/lib/index.d.ts.map +1 -1
  12. package/dist/typescript/lib/version.d.ts +1 -1
  13. package/dist/typescript/lib/web/api.d.ts +4 -1
  14. package/dist/typescript/lib/web/api.d.ts.map +1 -1
  15. package/ios/RNFBAnalytics/RNFBAnalyticsConsentSettingsMapper.swift +98 -0
  16. package/ios/RNFBAnalytics/RNFBAnalyticsHelper.h +111 -0
  17. package/ios/RNFBAnalytics/RNFBAnalyticsHelper.m +338 -0
  18. package/ios/RNFBAnalytics/RNFBAnalyticsJavascriptParamsCleaner.swift +167 -0
  19. package/ios/RNFBAnalytics/RNFBAnalyticsLogTransaction.swift +2 -2
  20. package/ios/RNFBAnalytics/RNFBAnalyticsModule.mm +32 -251
  21. package/ios/RNFBAnalytics.xcodeproj/project.pbxproj +16 -2
  22. package/ios/RNFBAnalyticsUnitTests/HostStubs/Firebase/FIRAnalyticsHostStub.m +304 -0
  23. package/ios/RNFBAnalyticsUnitTests/HostStubs/Firebase/Firebase.h +71 -0
  24. package/ios/RNFBAnalyticsUnitTests/HostStubs/RNFBAnalytics/RNFBAnalyticsLogTransactionHostStub.h +20 -0
  25. package/ios/RNFBAnalyticsUnitTests/HostStubs/RNFBAnalytics/RNFBAnalyticsLogTransactionHostStub.m +43 -0
  26. package/ios/RNFBAnalyticsUnitTests/HostStubs/RNFBApp/RNFBSharedUtils.h +21 -0
  27. package/ios/RNFBAnalyticsUnitTests/HostStubs/RNFBApp/RNFBSharedUtilsHostStub.m +26 -0
  28. package/ios/RNFBAnalyticsUnitTests/HostStubs/React/RCTBridgeModule.h +10 -0
  29. package/ios/RNFBAnalyticsUnitTests/RNFBAnalyticsConsentSettingsMapperHostStubTests.m +62 -0
  30. package/ios/RNFBAnalyticsUnitTests/RNFBAnalyticsConsentSettingsMapperTests.swift +91 -0
  31. package/ios/RNFBAnalyticsUnitTests/RNFBAnalyticsHelperHostStubTests.m +542 -0
  32. package/ios/RNFBAnalyticsUnitTests/RNFBAnalyticsJavascriptParamsCleanerHostStubTests.m +59 -0
  33. package/ios/RNFBAnalyticsUnitTests/RNFBAnalyticsJavascriptParamsCleanerTests.swift +205 -0
  34. package/ios/RNFBAnalyticsUnitTests/RNFBAnalyticsNullSentinelDecodeTests.m +111 -0
  35. package/ios/RNFBAnalyticsUnitTests/RNFBAnalyticsUnitTests.xcodeproj/project.pbxproj +302 -0
  36. package/ios/RNFBAnalyticsUnitTests/RNFBAnalyticsUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBAnalyticsUnitTests.xcscheme +69 -0
  37. package/lib/index.ts +38 -10
  38. package/lib/version.ts +1 -1
  39. package/lib/web/api.ts +51 -19
  40. package/package.json +5 -4
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import Foundation
18
+
19
+ /**
20
+ * Foundation-friendly Analytics JS param helpers previously inlined in
21
+ * `RNFBAnalyticsModule.mm`.
22
+ *
23
+ * Pre-port contracts:
24
+ * - NSNull over the bridge → nil (`convertNSNullToNil`)
25
+ * - 64 hex chars → 32-byte `Data` or nil (`dataFromSHA256HexString`)
26
+ * - Long-numeric GA4 keys coerced via `integerValue`
27
+ * - `success` string/bool coercion to 0/1 `NSNumber`
28
+ * - `extend_session` `@1` → `@YES`
29
+ *
30
+ * Parameter key strings match Firebase Analytics `kFIRParameter*` values
31
+ * (Foundation-only so unit tests do not link the Analytics SDK).
32
+ */
33
+ @objc(RNFBAnalyticsJavascriptParamsCleaner)
34
+ public final class RNFBAnalyticsJavascriptParamsCleaner: NSObject {
35
+ // MARK: - Firebase Analytics parameter key strings (kFIRParameter*)
36
+
37
+ private static let parameterQuantity = "quantity"
38
+ private static let parameterIndex = "index"
39
+ private static let parameterLevel = "level"
40
+ private static let parameterNumberOfNights = "number_of_nights"
41
+ private static let parameterNumberOfPassengers = "number_of_passengers"
42
+ private static let parameterNumberOfRooms = "number_of_rooms"
43
+ private static let parameterScore = "score"
44
+ private static let parameterItems = "items"
45
+ private static let parameterSuccess = "success"
46
+ private static let parameterExtendSession = "extend_session"
47
+
48
+ /// Same keys as pre-port `RNFBAnalyticsLongNumericParameterKeys`.
49
+ @objc public static var longNumericParameterKeys: [String] {
50
+ [
51
+ parameterQuantity,
52
+ parameterIndex,
53
+ parameterLevel,
54
+ parameterNumberOfNights,
55
+ parameterNumberOfPassengers,
56
+ parameterNumberOfRooms,
57
+ parameterScore,
58
+ ]
59
+ }
60
+
61
+ // MARK: - NSNull bridge
62
+
63
+ /// Converts null values received over the bridge from NSNull to nil.
64
+ @objc(convertNSNullToNil:)
65
+ public static func convertNSNullToNil(_ value: Any?) -> String? {
66
+ // Pre-port: `[value isEqual:[NSNull null]] ? nil : value`
67
+ if let value, (value as AnyObject).isEqual(NSNull()) {
68
+ return nil
69
+ }
70
+ return value as? String
71
+ }
72
+
73
+ // MARK: - SHA-256 hex
74
+
75
+ private static func hexDigit(_ character: unichar) -> Int {
76
+ if character >= 0x30 && character <= 0x39 {
77
+ return Int(character) - 0x30
78
+ }
79
+ if character >= 0x61 && character <= 0x66 {
80
+ return Int(character) - 0x61 + 10
81
+ }
82
+ if character >= 0x41 && character <= 0x46 {
83
+ return Int(character) - 0x41 + 10
84
+ }
85
+ return -1
86
+ }
87
+
88
+ /// 64 hex characters → 32-byte `Data`, or nil when length/hex is invalid.
89
+ @objc(dataFromSHA256HexString:)
90
+ public static func dataFromSHA256HexString(_ hexString: String?) -> Data? {
91
+ guard let hexString, (hexString as NSString).length == 64 else {
92
+ return nil
93
+ }
94
+
95
+ var bytes = [UInt8](repeating: 0, count: 32)
96
+ let nsString = hexString as NSString
97
+ for i in 0..<32 {
98
+ let high = hexDigit(nsString.character(at: i * 2))
99
+ let low = hexDigit(nsString.character(at: i * 2 + 1))
100
+ if high < 0 || low < 0 {
101
+ return nil
102
+ }
103
+ bytes[i] = UInt8((high << 4) | low)
104
+ }
105
+ return Data(bytes)
106
+ }
107
+
108
+ // MARK: - JS params cleaning
109
+
110
+ @objc(cleanJavascriptParams:)
111
+ public static func cleanJavascriptParams(_ params: NSDictionary?) -> NSDictionary? {
112
+ guard let params else {
113
+ return nil
114
+ }
115
+ let newParams = NSMutableDictionary(dictionary: params)
116
+
117
+ if newParams[parameterItems] != nil {
118
+ let newItems = NSMutableArray()
119
+ let items = newParams[parameterItems] as! NSArray
120
+ items.enumerateObjects { obj, _, _ in
121
+ let item = (obj as AnyObject).mutableCopy() as! NSMutableDictionary
122
+ coerceLongNumericParameters(in: item)
123
+ newItems.add(item.copy())
124
+ }
125
+ newParams[parameterItems] = newItems.copy()
126
+ }
127
+
128
+ coerceLongNumericParameters(in: newParams)
129
+ coerceSuccessParameter(in: newParams)
130
+
131
+ if let extendSession = newParams.value(forKey: parameterExtendSession) as? NSNumber,
132
+ extendSession.isEqual(to: NSNumber(value: 1))
133
+ {
134
+ newParams[parameterExtendSession] = NSNumber(value: true)
135
+ }
136
+
137
+ return newParams.copy() as? NSDictionary
138
+ }
139
+
140
+ private static func coerceLongNumericParameters(in dict: NSMutableDictionary) {
141
+ for key in longNumericParameterKeys {
142
+ guard let value = dict[key], !(value is NSNull) else {
143
+ continue
144
+ }
145
+ // Pre-port: `dict[key] = @([value integerValue]);`
146
+ dict[key] = NSNumber(value: (value as AnyObject).integerValue)
147
+ }
148
+ }
149
+
150
+ private static func coerceSuccessParameter(in dict: NSMutableDictionary) {
151
+ guard let value = dict[parameterSuccess], !(value is NSNull) else {
152
+ return
153
+ }
154
+
155
+ var success = 0
156
+ if let string = value as? String {
157
+ let lower = string.lowercased()
158
+ if lower == "true" || lower == "yes" || lower == "1" {
159
+ success = 1
160
+ }
161
+ } else {
162
+ // Pre-port: `success = [value boolValue] ? 1 : 0;`
163
+ success = (value as AnyObject).boolValue ? 1 : 0
164
+ }
165
+ dict[parameterSuccess] = NSNumber(value: success)
166
+ }
167
+ }
@@ -51,12 +51,12 @@ import StoreKit
51
51
  }
52
52
 
53
53
  var foundTransaction: StoreKit.Transaction?
54
- for await result in StoreKit.Transaction.all {
54
+ transactionSearch: for await result in StoreKit.Transaction.all {
55
55
  switch result {
56
56
  case let .verified(transaction):
57
57
  if transaction.id == id {
58
58
  foundTransaction = transaction
59
- break
59
+ break transactionSearch
60
60
  }
61
61
  case .unverified:
62
62
  continue
@@ -15,48 +15,14 @@
15
15
  *
16
16
  */
17
17
 
18
- #if __has_include(<Firebase/Firebase.h>)
19
- #import <Firebase/Firebase.h>
20
- #elif __has_include(<FirebaseAnalytics/FirebaseAnalytics.h>)
21
- #import <FirebaseAnalytics/FirebaseAnalytics.h>
22
- #import <FirebaseCore/FirebaseCore.h>
23
- #else
24
- @import FirebaseCore;
25
- @import FirebaseAnalytics;
26
- #endif
27
- #import <React/RCTUtils.h>
28
-
29
- #if __has_include(<RNFBAnalytics/RNFBAnalytics-Swift.h>)
30
- // This import will work in situations where `use_frameworks!` is in use
31
- #import <RNFBAnalytics/RNFBAnalytics-Swift.h>
32
- #elif __has_include("RNFBAnalytics-Swift.h")
33
- // If `use_frameworks!` is not in use (for example, while using pre-built
34
- // react-native core) then header imports based on frameworks assumptions fail.
35
- // So, if frameworks are not available, fall back to importing the header directly, it
36
- // should be findable from a header search path pointing to the build
37
- // directory. See firebase-ios-sdk#12611 for more context.
38
- #import "RNFBAnalytics-Swift.h"
39
- #endif
40
- #import <RNFBApp/RNFBSharedUtils.h>
18
+ // This module intentionally has no Firebase imports and no `*-Swift.h` —
19
+ // see RNFBAnalyticsHelper.h. Every Firebase Analytics SDK call (and the
20
+ // Swift param cleaner / consent mapper / log-transaction types) is routed
21
+ // through the plain Objective-C RNFBAnalyticsHelper class instead, which
22
+ // can safely `@import` FirebaseAnalytics / import the generated Swift
23
+ // interface because it compiles as ObjC, not ObjC++.
41
24
  #import "RNFBAnalyticsModule.h"
42
-
43
- /** GA4 parameters that must be sent as integer NSNumber values (not doubles from JS). */
44
- static NSArray<NSString *> *RNFBAnalyticsLongNumericParameterKeys(void) {
45
- static NSArray<NSString *> *keys;
46
- static dispatch_once_t onceToken;
47
- dispatch_once(&onceToken, ^{
48
- keys = @[
49
- kFIRParameterQuantity,
50
- kFIRParameterIndex,
51
- kFIRParameterLevel,
52
- kFIRParameterNumberOfNights,
53
- kFIRParameterNumberOfPassengers,
54
- kFIRParameterNumberOfRooms,
55
- kFIRParameterScore,
56
- ];
57
- });
58
- return keys;
59
- }
25
+ #import "RNFBAnalyticsHelper.h"
60
26
 
61
27
  @implementation RNFBAnalyticsModule
62
28
  #pragma mark -
@@ -80,286 +46,101 @@ RCT_EXPORT_MODULE(NativeRNFBTurboAnalytics)
80
46
  params:(NSDictionary *)params
81
47
  resolve:(RCTPromiseResolveBlock)resolve
82
48
  reject:(RCTPromiseRejectBlock)reject {
83
- @try {
84
- [FIRAnalytics logEventWithName:name parameters:[self cleanJavascriptParams:params]];
85
- } @catch (NSException *exception) {
86
- return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception];
87
- }
88
-
89
- return resolve([NSNull null]);
49
+ [RNFBAnalyticsHelper logEvent:name params:params resolve:resolve reject:reject];
90
50
  }
91
51
 
92
52
  - (void)setAnalyticsCollectionEnabled:(BOOL)enabled
93
53
  resolve:(RCTPromiseResolveBlock)resolve
94
54
  reject:(RCTPromiseRejectBlock)reject {
95
- @try {
96
- [FIRAnalytics setAnalyticsCollectionEnabled:enabled];
97
- } @catch (NSException *exception) {
98
- return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception];
99
- }
100
-
101
- return resolve([NSNull null]);
55
+ [RNFBAnalyticsHelper setAnalyticsCollectionEnabled:enabled resolve:resolve reject:reject];
102
56
  }
103
57
 
104
58
  - (void)setUserId:(NSString *)id
105
59
  resolve:(RCTPromiseResolveBlock)resolve
106
60
  reject:(RCTPromiseRejectBlock)reject {
107
- @try {
108
- [FIRAnalytics setUserID:[self convertNSNullToNil:id]];
109
- } @catch (NSException *exception) {
110
- return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception];
111
- }
112
- return resolve([NSNull null]);
61
+ [RNFBAnalyticsHelper setUserId:id resolve:resolve reject:reject];
113
62
  }
114
63
 
115
64
  - (void)setUserProperty:(NSString *)name
116
65
  value:(NSString *)value
117
66
  resolve:(RCTPromiseResolveBlock)resolve
118
67
  reject:(RCTPromiseRejectBlock)reject {
119
- @try {
120
- [FIRAnalytics setUserPropertyString:[self convertNSNullToNil:value] forName:name];
121
- } @catch (NSException *exception) {
122
- return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception];
123
- }
124
- return resolve([NSNull null]);
68
+ [RNFBAnalyticsHelper setUserProperty:name value:value resolve:resolve reject:reject];
125
69
  }
126
70
 
127
71
  - (void)setUserProperties:(NSDictionary *)properties
128
72
  resolve:(RCTPromiseResolveBlock)resolve
129
73
  reject:(RCTPromiseRejectBlock)reject {
130
- @try {
131
- [properties enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
132
- [FIRAnalytics setUserPropertyString:[self convertNSNullToNil:value] forName:key];
133
- }];
134
- } @catch (NSException *exception) {
135
- return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception];
136
- }
137
- return resolve([NSNull null]);
74
+ [RNFBAnalyticsHelper setUserProperties:properties resolve:resolve reject:reject];
138
75
  }
139
76
 
140
77
  - (void)resetAnalyticsData:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
141
- @try {
142
- [FIRAnalytics resetAnalyticsData];
143
- } @catch (NSException *exception) {
144
- return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception];
145
- }
146
- return resolve([NSNull null]);
78
+ [RNFBAnalyticsHelper resetAnalyticsData:resolve reject:reject];
147
79
  }
148
80
 
149
81
  - (void)setSessionTimeoutDuration:(double)milliseconds
150
82
  resolve:(RCTPromiseResolveBlock)resolve
151
83
  reject:(RCTPromiseRejectBlock)reject {
152
- [FIRAnalytics setSessionTimeoutInterval:milliseconds / 1000];
153
- return resolve([NSNull null]);
84
+ [RNFBAnalyticsHelper setSessionTimeoutDuration:milliseconds resolve:resolve reject:reject];
154
85
  }
155
86
 
156
87
  - (void)getAppInstanceId:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
157
- return resolve([FIRAnalytics appInstanceID]);
88
+ [RNFBAnalyticsHelper getAppInstanceId:resolve reject:reject];
158
89
  }
159
90
 
160
91
  - (void)getSessionId:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
161
- __block BOOL completed = NO;
162
- const int64_t timeoutNs = (int64_t)(60 * NSEC_PER_SEC);
163
-
164
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeoutNs), dispatch_get_main_queue(), ^{
165
- if (completed) {
166
- return;
167
- }
168
- completed = YES;
169
- DLog(@"getSessionId timed_out: no SDK callback within 60 seconds");
170
- resolve([NSNull null]);
171
- });
172
-
173
- [FIRAnalytics sessionIDWithCompletion:^(int64_t sessionID, NSError *_Nullable error) {
174
- if (completed) {
175
- return;
176
- }
177
- completed = YES;
178
-
179
- // Occasionally sessionID is 0 despite nil error, reject as if it were an error
180
- // https://github.com/firebase/firebase-ios-sdk/issues/15258
181
- if (!error && [NSNumber numberWithLongLong:sessionID] == 0) {
182
- DLog(@"getSessionId zero_without_error: sessionID=0 (firebase-ios-sdk#15258)");
183
- return resolve([NSNull null]);
184
- }
185
-
186
- if (error) {
187
- DLog(@"getSessionId sdk_error: domain=%@ code=%ld description=%@", error.domain,
188
- (long)error.code, error.localizedDescription ?: @"(none)");
189
- return resolve([NSNull null]);
190
- }
191
-
192
- DLog(@"getSessionId success: sessionID=%lld", sessionID);
193
- return resolve([NSNumber numberWithLongLong:sessionID]);
194
- }];
92
+ [RNFBAnalyticsHelper getSessionId:resolve reject:reject];
195
93
  }
196
94
 
197
95
  - (void)setDefaultEventParameters:(NSDictionary *)params
198
96
  resolve:(RCTPromiseResolveBlock)resolve
199
97
  reject:(RCTPromiseRejectBlock)reject {
200
- @try {
201
- [FIRAnalytics setDefaultEventParameters:[self cleanJavascriptParams:params]];
202
- } @catch (NSException *exception) {
203
- return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception];
204
- }
205
-
206
- return resolve([NSNull null]);
98
+ [RNFBAnalyticsHelper setDefaultEventParameters:params resolve:resolve reject:reject];
207
99
  }
208
100
 
209
101
  - (void)initiateOnDeviceConversionMeasurementWithEmailAddress:(NSString *)emailAddress
210
102
  resolve:(RCTPromiseResolveBlock)resolve
211
103
  reject:(RCTPromiseRejectBlock)reject {
212
- @try {
213
- [FIRAnalytics initiateOnDeviceConversionMeasurementWithEmailAddress:emailAddress];
214
- } @catch (NSException *exception) {
215
- return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception];
216
- }
217
-
218
- return resolve([NSNull null]);
104
+ [RNFBAnalyticsHelper initiateOnDeviceConversionMeasurementWithEmailAddress:emailAddress
105
+ resolve:resolve
106
+ reject:reject];
219
107
  }
220
108
 
221
109
  - (void)initiateOnDeviceConversionMeasurementWithHashedEmailAddress:(NSString *)hashedEmailAddress
222
110
  resolve:(RCTPromiseResolveBlock)resolve
223
111
  reject:(RCTPromiseRejectBlock)reject {
224
- @try {
225
- NSData *emailAddress = [self dataFromHexString:hashedEmailAddress];
226
- [FIRAnalytics initiateOnDeviceConversionMeasurementWithHashedEmailAddress:emailAddress];
227
- } @catch (NSException *exception) {
228
- return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception];
229
- }
230
-
231
- return resolve([NSNull null]);
112
+ [RNFBAnalyticsHelper
113
+ initiateOnDeviceConversionMeasurementWithHashedEmailAddress:hashedEmailAddress
114
+ resolve:resolve
115
+ reject:reject];
232
116
  }
233
117
 
234
118
  - (void)initiateOnDeviceConversionMeasurementWithPhoneNumber:(NSString *)phoneNumber
235
119
  resolve:(RCTPromiseResolveBlock)resolve
236
120
  reject:(RCTPromiseRejectBlock)reject {
237
- @try {
238
- [FIRAnalytics initiateOnDeviceConversionMeasurementWithPhoneNumber:phoneNumber];
239
- } @catch (NSException *exception) {
240
- return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception];
241
- }
242
-
243
- return resolve([NSNull null]);
121
+ [RNFBAnalyticsHelper initiateOnDeviceConversionMeasurementWithPhoneNumber:phoneNumber
122
+ resolve:resolve
123
+ reject:reject];
244
124
  }
245
125
 
246
126
  - (void)initiateOnDeviceConversionMeasurementWithHashedPhoneNumber:(NSString *)hashedPhoneNumber
247
127
  resolve:(RCTPromiseResolveBlock)resolve
248
128
  reject:(RCTPromiseRejectBlock)reject {
249
- @try {
250
- NSData *phoneNumber = [self dataFromHexString:hashedPhoneNumber];
251
- [FIRAnalytics initiateOnDeviceConversionMeasurementWithHashedPhoneNumber:phoneNumber];
252
- } @catch (NSException *exception) {
253
- return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception];
254
- }
255
-
256
- return resolve([NSNull null]);
129
+ [RNFBAnalyticsHelper initiateOnDeviceConversionMeasurementWithHashedPhoneNumber:hashedPhoneNumber
130
+ resolve:resolve
131
+ reject:reject];
257
132
  }
258
133
 
259
134
  - (void)logTransaction:(NSString *)transactionId
260
135
  resolve:(RCTPromiseResolveBlock)resolve
261
136
  reject:(RCTPromiseRejectBlock)reject {
262
- if (@available(iOS 15.0, macOS 12.0, *)) {
263
- RNFBAnalyticsLogTransaction *handler = [[RNFBAnalyticsLogTransaction alloc] init];
264
- [handler logTransactionWithTransactionId:transactionId resolve:resolve reject:reject];
265
- } else {
266
- reject(@"firebase_analytics", @"logTransaction() is only supported on iOS 15.0 or newer", nil);
267
- }
137
+ [RNFBAnalyticsHelper logTransaction:transactionId resolve:resolve reject:reject];
268
138
  }
269
139
 
270
140
  - (void)setConsent:(NSDictionary *)consentSettings
271
141
  resolve:(RCTPromiseResolveBlock)resolve
272
142
  reject:(RCTPromiseRejectBlock)reject {
273
- @try {
274
- BOOL analyticsStorage = [consentSettings[@"analytics_storage"] boolValue];
275
- BOOL adStorage = [consentSettings[@"ad_storage"] boolValue];
276
- BOOL adUserData = [consentSettings[@"ad_user_data"] boolValue];
277
- BOOL adPersonalization = [consentSettings[@"ad_personalization"] boolValue];
278
- [FIRAnalytics setConsent:@{
279
- FIRConsentTypeAnalyticsStorage : analyticsStorage ? FIRConsentStatusGranted
280
- : FIRConsentStatusDenied,
281
- FIRConsentTypeAdStorage : adStorage ? FIRConsentStatusGranted : FIRConsentStatusDenied,
282
- FIRConsentTypeAdUserData : adUserData ? FIRConsentStatusGranted : FIRConsentStatusDenied,
283
- FIRConsentTypeAdPersonalization : adPersonalization ? FIRConsentStatusGranted
284
- : FIRConsentStatusDenied,
285
- }];
286
- } @catch (NSException *exception) {
287
- return [RNFBSharedUtils rejectPromiseWithExceptionDict:reject exception:exception];
288
- }
289
- return resolve([NSNull null]);
290
- }
291
-
292
- #pragma mark -
293
- #pragma mark Private methods
294
-
295
- - (NSDictionary *)cleanJavascriptParams:(NSDictionary *)params {
296
- NSMutableDictionary *newParams = [params mutableCopy];
297
- if (newParams[kFIRParameterItems]) {
298
- NSMutableArray *newItems = [NSMutableArray array];
299
- [(NSArray *)newParams[kFIRParameterItems]
300
- enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
301
- NSMutableDictionary *item = [obj mutableCopy];
302
- [self rnfb_coerceLongNumericParametersInMutableDictionary:item];
303
- [newItems addObject:[item copy]];
304
- }];
305
- newParams[kFIRParameterItems] = [newItems copy];
306
- }
307
- [self rnfb_coerceLongNumericParametersInMutableDictionary:newParams];
308
- [self rnfb_coerceSuccessParameterInMutableDictionary:newParams];
309
- NSNumber *extendSession = [newParams valueForKey:kFIRParameterExtendSession];
310
- if ([extendSession isEqualToNumber:@1]) {
311
- newParams[kFIRParameterExtendSession] = @YES;
312
- }
313
- return [newParams copy];
314
- }
315
-
316
- - (void)rnfb_coerceLongNumericParametersInMutableDictionary:(NSMutableDictionary *)dict {
317
- for (NSString *key in RNFBAnalyticsLongNumericParameterKeys()) {
318
- id value = dict[key];
319
- if (value != nil && value != [NSNull null]) {
320
- dict[key] = @([value integerValue]);
321
- }
322
- }
323
- }
324
-
325
- - (void)rnfb_coerceSuccessParameterInMutableDictionary:(NSMutableDictionary *)dict {
326
- id value = dict[kFIRParameterSuccess];
327
- if (value == nil || value == [NSNull null]) {
328
- return;
329
- }
330
- int success = 0;
331
- if ([value isKindOfClass:[NSString class]]) {
332
- NSString *lower = [(NSString *)value lowercaseString];
333
- if ([lower isEqualToString:@"true"] || [lower isEqualToString:@"yes"] ||
334
- [lower isEqualToString:@"1"]) {
335
- success = 1;
336
- }
337
- } else {
338
- success = [value boolValue] ? 1 : 0;
339
- }
340
- dict[kFIRParameterSuccess] = @(success);
341
- }
342
-
343
- /// Converts null values received over the bridge from NSNull to nil
344
- /// @param value Nullable string value
345
- - (NSString *)convertNSNullToNil:(NSString *)value {
346
- return [value isEqual:[NSNull null]] ? nil : value;
347
- }
348
-
349
- /// Converts a hex string to NSData
350
- /// @param hexString A hex string (e.g., SHA256 hash as 64-character hex string)
351
- /// @return NSData containing the decoded bytes (e.g., 32 bytes for SHA256)
352
- - (NSData *)dataFromHexString:(NSString *)hexString {
353
- NSMutableData *data = [NSMutableData dataWithCapacity:hexString.length / 2];
354
- unsigned char wholeByte;
355
- char byteChars[3] = {'\0', '\0', '\0'};
356
- for (NSUInteger i = 0; i < hexString.length; i += 2) {
357
- byteChars[0] = [hexString characterAtIndex:i];
358
- byteChars[1] = [hexString characterAtIndex:i + 1];
359
- wholeByte = strtol(byteChars, NULL, 16);
360
- [data appendBytes:&wholeByte length:1];
361
- }
362
- return data;
143
+ [RNFBAnalyticsHelper setConsent:consentSettings resolve:resolve reject:reject];
363
144
  }
364
145
 
365
146
  @end
@@ -9,6 +9,9 @@
9
9
  /* Begin PBXBuildFile section */
10
10
  2744B98621F45429004F8E3F /* RNFBAnalyticsModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2744B98521F45429004F8E3F /* RNFBAnalyticsModule.mm */; };
11
11
  2744B99021F45429004F8E3F /* RNFBAnalyticsLogTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2744B98F21F45429004F8E3F /* RNFBAnalyticsLogTransaction.swift */; };
12
+ 2744B99121F45429004F8E3F /* RNFBAnalyticsJavascriptParamsCleaner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2744B99221F45429004F8E3F /* RNFBAnalyticsJavascriptParamsCleaner.swift */; };
13
+ 2744B99321F45429004F8E3F /* RNFBAnalyticsConsentSettingsMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2744B99421F45429004F8E3F /* RNFBAnalyticsConsentSettingsMapper.swift */; };
14
+ RNFBAN000000000000000006 /* RNFBAnalyticsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = RNFBAN000000000000000008 /* RNFBAnalyticsHelper.m */; };
12
15
  /* End PBXBuildFile section */
13
16
 
14
17
  /* Begin PBXCopyFilesBuildPhase section */
@@ -28,6 +31,10 @@
28
31
  2744B98421F45429004F8E3F /* RNFBAnalyticsModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNFBAnalyticsModule.h; path = RNFBAnalytics/RNFBAnalyticsModule.h; sourceTree = SOURCE_ROOT; };
29
32
  2744B98521F45429004F8E3F /* RNFBAnalyticsModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = RNFBAnalyticsModule.mm; path = RNFBAnalytics/RNFBAnalyticsModule.mm; sourceTree = SOURCE_ROOT; };
30
33
  2744B98F21F45429004F8E3F /* RNFBAnalyticsLogTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBAnalyticsLogTransaction.swift; path = RNFBAnalytics/RNFBAnalyticsLogTransaction.swift; sourceTree = SOURCE_ROOT; };
34
+ 2744B99221F45429004F8E3F /* RNFBAnalyticsJavascriptParamsCleaner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBAnalyticsJavascriptParamsCleaner.swift; path = RNFBAnalytics/RNFBAnalyticsJavascriptParamsCleaner.swift; sourceTree = SOURCE_ROOT; };
35
+ 2744B99421F45429004F8E3F /* RNFBAnalyticsConsentSettingsMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RNFBAnalyticsConsentSettingsMapper.swift; path = RNFBAnalytics/RNFBAnalyticsConsentSettingsMapper.swift; sourceTree = SOURCE_ROOT; };
36
+ RNFBAN000000000000000007 /* RNFBAnalyticsHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNFBAnalyticsHelper.h; path = RNFBAnalytics/RNFBAnalyticsHelper.h; sourceTree = SOURCE_ROOT; };
37
+ RNFBAN000000000000000008 /* RNFBAnalyticsHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RNFBAnalyticsHelper.m; path = RNFBAnalytics/RNFBAnalyticsHelper.m; sourceTree = SOURCE_ROOT; };
31
38
  /* End PBXFileReference section */
32
39
 
33
40
  /* Begin PBXFrameworksBuildPhase section */
@@ -56,7 +63,11 @@
56
63
  2744B98C21F45C64004F8E3F /* common */,
57
64
  2744B98421F45429004F8E3F /* RNFBAnalyticsModule.h */,
58
65
  2744B98521F45429004F8E3F /* RNFBAnalyticsModule.mm */,
66
+ RNFBAN000000000000000007 /* RNFBAnalyticsHelper.h */,
67
+ RNFBAN000000000000000008 /* RNFBAnalyticsHelper.m */,
59
68
  2744B98F21F45429004F8E3F /* RNFBAnalyticsLogTransaction.swift */,
69
+ 2744B99221F45429004F8E3F /* RNFBAnalyticsJavascriptParamsCleaner.swift */,
70
+ 2744B99421F45429004F8E3F /* RNFBAnalyticsConsentSettingsMapper.swift */,
60
71
  );
61
72
  path = RNFBAnalytics;
62
73
  sourceTree = "<group>";
@@ -128,7 +139,10 @@
128
139
  buildActionMask = 2147483647;
129
140
  files = (
130
141
  2744B98621F45429004F8E3F /* RNFBAnalyticsModule.mm in Sources */,
142
+ RNFBAN000000000000000006 /* RNFBAnalyticsHelper.m in Sources */,
131
143
  2744B99021F45429004F8E3F /* RNFBAnalyticsLogTransaction.swift in Sources */,
144
+ 2744B99121F45429004F8E3F /* RNFBAnalyticsJavascriptParamsCleaner.swift in Sources */,
145
+ 2744B99321F45429004F8E3F /* RNFBAnalyticsConsentSettingsMapper.swift in Sources */,
132
146
  );
133
147
  runOnlyForDeploymentPostprocessing = 0;
134
148
  };
@@ -170,7 +184,7 @@
170
184
  PRODUCT_NAME = "$(TARGET_NAME)";
171
185
  SDKROOT = iphoneos;
172
186
  SKIP_INSTALL = YES;
173
- SWIFT_VERSION = 5.0;
187
+ SWIFT_VERSION = 5.10;
174
188
  TARGETED_DEVICE_FAMILY = "1,2";
175
189
  };
176
190
  name = Debug;
@@ -205,7 +219,7 @@
205
219
  PRODUCT_NAME = "$(TARGET_NAME)";
206
220
  SDKROOT = iphoneos;
207
221
  SKIP_INSTALL = YES;
208
- SWIFT_VERSION = 5.0;
222
+ SWIFT_VERSION = 5.10;
209
223
  TARGETED_DEVICE_FAMILY = "1,2";
210
224
  VALIDATE_PRODUCT = YES;
211
225
  };