swiftswiftuinsuserdefaultsuserdefaults

UserDefaults suites ("standard" and "custom") overlapping - the keys appear in both?


I'm currently working on a Swift project where I register default values to two different UserDefaults suites: the standard suite and a custom suite. However, I'm seeing an unexpected (potentially correct) overlap between them. I've tried finding information in the docs about this but couldn't.

Here's a simplified version of my code:

Note: com.domain.custom isn't the app bundle ID, I was only mimicking App Groups reverse domain without the group. and using a domain not the same as my app ID

import SwiftUI

@main
struct TestApp: App {
  init() {
        // Register defaults for standard UserDefaults
        UserDefaults.standard.register(defaults: [
            "standardA": "Standard A value"
        ])

        // Register defaults for custom UserDefaults with suiteName
        if let customUserDefaults = UserDefaults(suiteName: "com.domain.custom") {
            customUserDefaults.register(defaults: [
                "customA": "Custom A value"
            ])
        }

        // Function to print all keys
        func printCustomKeys(for userDefaults: UserDefaults) {
            let allKeys = userDefaults.dictionaryRepresentation()
            print("Custom keys for UserDefaults suite:")
            allKeys.forEach { key, value in
                print("\(key) = \(value)")
            }
        }

        // Usage
        printCustomKeys(for: UserDefaults.standard)
        if let customUserDefaults = UserDefaults(suiteName: "com.domain.custom") {
            printCustomKeys(for: customUserDefaults)
        }
    }

    var body: some Scene {
        WindowGroup { .. }
    }
}

The output I get is:

Custom keys for UserDefaults suite:
AppleLocale = en_AU
standardA = Standard A value
...
customA = Custom A value
...

Custom keys for UserDefaults suite:
customA = Custom A value
standardA = Standard A value
...

I register "standardA" to UserDefaults.standard and "customA" to a custom suite (com.domain.custom). But when I print the keys, I see both "standardA" and "customA" in both suites, even though I expected "standardA" to only exist in the standard suite and "customA" only in the custom suite.

My question: Why are keys from UserDefaults.standard appearing in my custom suite and vice versa?

Is there some behaviour in UserDefaults that I'm misunderstanding? I was under the impression that standard and custom suites should be completely isolated.


Solution

  • This is because you use register. From the docs,

    Adds the contents of the specified dictionary to the registration domain.

    If there is no registration domain, one is created using the specified dictionary, and registrationDomain is added to the end of the search list.

    The contents of the registration domain are not written to disk; you need to call this method each time your application starts.

    So using register will always add the keys and values to the registration domain. That is the domain with the name UserDefaults.registrationDomain. The UserDefaults object you create will always search the registration domain, in addition to the domain indicated by suitName.