Skip to content

Commit 23e4cf5

Browse files
committed
Merge remote-tracking branch 'origin/main' into ThreadSafeMemoizerMemoryLeakFix
2 parents 90a3564 + f94a964 commit 23e4cf5

18 files changed

+561
-8
lines changed

Crashlytics/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Unreleased
1+
# 11.7.0
22
- [fixed] Updated `upload-symbols` to version 3.20, wait for `debug.dylib` DWARF content getting generated when build with `--build-phase` option. Added `debug.dylib` DWARF content to run script input file list for user who enabled user script sandboxing (#14054).
33
- [fixed] Updated all memory allocation from `malloc()` to `calloc()` (#14209).
44

FirebaseAnalytics.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
1313
s.authors = 'Google, Inc.'
1414

1515
s.source = {
16-
:http => 'https://dl.google.com/firebase/ios/analytics/edf73aefd77661bd/FirebaseAnalytics-11.4.0.tar.gz'
16+
:http => 'https://dl.google.com/firebase/ios/analytics/f18d9810c6c5311c/FirebaseAnalytics-11.7.0.tar.gz'
1717
}
1818

1919
s.cocoapods_version = '>= 1.12.0'

FirebaseAuth/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
# Unreleased
1+
# 11.7.0
22
- [fixed] Fix Multi-factor session crash on second Firebase app. (#14238)
33
- [fixed] Updated most decoders to be consistent with Firebase 10's behavior
44
for decoding `nil` values. (#14212)
55
- [fixed] Address Xcode 16.2 concurrency compile time issues. (#14279)
66
- [fixed] Fix handling of cloud blocking function errors. (#14052)
77
- [fixed] Fix phone auth flow to skip RCE verification if appVerificationDisabledForTesting is set. (#14242)
8+
- [fixed] Avoid over release crash by making concurrently accessed properties
9+
atomic (#14308).
810

911
# 11.6.0
1012
- [added] Added reCAPTCHA Enterprise support for app verification during phone

FirebaseRemoteConfig/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
# Unreleased
1+
# 11.7.0
22
- [fixed] Mark ConfigUpdateListenerRegistration Sendable. (#14215)
3+
- [fixed] Mark completion handlers as Sendable in RemoteConfig class. (#14257)
4+
- [feature] Added support for custom signal targeting in Remote Config. Use
5+
`setCustomSignals` API for setting custom signals and use them to build
6+
custom targeting conditions in Remote Config. (#13976)
37

48
# 11.5.0
59
- [fixed] Mark two internal properties as `atomic` to prevent concurrency

FirebaseRemoteConfig/Sources/FIRRemoteConfig.m

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
/// Remote Config Error Domain.
3535
/// TODO: Rename according to obj-c style for constants.
3636
NSString *const FIRRemoteConfigErrorDomain = @"com.google.remoteconfig.ErrorDomain";
37+
// Remote Config Custom Signals Error Domain
38+
NSString *const FIRRemoteConfigCustomSignalsErrorDomain =
39+
@"com.google.remoteconfig.customsignals.ErrorDomain";
3740
// Remote Config Realtime Error Domain
3841
NSString *const FIRRemoteConfigUpdateErrorDomain = @"com.google.remoteconfig.update.ErrorDomain";
3942
/// Remote Config Error Info End Time Seconds;
@@ -47,6 +50,12 @@
4750
@"FIRRemoteConfigActivateNotification";
4851
static NSNotificationName FIRRolloutsStateDidChangeNotificationName =
4952
@"FIRRolloutsStateDidChangeNotification";
53+
/// Maximum allowed length for a custom signal key (in characters).
54+
static const NSUInteger FIRRemoteConfigCustomSignalsMaxKeyLength = 250;
55+
/// Maximum allowed length for a string value in custom signals (in characters).
56+
static const NSUInteger FIRRemoteConfigCustomSignalsMaxStringValueLength = 500;
57+
/// Maximum number of custom signals allowed.
58+
static const NSUInteger FIRRemoteConfigCustomSignalsMaxCount = 100;
5059

5160
/// Listener for the get methods.
5261
typedef void (^FIRRemoteConfigListener)(NSString *_Nonnull, NSDictionary *_Nonnull);
@@ -237,6 +246,103 @@ - (void)callListeners:(NSString *)key config:(NSDictionary *)config {
237246
}
238247
}
239248

249+
- (void)setCustomSignals:(nonnull NSDictionary<NSString *, NSObject *> *)customSignals
250+
withCompletion:(void (^_Nullable)(NSError *_Nullable error))completionHandler {
251+
void (^setCustomSignalsBlock)(void) = ^{
252+
// Validate value type, and key and value length
253+
for (NSString *key in customSignals) {
254+
NSObject *value = customSignals[key];
255+
if (![value isKindOfClass:[NSNull class]] && ![value isKindOfClass:[NSString class]] &&
256+
![value isKindOfClass:[NSNumber class]]) {
257+
if (completionHandler) {
258+
dispatch_async(dispatch_get_main_queue(), ^{
259+
NSError *error =
260+
[NSError errorWithDomain:FIRRemoteConfigCustomSignalsErrorDomain
261+
code:FIRRemoteConfigCustomSignalsErrorInvalidValueType
262+
userInfo:@{
263+
NSLocalizedDescriptionKey :
264+
@"Invalid value type. Must be NSString, NSNumber or NSNull"
265+
}];
266+
completionHandler(error);
267+
});
268+
}
269+
return;
270+
}
271+
272+
if (key.length > FIRRemoteConfigCustomSignalsMaxKeyLength ||
273+
([value isKindOfClass:[NSString class]] &&
274+
[(NSString *)value length] > FIRRemoteConfigCustomSignalsMaxStringValueLength)) {
275+
if (completionHandler) {
276+
dispatch_async(dispatch_get_main_queue(), ^{
277+
NSError *error = [NSError
278+
errorWithDomain:FIRRemoteConfigCustomSignalsErrorDomain
279+
code:FIRRemoteConfigCustomSignalsErrorLimitExceeded
280+
userInfo:@{
281+
NSLocalizedDescriptionKey : [NSString
282+
stringWithFormat:@"Custom signal keys and string values must be "
283+
@"%lu and %lu characters or less respectively.",
284+
FIRRemoteConfigCustomSignalsMaxKeyLength,
285+
FIRRemoteConfigCustomSignalsMaxStringValueLength]
286+
}];
287+
completionHandler(error);
288+
});
289+
}
290+
return;
291+
}
292+
}
293+
294+
// Merge new signals with existing ones, overwriting existing keys.
295+
// Also, remove entries where the new value is null.
296+
NSMutableDictionary<NSString *, NSString *> *newCustomSignals =
297+
[[NSMutableDictionary alloc] initWithDictionary:self->_settings.customSignals];
298+
299+
for (NSString *key in customSignals) {
300+
NSObject *value = customSignals[key];
301+
if (![value isKindOfClass:[NSNull class]]) {
302+
NSString *stringValue = [value isKindOfClass:[NSNumber class]]
303+
? [(NSNumber *)value stringValue]
304+
: (NSString *)value;
305+
[newCustomSignals setObject:stringValue forKey:key];
306+
} else {
307+
[newCustomSignals removeObjectForKey:key];
308+
}
309+
}
310+
311+
// Check the size limit.
312+
if (newCustomSignals.count > FIRRemoteConfigCustomSignalsMaxCount) {
313+
if (completionHandler) {
314+
dispatch_async(dispatch_get_main_queue(), ^{
315+
NSError *error = [NSError
316+
errorWithDomain:FIRRemoteConfigCustomSignalsErrorDomain
317+
code:FIRRemoteConfigCustomSignalsErrorLimitExceeded
318+
userInfo:@{
319+
NSLocalizedDescriptionKey : [NSString
320+
stringWithFormat:@"Custom signals count exceeds the limit of %lu.",
321+
FIRRemoteConfigCustomSignalsMaxCount]
322+
}];
323+
completionHandler(error);
324+
});
325+
}
326+
return;
327+
}
328+
329+
// Update only if there are changes.
330+
if (![newCustomSignals isEqualToDictionary:self->_settings.customSignals]) {
331+
self->_settings.customSignals = newCustomSignals;
332+
}
333+
// Log the keys of the updated custom signals.
334+
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000078", @"Keys of updated custom signals: %@",
335+
[newCustomSignals allKeys]);
336+
337+
if (completionHandler) {
338+
dispatch_async(dispatch_get_main_queue(), ^{
339+
completionHandler(nil);
340+
});
341+
}
342+
};
343+
dispatch_async(_queue, setCustomSignalsBlock);
344+
}
345+
240346
#pragma mark - fetch
241347

242348
- (void)fetchWithCompletionHandler:(FIRRemoteConfigFetchCompletion)completionHandler {

FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@
8181
/// Last active template version.
8282
@property(nonatomic, readwrite, assign) NSString *lastActiveTemplateVersion;
8383

84+
#pragma mark - Custom Signals
85+
86+
/// A dictionary to hold custom signals that are set by the developer.
87+
@property(nonatomic, readwrite, strong) NSDictionary<NSString *, NSString *> *customSignals;
88+
8489
#pragma mark Throttling properties
8590

8691
/// Throttling intervals are based on https://cloud.google.com/storage/docs/exponential-backoff

FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig/FIRRemoteConfig.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,19 @@ typedef NS_ERROR_ENUM(FIRRemoteConfigUpdateErrorDomain, FIRRemoteConfigUpdateErr
9797
FIRRemoteConfigUpdateErrorUnavailable = 8004,
9898
} NS_SWIFT_NAME(RemoteConfigUpdateError);
9999

100+
/// Error domain for custom signals errors.
101+
extern NSString *const _Nonnull FIRRemoteConfigCustomSignalsErrorDomain NS_SWIFT_NAME(RemoteConfigCustomSignalsErrorDomain);
102+
103+
/// Firebase Remote Config custom signals error.
104+
typedef NS_ERROR_ENUM(FIRRemoteConfigCustomSignalsErrorDomain, FIRRemoteConfigCustomSignalsError){
105+
/// Unknown error.
106+
FIRRemoteConfigCustomSignalsErrorUnknown = 8101,
107+
/// Invalid value type in the custom signals dictionary.
108+
FIRRemoteConfigCustomSignalsErrorInvalidValueType = 8102,
109+
/// Limit exceeded for key length, value length, or number of signals.
110+
FIRRemoteConfigCustomSignalsErrorLimitExceeded = 8103,
111+
} NS_SWIFT_NAME(RemoteConfigCustomSignalsError);
112+
100113
/// Enumerated value that indicates the source of Remote Config data. Data can come from
101114
/// the Remote Config service, the DefaultConfig that is available when the app is first installed,
102115
/// or a static initialized value if data is not available from the service or DefaultConfig.
@@ -223,6 +236,22 @@ NS_SWIFT_NAME(RemoteConfig)
223236
- (void)ensureInitializedWithCompletionHandler:
224237
(void (^_Nonnull)(NSError *_Nullable initializationError))completionHandler;
225238
#pragma mark - Fetch
239+
240+
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000)
241+
/// Fetches Remote Config data with a callback. Call `activate()` to make fetched data
242+
/// available to your app.
243+
///
244+
/// Note: This method uses a Firebase Installations token to identify the app instance, and once
245+
/// it's called, it periodically sends data to the Firebase backend. (see
246+
/// `Installations.authToken(completion:)`).
247+
/// To stop the periodic sync, call `Installations.delete(completion:)`
248+
/// and avoid calling this method again.
249+
///
250+
/// @param completionHandler Fetch operation callback with status and error parameters.
251+
- (void)fetchWithCompletionHandler:
252+
(void (^_Nullable NS_SWIFT_SENDABLE)(FIRRemoteConfigFetchStatus status,
253+
NSError *_Nullable error))completionHandler;
254+
#else
226255
/// Fetches Remote Config data with a callback. Call `activate()` to make fetched data
227256
/// available to your app.
228257
///
@@ -235,7 +264,27 @@ NS_SWIFT_NAME(RemoteConfig)
235264
/// @param completionHandler Fetch operation callback with status and error parameters.
236265
- (void)fetchWithCompletionHandler:(void (^_Nullable)(FIRRemoteConfigFetchStatus status,
237266
NSError *_Nullable error))completionHandler;
267+
#endif
238268

269+
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000)
270+
/// Fetches Remote Config data and sets a duration that specifies how long config data lasts.
271+
/// Call `activateWithCompletion:` to make fetched data available to your app.
272+
///
273+
/// Note: This method uses a Firebase Installations token to identify the app instance, and once
274+
/// it's called, it periodically sends data to the Firebase backend. (see
275+
/// `Installations.authToken(completion:)`).
276+
/// To stop the periodic sync, call `Installations.delete(completion:)`
277+
/// and avoid calling this method again.
278+
///
279+
/// @param expirationDuration Override the (default or optionally set `minimumFetchInterval`
280+
/// property in RemoteConfigSettings) `minimumFetchInterval` for only the current request, in
281+
/// seconds. Setting a value of 0 seconds will force a fetch to the backend.
282+
/// @param completionHandler Fetch operation callback with status and error parameters.
283+
- (void)fetchWithExpirationDuration:(NSTimeInterval)expirationDuration
284+
completionHandler:(void (^_Nullable NS_SWIFT_SENDABLE)(
285+
FIRRemoteConfigFetchStatus status,
286+
NSError *_Nullable error))completionHandler;
287+
#else
239288
/// Fetches Remote Config data and sets a duration that specifies how long config data lasts.
240289
/// Call `activateWithCompletion:` to make fetched data available to your app.
241290
///
@@ -252,7 +301,23 @@ NS_SWIFT_NAME(RemoteConfig)
252301
- (void)fetchWithExpirationDuration:(NSTimeInterval)expirationDuration
253302
completionHandler:(void (^_Nullable)(FIRRemoteConfigFetchStatus status,
254303
NSError *_Nullable error))completionHandler;
304+
#endif
255305

306+
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000)
307+
/// Fetches Remote Config data and if successful, activates fetched data. Optional completion
308+
/// handler callback is invoked after the attempted activation of data, if the fetch call succeeded.
309+
///
310+
/// Note: This method uses a Firebase Installations token to identify the app instance, and once
311+
/// it's called, it periodically sends data to the Firebase backend. (see
312+
/// `Installations.authToken(completion:)`).
313+
/// To stop the periodic sync, call `Installations.delete(completion:)`
314+
/// and avoid calling this method again.
315+
///
316+
/// @param completionHandler Fetch operation callback with status and error parameters.
317+
- (void)fetchAndActivateWithCompletionHandler:
318+
(void (^_Nullable NS_SWIFT_SENDABLE)(FIRRemoteConfigFetchAndActivateStatus status,
319+
NSError *_Nullable error))completionHandler;
320+
#else
256321
/// Fetches Remote Config data and if successful, activates fetched data. Optional completion
257322
/// handler callback is invoked after the attempted activation of data, if the fetch call succeeded.
258323
///
@@ -266,14 +331,23 @@ NS_SWIFT_NAME(RemoteConfig)
266331
- (void)fetchAndActivateWithCompletionHandler:
267332
(void (^_Nullable)(FIRRemoteConfigFetchAndActivateStatus status,
268333
NSError *_Nullable error))completionHandler;
334+
#endif
269335

270336
#pragma mark - Apply
271337

338+
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000)
339+
/// Applies Fetched Config data to the Active Config, causing updates to the behavior and appearance
340+
/// of the app to take effect (depending on how config data is used in the app).
341+
/// @param completion Activate operation callback with changed and error parameters.
342+
- (void)activateWithCompletion:
343+
(void (^_Nullable NS_SWIFT_SENDABLE)(BOOL changed, NSError *_Nullable error))completion;
344+
#else
272345
/// Applies Fetched Config data to the Active Config, causing updates to the behavior and appearance
273346
/// of the app to take effect (depending on how config data is used in the app).
274347
/// @param completion Activate operation callback with changed and error parameters.
275348
- (void)activateWithCompletion:(void (^_Nullable)(BOOL changed,
276349
NSError *_Nullable error))completion;
350+
#endif
277351

278352
#pragma mark - Get Config
279353
/// Enables access to configuration values by using object subscripting syntax.
@@ -358,4 +432,8 @@ typedef void (^FIRRemoteConfigUpdateCompletion)(FIRRemoteConfigUpdate *_Nullable
358432
(FIRRemoteConfigUpdateCompletion _Nonnull)listener
359433
NS_SWIFT_NAME(addOnConfigUpdateListener(remoteConfigUpdateCompletion:));
360434

435+
- (void)setCustomSignals:(nonnull NSDictionary<NSString *, NSObject *> *)customSignals
436+
withCompletion:(void (^_Nullable)(NSError *_Nullable error))completionHandler
437+
NS_REFINED_FOR_SWIFT;
438+
361439
@end

FirebaseRemoteConfig/Sources/RCNConfigSettings.m

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,25 @@ - (NSString *)nextRequestWithUserProperties:(NSDictionary *)userProperties {
404404
}
405405
}
406406
}
407+
408+
NSDictionary<NSString *, NSString *> *customSignals = [self customSignals];
409+
if (customSignals.count > 0) {
410+
NSError *error;
411+
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:customSignals
412+
options:0
413+
error:&error];
414+
if (!error) {
415+
ret = [ret
416+
stringByAppendingString:[NSString
417+
stringWithFormat:@", custom_signals:%@",
418+
[[NSString alloc]
419+
initWithData:jsonData
420+
encoding:NSUTF8StringEncoding]]];
421+
// Log the keys of the custom signals sent during fetch.
422+
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000078",
423+
@"Keys of custom signals during fetch: %@", [customSignals allKeys]);
424+
}
425+
}
407426
ret = [ret stringByAppendingString:@"}"];
408427
return ret;
409428
}
@@ -473,6 +492,14 @@ - (void)setLastSetDefaultsTimeInterval:(NSTimeInterval)lastSetDefaultsTimestamp
473492
completionHandler:nil];
474493
}
475494

