From f73ee14e52042d78009a60e91107bedbe375fdf3 Mon Sep 17 00:00:00 2001 From: tusharkhandelwal8 Date: Fri, 24 Oct 2025 22:07:25 +0530 Subject: [PATCH 1/4] This is a test commit. --- FirebaseRemoteConfig/Sources/RCNConfigDBManager.m | 2 ++ FirebaseRemoteConfig/Sources/RCNConfigRealtime.m | 1 + FirebaseRemoteConfig/Sources/RCNConfigSettings.m | 2 ++ FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m | 1 + 4 files changed, 6 insertions(+) diff --git a/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m b/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m index 161f678b8d6..62d3b2f7a30 100644 --- a/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m +++ b/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m @@ -80,8 +80,10 @@ static BOOL RemoteConfigCreateFilePathIfNotExist(NSString *filePath) { @"Failed to create subdirectory for an empty file path."); return NO; } + FIRLogDebug(kFIRLoggerRemoteConfig, @"RC-SDK-DEBUG", @"DBManager: DB file does not exists BEFORE create: 0"); NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:filePath]) { + FIRLogDebug(kFIRLoggerRemoteConfig, @"RC-SDK-DEBUG", @"DBManager: Setting gIsNewDatabase = YES"); gIsNewDatabase = YES; NSError *error; [fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent] diff --git a/FirebaseRemoteConfig/Sources/RCNConfigRealtime.m b/FirebaseRemoteConfig/Sources/RCNConfigRealtime.m index 2c998eae18a..ecc772aacc4 100644 --- a/FirebaseRemoteConfig/Sources/RCNConfigRealtime.m +++ b/FirebaseRemoteConfig/Sources/RCNConfigRealtime.m @@ -723,6 +723,7 @@ - (void)pauseRealtimeStream { - (FIRConfigUpdateListenerRegistration *)addConfigUpdateListener: (void (^_Nonnull)(FIRRemoteConfigUpdate *configUpdate, NSError *_Nullable error))listener { + FIRLogDebug(kFIRLoggerRemoteConfig, @"RC-SDK-DEBUG", @"Realtime: Check1 local SDK is working"); if (listener == nil) { return nil; } diff --git a/FirebaseRemoteConfig/Sources/RCNConfigSettings.m b/FirebaseRemoteConfig/Sources/RCNConfigSettings.m index 115a615b845..5207e272c19 100644 --- a/FirebaseRemoteConfig/Sources/RCNConfigSettings.m +++ b/FirebaseRemoteConfig/Sources/RCNConfigSettings.m @@ -72,6 +72,7 @@ - (instancetype)initWithDatabaseManager:(RCNConfigDBManager *)manager googleAppID:(NSString *)googleAppID { self = [super init]; if (self) { + FIRLogDebug(kFIRLoggerRemoteConfig, @"RC-SDK-DEBUG", @"Config Settings: Init for namespace"); _FIRNamespace = FIRNamespace; _googleAppID = googleAppID; _bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; @@ -92,6 +93,7 @@ - (instancetype)initWithDatabaseManager:(RCNConfigDBManager *)manager // Check if the config database is new. If so, clear the configs saved in userDefaults. if ([_DBManager isNewDatabase]) { + FIRLogDebug(kFIRLoggerRemoteConfig, @"RC-SDK-DEBUG", @"isNewDatabase returned: 1"); FIRLogNotice(kFIRLoggerRemoteConfig, @"I-RCN000072", @"New config database created. Resetting user defaults."); [_userDefaultsManager resetUserDefaults]; diff --git a/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m b/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m index 3f266097ee2..60c061eece6 100644 --- a/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m +++ b/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m @@ -316,6 +316,7 @@ - (void)setInstanceUserDefaultsValue:(NSObject *)value forKey:(NSString *)key { // Delete any existing userdefaults for this instance. - (void)resetInstanceUserDefaults { @synchronized(_userDefaults) { + FIRLogDebug(kFIRLoggerRemoteConfig, @"RC-SDK-DEBUG", @"Defaults Manager: Reset is happening"); NSMutableDictionary *appUserDefaults = [[self appUserDefaults] mutableCopy]; NSMutableDictionary *appNamespaceUserDefaults = [[self instanceUserDefaults] mutableCopy]; [appNamespaceUserDefaults removeAllObjects]; From fef7940e21201c99178e46673c57720bb1b22f21 Mon Sep 17 00:00:00 2001 From: tusharkhandelwal8 Date: Sat, 25 Oct 2025 13:27:59 +0530 Subject: [PATCH 2/4] Prevent stale configuration data after iOS device restore --- FirebaseRemoteConfig/CHANGELOG.md | 6 +++++ .../Sources/FIRRemoteConfig.m | 4 +++ .../Sources/RCNConfigDBManager.h | 4 +++ .../Sources/RCNConfigDBManager.m | 26 ++++++++++++++++--- .../Sources/RCNConfigRealtime.m | 1 - .../Sources/RCNConfigSettings.m | 3 --- .../Sources/RCNUserDefaultsManager.m | 1 - 7 files changed, 36 insertions(+), 9 deletions(-) diff --git a/FirebaseRemoteConfig/CHANGELOG.md b/FirebaseRemoteConfig/CHANGELOG.md index 91ba0e80f24..126a1571aef 100644 --- a/FirebaseRemoteConfig/CHANGELOG.md +++ b/FirebaseRemoteConfig/CHANGELOG.md @@ -1,3 +1,9 @@ +# Unreleased +- [fixed] Fixed a bug where Remote Config does not work after a restore + of a previous backup of the device. (#14459) +- [fixed] Fixed a data race condition on the global database status flag + by synchronizing all read and write operations. (#14715) + # 12.3.0 - [fixed] Add missing GoogleUtilities dependency to fix SwiftPM builds when building dynamically linked libraries. (#15276) diff --git a/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m b/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m index 258eb362fe7..bfc55cdf877 100644 --- a/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m +++ b/FirebaseRemoteConfig/Sources/FIRRemoteConfig.m @@ -160,6 +160,10 @@ - (instancetype)initWithAppName:(NSString *)appName // Initialize RCConfigContent if not already. _configContent = configContent; + + // We must ensure the DBManager's asynchronous setup (which sets gIsNewDatabase) + // completes before RCNConfigSettings tries to read that state for the resetUserDefaults logic. + [_DBManager waitForDatabaseOperationQueue]; _settings = [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager namespace:_FIRNamespace firebaseAppName:appName diff --git a/FirebaseRemoteConfig/Sources/RCNConfigDBManager.h b/FirebaseRemoteConfig/Sources/RCNConfigDBManager.h index e22b40d3779..d881381186b 100644 --- a/FirebaseRemoteConfig/Sources/RCNConfigDBManager.h +++ b/FirebaseRemoteConfig/Sources/RCNConfigDBManager.h @@ -130,4 +130,8 @@ typedef void (^RCNDBLoadCompletion)(BOOL success, /// Returns true if this a new install of the Config database. - (BOOL)isNewDatabase; + +/// Blocks the calling thread until all pending database operations on the internal serial queue are +/// completed. Used to enforce initialization order. +- (void)waitForDatabaseOperationQueue; @end diff --git a/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m b/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m index 62d3b2f7a30..ec17be5b33f 100644 --- a/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m +++ b/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m @@ -38,6 +38,9 @@ /// The storage sub-directory that the Remote Config database resides in. static NSString *const RCNRemoteConfigStorageSubDirectory = @"Google/RemoteConfig"; +/// Introduce a dedicated serial queue for gIsNewDatabase access. +static dispatch_queue_t gIsNewDatabaseQueue; + /// Remote Config database path for deprecated V0 version. static NSString *RemoteConfigPathForOldDatabaseV0(void) { NSArray *dirPaths = @@ -80,11 +83,11 @@ static BOOL RemoteConfigCreateFilePathIfNotExist(NSString *filePath) { @"Failed to create subdirectory for an empty file path."); return NO; } - FIRLogDebug(kFIRLoggerRemoteConfig, @"RC-SDK-DEBUG", @"DBManager: DB file does not exists BEFORE create: 0"); NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:filePath]) { - FIRLogDebug(kFIRLoggerRemoteConfig, @"RC-SDK-DEBUG", @"DBManager: Setting gIsNewDatabase = YES"); - gIsNewDatabase = YES; + dispatch_sync(gIsNewDatabaseQueue, ^{ + gIsNewDatabase = YES; + }); NSError *error; [fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent] withIntermediateDirectories:YES @@ -121,6 +124,8 @@ + (instancetype)sharedInstance { static dispatch_once_t onceToken; static RCNConfigDBManager *sharedInstance; dispatch_once(&onceToken, ^{ + gIsNewDatabaseQueue = dispatch_queue_create("com.google.FirebaseRemoteConfig.gIsNewDatabase", + DISPATCH_QUEUE_SERIAL); sharedInstance = [[RCNConfigDBManager alloc] init]; }); return sharedInstance; @@ -1221,7 +1226,20 @@ - (BOOL)logErrorWithSQL:(const char *)SQL } - (BOOL)isNewDatabase { - return gIsNewDatabase; + __block BOOL isNew; + dispatch_sync(gIsNewDatabaseQueue, ^{ + isNew = gIsNewDatabase; + }); + return isNew; +} + +- (void)waitForDatabaseOperationQueue { + // This dispatch_sync call ensures that all blocks queued before it on _databaseOperationQueue + // (including the createOrOpenDatabase setup block) execute and complete before this method + // returns. + dispatch_sync(_databaseOperationQueue, ^{ + // Empty block forces synchronization. + }); } @end diff --git a/FirebaseRemoteConfig/Sources/RCNConfigRealtime.m b/FirebaseRemoteConfig/Sources/RCNConfigRealtime.m index ecc772aacc4..2c998eae18a 100644 --- a/FirebaseRemoteConfig/Sources/RCNConfigRealtime.m +++ b/FirebaseRemoteConfig/Sources/RCNConfigRealtime.m @@ -723,7 +723,6 @@ - (void)pauseRealtimeStream { - (FIRConfigUpdateListenerRegistration *)addConfigUpdateListener: (void (^_Nonnull)(FIRRemoteConfigUpdate *configUpdate, NSError *_Nullable error))listener { - FIRLogDebug(kFIRLoggerRemoteConfig, @"RC-SDK-DEBUG", @"Realtime: Check1 local SDK is working"); if (listener == nil) { return nil; } diff --git a/FirebaseRemoteConfig/Sources/RCNConfigSettings.m b/FirebaseRemoteConfig/Sources/RCNConfigSettings.m index 5207e272c19..2bbea5de99e 100644 --- a/FirebaseRemoteConfig/Sources/RCNConfigSettings.m +++ b/FirebaseRemoteConfig/Sources/RCNConfigSettings.m @@ -72,7 +72,6 @@ - (instancetype)initWithDatabaseManager:(RCNConfigDBManager *)manager googleAppID:(NSString *)googleAppID { self = [super init]; if (self) { - FIRLogDebug(kFIRLoggerRemoteConfig, @"RC-SDK-DEBUG", @"Config Settings: Init for namespace"); _FIRNamespace = FIRNamespace; _googleAppID = googleAppID; _bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; @@ -93,12 +92,10 @@ - (instancetype)initWithDatabaseManager:(RCNConfigDBManager *)manager // Check if the config database is new. If so, clear the configs saved in userDefaults. if ([_DBManager isNewDatabase]) { - FIRLogDebug(kFIRLoggerRemoteConfig, @"RC-SDK-DEBUG", @"isNewDatabase returned: 1"); FIRLogNotice(kFIRLoggerRemoteConfig, @"I-RCN000072", @"New config database created. Resetting user defaults."); [_userDefaultsManager resetUserDefaults]; } - _isFetchInProgress = NO; _lastFetchedTemplateVersion = [_userDefaultsManager lastFetchedTemplateVersion]; _lastActiveTemplateVersion = [_userDefaultsManager lastActiveTemplateVersion]; diff --git a/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m b/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m index 60c061eece6..3f266097ee2 100644 --- a/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m +++ b/FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.m @@ -316,7 +316,6 @@ - (void)setInstanceUserDefaultsValue:(NSObject *)value forKey:(NSString *)key { // Delete any existing userdefaults for this instance. - (void)resetInstanceUserDefaults { @synchronized(_userDefaults) { - FIRLogDebug(kFIRLoggerRemoteConfig, @"RC-SDK-DEBUG", @"Defaults Manager: Reset is happening"); NSMutableDictionary *appUserDefaults = [[self appUserDefaults] mutableCopy]; NSMutableDictionary *appNamespaceUserDefaults = [[self instanceUserDefaults] mutableCopy]; [appNamespaceUserDefaults removeAllObjects]; From 74a05154802c165ed66da78e34033ba54393d6c1 Mon Sep 17 00:00:00 2001 From: tusharkhandelwal8 Date: Sat, 25 Oct 2025 13:37:05 +0530 Subject: [PATCH 3/4] Fix Lint errors --- FirebaseRemoteConfig/CHANGELOG.md | 4 ++-- FirebaseRemoteConfig/Sources/RCNConfigSettings.m | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/FirebaseRemoteConfig/CHANGELOG.md b/FirebaseRemoteConfig/CHANGELOG.md index 126a1571aef..495dbaeb55e 100644 --- a/FirebaseRemoteConfig/CHANGELOG.md +++ b/FirebaseRemoteConfig/CHANGELOG.md @@ -1,7 +1,7 @@ # Unreleased -- [fixed] Fixed a bug where Remote Config does not work after a restore +- [fixed] Fixed a bug where Remote Config does not work after a restore of a previous backup of the device. (#14459) -- [fixed] Fixed a data race condition on the global database status flag +- [fixed] Fixed a data race condition on the global database status flag by synchronizing all read and write operations. (#14715) # 12.3.0 diff --git a/FirebaseRemoteConfig/Sources/RCNConfigSettings.m b/FirebaseRemoteConfig/Sources/RCNConfigSettings.m index 2bbea5de99e..115a615b845 100644 --- a/FirebaseRemoteConfig/Sources/RCNConfigSettings.m +++ b/FirebaseRemoteConfig/Sources/RCNConfigSettings.m @@ -96,6 +96,7 @@ - (instancetype)initWithDatabaseManager:(RCNConfigDBManager *)manager @"New config database created. Resetting user defaults."); [_userDefaultsManager resetUserDefaults]; } + _isFetchInProgress = NO; _lastFetchedTemplateVersion = [_userDefaultsManager lastFetchedTemplateVersion]; _lastActiveTemplateVersion = [_userDefaultsManager lastActiveTemplateVersion]; From d6910c46b20f4eefb7f845d43b616f7e99c87293 Mon Sep 17 00:00:00 2001 From: tusharkhandelwal8 Date: Thu, 30 Oct 2025 00:23:37 +0530 Subject: [PATCH 4/4] Refactor dispatch_sync for conciseness --- FirebaseRemoteConfig/Sources/RCNConfigDBManager.m | 1 - 1 file changed, 1 deletion(-) diff --git a/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m b/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m index ec17be5b33f..c1fd403a246 100644 --- a/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m +++ b/FirebaseRemoteConfig/Sources/RCNConfigDBManager.m @@ -1238,7 +1238,6 @@ - (void)waitForDatabaseOperationQueue { // (including the createOrOpenDatabase setup block) execute and complete before this method // returns. dispatch_sync(_databaseOperationQueue, ^{ - // Empty block forces synchronization. }); }