UserDefaults.register(defaults:) Footgun
“Every instance of UserDefaults shares the contents of the argument and registration domains.” In other words, the result of calling
registerDefaultson the object returned by[NSUserDefaults initWithSuiteName:]is the same as callingregisterDefaultson the object returned by[NSUserDefaults standardUserDefaults]! Yet the documentation forregisterDefaultsdoes not mention this fact.How did this become a Link Unshortener bug? In the NSApplicationDelegate method
applicationWillFinishLaunching, I call[NSUserDefaults initWithSuiteName:]andregisterDefaultsto register the default values of Link Unshortener settings. Then I check whether the app container settings need to be migrated. If migration is necessary, then I call[NSUserDefaults setObject: forKey:]on the group defaults, using[NSUserDefaults objectForKey:]from the app defaults. If the default key has never been set in the app defaults, then[NSUserDefaults objectForKey:]should return nil. Or so I thought! But at that pointregisterDefaultshas already been called on the group defaults object, and the app defaults object shares the registration domain with the group defaults object, so[NSUserDefaults objectForKey:]returns a non-nil value, which gets saved in the group defaults.
Previously:
- Swift Configuration
- Launching Before UserDefaults Is Available
- Use and Misuse of NSUserDefaults
- Debugging NSUserDefaults
3 Comments RSS · Twitter · Mastodon
It's documented:
> This method assigns the key-value pairs you provide to the registration domain, which is typically the last domain in the search list. The registration domain is volatile, so you must register the set of default values each time your app launches.
https://developer.apple.com/documentation/foundation/userdefaults/register(defaults:)
Léo, it looks like you didn't read the blog post, which in the second paragraph links to the same documentation as your first comment.
And the issue isn't that the registration domain is volatile; of course we knew that.