495+
- (NSDictionary<NSString *, NSString *> *)customSignals {
496+
return [_userDefaultsManager customSignals];
497+
}
498+
499+
- (void)setCustomSignals:(NSDictionary<NSString *, NSString *> *)customSignals {
500+
[_userDefaultsManager setCustomSignals:customSignals];
501+
}
502+
476503
#pragma mark Throttling
477504

478505
- (BOOL)hasMinimumFetchIntervalElapsed:(NSTimeInterval)minimumFetchInterval {

FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ NS_ASSUME_NONNULL_BEGIN
4747
@property(nonatomic, assign) NSString *lastFetchedTemplateVersion;
4848
/// Last active template version.
4949
@property(nonatomic, assign) NSString *lastActiveTemplateVersion;
50+
/// A dictionary to hold the latest custom signals set by the developer.
51+
@property(nonatomic, readwrite, strong) NSDictionary<NSString *, NSString *> *customSignals;
5052

5153
/// Designated initializer.
5254
- (instancetype)initWithAppName:(NSString *)appName

FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
static NSString *const kRCNUserDefaultsKeyNameCurrentRealtimeThrottlingRetryInterval =
3535
@"currentRealtimeThrottlingRetryInterval";
3636
static NSString *const kRCNUserDefaultsKeyNameRealtimeRetryCount = @"realtimeRetryCount";
37+
static NSString *const kRCNUserDefaultsKeyCustomSignals = @"customSignals";
3738

3839
@interface RCNUserDefaultsManager () {
3940
/// User Defaults instance for this bundleID. NSUserDefaults is guaranteed to be thread-safe.
@@ -141,6 +142,21 @@ - (void)setLastActiveTemplateVersion:(NSString *)templateVersion {
141142
}
142143
}
143144

145+
- (NSDictionary<NSString *, NSString *> *)customSignals {
146+
NSDictionary *userDefaults = [self instanceUserDefaults];
147+
if ([userDefaults objectForKey:kRCNUserDefaultsKeyCustomSignals]) {
148+
return [userDefaults objectForKey:kRCNUserDefaultsKeyCustomSignals];
149+
}
150+
151+
return [[NSDictionary<NSString *, NSString *> alloc] init];
152+
}
153+
154+
- (void)setCustomSignals:(NSDictionary<NSString *, NSString *> *)customSignals {
155+
if (customSignals) {
156+
[self setInstanceUserDefaultsValue:customSignals forKey:kRCNUserDefaultsKeyCustomSignals];
157+
}
158+
}
159+
144160
- (NSTimeInterval)lastETagUpdateTime {
145161
NSNumber *lastETagUpdateTime =
146162
[[self instanceUserDefaults] objectForKey:kRCNUserDefaultsKeyNamelastETagUpdateTime];

0 commit comments

Comments
 (0)