iOS Intermittent Bug: UserDefaults Preferences Loading Issue
Problem Summary
We're experiencing an intermittent issue where UserPreferences.shared.preferences returns inconsistent values even after calling getPreferences(). The behavior is unpredictable and affects critical functionality.
Environment
iOS Version: 15+
Language: Objective-C with Swift interop
Storage: UserDefaults with App Group (group.com.jci.tyco.glss)
Architecture: Singleton pattern for UserPreferences (Swift class)
The Issue
When a push notification arrives and triggers the showEvent: method, user preferences are sometimes loaded correctly and sometimes return nil or default values:
Scenario A (Works - ~60% of time):
Scenario B (Fails - ~40% of time):
Observed Pattern
From extensive logging over multiple test runs:
Key Observation:
At app launch: Preferences often load successfully
Seconds later when push arrives: Same preferences become unavailable
User navigates to another screen and back: Preferences suddenly work again
Code Structure
Objective-C AppDelegate.m
- (void)showEvent:(FMEvent *)event eventReadSource:(FMEventReadSourceType)eventSource {
BOOL isUserRoleEndUser = [[FMUserRolesRepository instance] userRoleAvailable:FMUserRoleEndUser];
UserPreferences *userPrefs = [UserPreferences shared];
[userPrefs getPreferences]; // ← Called here
// Immediately after, sometimes nil!
bool hasFireAudio = [userPrefs.preferences.fireAudio.lowercaseString isEqual:@"true"];
NSLog(@"isUserRoleEndUser: %d | Fire Audio: %d", isUserRoleEndUser, hasFireAudio);
// Decision logic based on preferences
if ([FMConstants getUserRegion] == UserRegionUK &&
isUserRoleEndUser &&
userPrefs.preferences && // ← Sometimes nil here
hasFireAudio) {
// Show Screen A
} else {
// Show Screen B
}
}
```class UserPreferences: NSObject {
static let shared = UserPreferences()
var preferences: FMUserPreferencesInfo? // ← This becomes nil intermittently
func getPreferences() {
let userDefaults = UserDefaults(suiteName: "group.com.jci.tyco.glss")
if let data = userDefaults?.data(forKey: "UserPreferences") {
// Decode and set preferences
self.preferences = // decoded object
} else {
// Create default preferences with fireAudio = "false"
self.preferences = FMUserPreferencesInfo()
}
}
}
Selecting any option will automatically load the page