I think it may be because your code doesn't synchronize()
the change to the defaults, so it doesn't save it immediately.
You could add a call to defaults.synchronize()
, but you'd still be running this code every time the view is drawn (in .onAppear
). That's a code smell; you're doing it in a place that can be executed multiple times within the app's lifetime, which opens you up to exactly the sort of behaviour you're experiencing.
A better way to do this is to move it to the main app startup, e.g.:
@main
struct MainApp: App {
init() {
// Is this the first launch of the app?
if(defaultsGetIsFirstLaunch(version: 6)) {
// Generate sample data...
defaultsUpdateIsFirstLaunch(version: 6, value: false)
}
}
}
func defaultsGetIsFirstLaunch(version: Int) -> Bool {
defaultsGetBool(forKey: "firstLaunchVersion\(version)", withFallback: true)
}
func defaultsUpdateIsFirstLaunch(version: Int, value: Bool) {
defaultsSet(value as Bool, forKey: "firstLaunchVersion\(version)")
}
func defaultsGetBool(forKey key: String, withFallback fallback: Bool) -> Bool {
guard let value = UserDefaults(suiteName: "myappgroup")?.object(forKey: key) else { return fallback }
return value as? Bool ?? fallback
}
func defaultsSet(_ value: Any, forKey key: String) {
UserDefaults(suiteName: "myappgroup")?.set(value, forKey: key)
UserDefaults(suiteName: "myappgroup")?.synchronize()
}
Note: I abstract the defaults functions out to their own methods, as above, so I have a method that gets a Bool from the defaults, one for an Int, one for a String etc., and a single method that sets a value in the defaults.
The get functions take a 'fallback' value, so if the value hasn't been set yet, it will return the fallback value instead.
So... In the code above, it runs in the main app's init()
function. It checks if this is the first launch and passes in true
if the key hasn't been set yet. It hasn't, so it returns true - this is the first launch of version 6. We generate the sample data, then we set the defaults value to false
. The next time you run the app, the key has been set to false, so the code doesn't run anymore.