Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into ThreadSafeMemoizerMem…
Browse files Browse the repository at this point in the history
…oryLeakFix
  • Loading branch information
dconeybe committed Jan 7, 2025
2 parents 90a3564 + f94a964 commit 23e4cf5
Show file tree
Hide file tree
Showing 18 changed files with 561 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Crashlytics/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Unreleased
# 11.7.0
- [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).
- [fixed] Updated all memory allocation from `malloc()` to `calloc()` (#14209).

Expand Down
2 changes: 1 addition & 1 deletion FirebaseAnalytics.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Pod::Spec.new do |s|
s.authors = 'Google, Inc.'

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

s.cocoapods_version = '>= 1.12.0'
Expand Down
4 changes: 3 additions & 1 deletion FirebaseAuth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Unreleased
# 11.7.0
- [fixed] Fix Multi-factor session crash on second Firebase app. (#14238)
- [fixed] Updated most decoders to be consistent with Firebase 10's behavior
for decoding `nil` values. (#14212)
- [fixed] Address Xcode 16.2 concurrency compile time issues. (#14279)
- [fixed] Fix handling of cloud blocking function errors. (#14052)
- [fixed] Fix phone auth flow to skip RCE verification if appVerificationDisabledForTesting is set. (#14242)
- [fixed] Avoid over release crash by making concurrently accessed properties
atomic (#14308).

# 11.6.0
- [added] Added reCAPTCHA Enterprise support for app verification during phone
Expand Down
6 changes: 5 additions & 1 deletion FirebaseRemoteConfig/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Unreleased
# 11.7.0
- [fixed] Mark ConfigUpdateListenerRegistration Sendable. (#14215)
- [fixed] Mark completion handlers as Sendable in RemoteConfig class. (#14257)
- [feature] Added support for custom signal targeting in Remote Config. Use
`setCustomSignals` API for setting custom signals and use them to build
custom targeting conditions in Remote Config. (#13976)

# 11.5.0
- [fixed] Mark two internal properties as `atomic` to prevent concurrency
Expand Down
106 changes: 106 additions & 0 deletions FirebaseRemoteConfig/Sources/FIRRemoteConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
/// Remote Config Error Domain.
/// TODO: Rename according to obj-c style for constants.
NSString *const FIRRemoteConfigErrorDomain = @"com.google.remoteconfig.ErrorDomain";
// Remote Config Custom Signals Error Domain
NSString *const FIRRemoteConfigCustomSignalsErrorDomain =
@"com.google.remoteconfig.customsignals.ErrorDomain";
// Remote Config Realtime Error Domain
NSString *const FIRRemoteConfigUpdateErrorDomain = @"com.google.remoteconfig.update.ErrorDomain";
/// Remote Config Error Info End Time Seconds;
Expand All @@ -47,6 +50,12 @@
@"FIRRemoteConfigActivateNotification";
static NSNotificationName FIRRolloutsStateDidChangeNotificationName =
@"FIRRolloutsStateDidChangeNotification";
/// Maximum allowed length for a custom signal key (in characters).
static const NSUInteger FIRRemoteConfigCustomSignalsMaxKeyLength = 250;
/// Maximum allowed length for a string value in custom signals (in characters).
static const NSUInteger FIRRemoteConfigCustomSignalsMaxStringValueLength = 500;
/// Maximum number of custom signals allowed.
static const NSUInteger FIRRemoteConfigCustomSignalsMaxCount = 100;

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

- (void)setCustomSignals:(nonnull NSDictionary<NSString *, NSObject *> *)customSignals
withCompletion:(void (^_Nullable)(NSError *_Nullable error))completionHandler {
void (^setCustomSignalsBlock)(void) = ^{
// Validate value type, and key and value length
for (NSString *key in customSignals) {
NSObject *value = customSignals[key];
if (![value isKindOfClass:[NSNull class]] && ![value isKindOfClass:[NSString class]] &&
![value isKindOfClass:[NSNumber class]]) {
if (completionHandler) {
dispatch_async(dispatch_get_main_queue(), ^{
NSError *error =
[NSError errorWithDomain:FIRRemoteConfigCustomSignalsErrorDomain
code:FIRRemoteConfigCustomSignalsErrorInvalidValueType
userInfo:@{
NSLocalizedDescriptionKey :
@"Invalid value type. Must be NSString, NSNumber or NSNull"
}];
completionHandler(error);
});
}
return;
}

if (key.length > FIRRemoteConfigCustomSignalsMaxKeyLength ||
([value isKindOfClass:[NSString class]] &&
[(NSString *)value length] > FIRRemoteConfigCustomSignalsMaxStringValueLength)) {
if (completionHandler) {
dispatch_async(dispatch_get_main_queue(), ^{
NSError *error = [NSError
errorWithDomain:FIRRemoteConfigCustomSignalsErrorDomain
code:FIRRemoteConfigCustomSignalsErrorLimitExceeded
userInfo:@{
NSLocalizedDescriptionKey : [NSString
stringWithFormat:@"Custom signal keys and string values must be "
@"%lu and %lu characters or less respectively.",
FIRRemoteConfigCustomSignalsMaxKeyLength,
FIRRemoteConfigCustomSignalsMaxStringValueLength]
}];
completionHandler(error);
});
}
return;
}
}

// Merge new signals with existing ones, overwriting existing keys.
// Also, remove entries where the new value is null.
NSMutableDictionary<NSString *, NSString *> *newCustomSignals =
[[NSMutableDictionary alloc] initWithDictionary:self->_settings.customSignals];

for (NSString *key in customSignals) {
NSObject *value = customSignals[key];
if (![value isKindOfClass:[NSNull class]]) {
NSString *stringValue = [value isKindOfClass:[NSNumber class]]
? [(NSNumber *)value stringValue]
: (NSString *)value;
[newCustomSignals setObject:stringValue forKey:key];
} else {
[newCustomSignals removeObjectForKey:key];
}
}

// Check the size limit.
if (newCustomSignals.count > FIRRemoteConfigCustomSignalsMaxCount) {
if (completionHandler) {
dispatch_async(dispatch_get_main_queue(), ^{
NSError *error = [NSError
errorWithDomain:FIRRemoteConfigCustomSignalsErrorDomain
code:FIRRemoteConfigCustomSignalsErrorLimitExceeded
userInfo:@{
NSLocalizedDescriptionKey : [NSString
stringWithFormat:@"Custom signals count exceeds the limit of %lu.",
FIRRemoteConfigCustomSignalsMaxCount]
}];
completionHandler(error);
});
}
return;
}

// Update only if there are changes.
if (![newCustomSignals isEqualToDictionary:self->_settings.customSignals]) {
self->_settings.customSignals = newCustomSignals;
}
// Log the keys of the updated custom signals.
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000078", @"Keys of updated custom signals: %@",
[newCustomSignals allKeys]);

if (completionHandler) {
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(nil);
});
}
};
dispatch_async(_queue, setCustomSignalsBlock);
}

#pragma mark - fetch

- (void)fetchWithCompletionHandler:(FIRRemoteConfigFetchCompletion)completionHandler {
Expand Down
5 changes: 5 additions & 0 deletions FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@
/// Last active template version.
@property(nonatomic, readwrite, assign) NSString *lastActiveTemplateVersion;

#pragma mark - Custom Signals

/// A dictionary to hold custom signals that are set by the developer.
@property(nonatomic, readwrite, strong) NSDictionary<NSString *, NSString *> *customSignals;

#pragma mark Throttling properties

/// Throttling intervals are based on https://cloud.google.com/storage/docs/exponential-backoff
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@ typedef NS_ERROR_ENUM(FIRRemoteConfigUpdateErrorDomain, FIRRemoteConfigUpdateErr
FIRRemoteConfigUpdateErrorUnavailable = 8004,
} NS_SWIFT_NAME(RemoteConfigUpdateError);

/// Error domain for custom signals errors.
extern NSString *const _Nonnull FIRRemoteConfigCustomSignalsErrorDomain NS_SWIFT_NAME(RemoteConfigCustomSignalsErrorDomain);

/// Firebase Remote Config custom signals error.
typedef NS_ERROR_ENUM(FIRRemoteConfigCustomSignalsErrorDomain, FIRRemoteConfigCustomSignalsError){
/// Unknown error.
FIRRemoteConfigCustomSignalsErrorUnknown = 8101,
/// Invalid value type in the custom signals dictionary.
FIRRemoteConfigCustomSignalsErrorInvalidValueType = 8102,
/// Limit exceeded for key length, value length, or number of signals.
FIRRemoteConfigCustomSignalsErrorLimitExceeded = 8103,
} NS_SWIFT_NAME(RemoteConfigCustomSignalsError);

/// Enumerated value that indicates the source of Remote Config data. Data can come from
/// the Remote Config service, the DefaultConfig that is available when the app is first installed,
/// or a static initialized value if data is not available from the service or DefaultConfig.
Expand Down Expand Up @@ -223,6 +236,22 @@ NS_SWIFT_NAME(RemoteConfig)
- (void)ensureInitializedWithCompletionHandler:
(void (^_Nonnull)(NSError *_Nullable initializationError))completionHandler;
#pragma mark - Fetch

#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000)
/// Fetches Remote Config data with a callback. Call `activate()` to make fetched data
/// available to your app.
///
/// Note: This method uses a Firebase Installations token to identify the app instance, and once
/// it's called, it periodically sends data to the Firebase backend. (see
/// `Installations.authToken(completion:)`).
/// To stop the periodic sync, call `Installations.delete(completion:)`
/// and avoid calling this method again.
///
/// @param completionHandler Fetch operation callback with status and error parameters.
- (void)fetchWithCompletionHandler:
(void (^_Nullable NS_SWIFT_SENDABLE)(FIRRemoteConfigFetchStatus status,
NSError *_Nullable error))completionHandler;
#else
/// Fetches Remote Config data with a callback. Call `activate()` to make fetched data
/// available to your app.
///
Expand All @@ -235,7 +264,27 @@ NS_SWIFT_NAME(RemoteConfig)
/// @param completionHandler Fetch operation callback with status and error parameters.
- (void)fetchWithCompletionHandler:(void (^_Nullable)(FIRRemoteConfigFetchStatus status,
NSError *_Nullable error))completionHandler;
#endif

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

#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000)
/// Fetches Remote Config data and if successful, activates fetched data. Optional completion
/// handler callback is invoked after the attempted activation of data, if the fetch call succeeded.
///
/// Note: This method uses a Firebase Installations token to identify the app instance, and once
/// it's called, it periodically sends data to the Firebase backend. (see
/// `Installations.authToken(completion:)`).
/// To stop the periodic sync, call `Installations.delete(completion:)`
/// and avoid calling this method again.
///
/// @param completionHandler Fetch operation callback with status and error parameters.
- (void)fetchAndActivateWithCompletionHandler:
(void (^_Nullable NS_SWIFT_SENDABLE)(FIRRemoteConfigFetchAndActivateStatus status,
NSError *_Nullable error))completionHandler;
#else
/// Fetches Remote Config data and if successful, activates fetched data. Optional completion
/// handler callback is invoked after the attempted activation of data, if the fetch call succeeded.
///
Expand All @@ -266,14 +331,23 @@ NS_SWIFT_NAME(RemoteConfig)
- (void)fetchAndActivateWithCompletionHandler:
(void (^_Nullable)(FIRRemoteConfigFetchAndActivateStatus status,
NSError *_Nullable error))completionHandler;
#endif

#pragma mark - Apply

#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000)
/// Applies Fetched Config data to the Active Config, causing updates to the behavior and appearance
/// of the app to take effect (depending on how config data is used in the app).
/// @param completion Activate operation callback with changed and error parameters.
- (void)activateWithCompletion:
(void (^_Nullable NS_SWIFT_SENDABLE)(BOOL changed, NSError *_Nullable error))completion;
#else
/// Applies Fetched Config data to the Active Config, causing updates to the behavior and appearance
/// of the app to take effect (depending on how config data is used in the app).
/// @param completion Activate operation callback with changed and error parameters.
- (void)activateWithCompletion:(void (^_Nullable)(BOOL changed,
NSError *_Nullable error))completion;
#endif

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

- (void)setCustomSignals:(nonnull NSDictionary<NSString *, NSObject *> *)customSignals
withCompletion:(void (^_Nullable)(NSError *_Nullable error))completionHandler
NS_REFINED_FOR_SWIFT;

@end
27 changes: 27 additions & 0 deletions FirebaseRemoteConfig/Sources/RCNConfigSettings.m
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,25 @@ - (NSString *)nextRequestWithUserProperties:(NSDictionary *)userProperties {
}
}
}

NSDictionary<NSString *, NSString *> *customSignals = [self customSignals];
if (customSignals.count > 0) {
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:customSignals
options:0
error:&error];
if (!error) {
ret = [ret
stringByAppendingString:[NSString
stringWithFormat:@", custom_signals:%@",
[[NSString alloc]
initWithData:jsonData
encoding:NSUTF8StringEncoding]]];
// Log the keys of the custom signals sent during fetch.
FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000078",
@"Keys of custom signals during fetch: %@", [customSignals allKeys]);
}
}
ret = [ret stringByAppendingString:@"}"];
return ret;
}
Expand Down Expand Up @@ -473,6 +492,14 @@ - (void)setLastSetDefaultsTimeInterval:(NSTimeInterval)lastSetDefaultsTimestamp
completionHandler:nil];
}

- (NSDictionary<NSString *, NSString *> *)customSignals {
return [_userDefaultsManager customSignals];
}

- (void)setCustomSignals:(NSDictionary<NSString *, NSString *> *)customSignals {
[_userDefaultsManager setCustomSignals:customSignals];
}

#pragma mark Throttling

- (BOOL)hasMinimumFetchIntervalElapsed:(NSTimeInterval)minimumFetchInterval {
Expand Down
2 changes: 2 additions & 0 deletions FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic, assign) NSString *lastFetchedTemplateVersion;
/// Last active template version.
@property(nonatomic, assign) NSString *lastActiveTemplateVersion;
/// A dictionary to hold the latest custom signals set by the developer.
@property(nonatomic, readwrite, strong) NSDictionary<NSString *, NSString *> *customSignals;

/// Designated initializer.
- (instancetype)initWithAppName:(NSString *)appName
Expand Down
16 changes: 16 additions & 0 deletions FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
static NSString *const kRCNUserDefaultsKeyNameCurrentRealtimeThrottlingRetryInterval =
@"currentRealtimeThrottlingRetryInterval";
static NSString *const kRCNUserDefaultsKeyNameRealtimeRetryCount = @"realtimeRetryCount";
static NSString *const kRCNUserDefaultsKeyCustomSignals = @"customSignals";

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

- (NSDictionary<NSString *, NSString *> *)customSignals {
NSDictionary *userDefaults = [self instanceUserDefaults];
if ([userDefaults objectForKey:kRCNUserDefaultsKeyCustomSignals]) {
return [userDefaults objectForKey:kRCNUserDefaultsKeyCustomSignals];
}

return [[NSDictionary<NSString *, NSString *> alloc] init];
}

- (void)setCustomSignals:(NSDictionary<NSString *, NSString *> *)customSignals {
if (customSignals) {
[self setInstanceUserDefaultsValue:customSignals forKey:kRCNUserDefaultsKeyCustomSignals];
}
}

- (NSTimeInterval)lastETagUpdateTime {
NSNumber *lastETagUpdateTime =
[[self instanceUserDefaults] objectForKey:kRCNUserDefaultsKeyNamelastETagUpdateTime];
Expand Down
Loading

0 comments on commit 23e4cf5

Please sign in to comment.