Search code examples
iosswiftmacosplistuserdefaults

Determining Swift Types That Can Be Stored in UserDefaults


I am in the beginning stages of developing an open-source utility for storing state in the Bundle UserDefaults.

I'm encountering an issue when I add non-Codable data types to my Dictionary of [String: Any].

I need to be able to vet the data before trying to submit it, because the UserDefaults.set(_:) method won't throw any errors. It just crashes.

So I want to make sure that the Dictionary that I'm submitting is kosher.

I can't just check if it's Codable, because it can sometimes say that it isn't, when the struct is actually good. (It's a Dictionary<String, Any>, and I can cram all kinds of things in there).

I need to validate that the Dictionary can produce a plist. If this were ObjC, I might use one of the NSPropertyListSerialization methods to test the Dictionary, but it appears as if this set of methods is not available to Swift.

According to the UserDefaults docs, there are a specific set of types and classes that are "plist-studly."

I think testing each type in the list is unacceptable. I need to see if I can find a way to test that won't be screwed the first time Apple updates an OS.

Is there a good way to test a Dictionary<String, Any> to see if it will make UserDefaults.set(_:) puke?


Solution

  • The Property List type set of UserDefaults is very limited. The supported types are

    • NSString → Swift String
    • NSNumber → Swift Int, Double or Bool
    • NSDate → Swift Date
    • NSData → Swift Data
    • Arrays and dictionaries containing the 4 value types.

    Any is not supported unless it represents one of the 4 value or 2 collection types.

    Property List compliant collection types can be written to UserDefaults with PropertyListSerialization (even in Swift).


    There are two protocols to serialize custom types to Data

    • Codable can serialize structs and classes.
    • NSCoding can serialize subclasses of NSObject.

    All types in the structs/classes must be encodable and decodable (means conform to the protocol themselves).


    The APIs of PropertyListSerialization / PropertyListEncoder/-Decoder and NSKeyed(Un)Archiver provide robust error handling to avoid crashes.