October 21, 2018
To clear a value from UserDefaults, I previously thought it was fine to do something like this:
UserDefaults.standard.set(nil, forKey: "myKey")
Turns out that this works differently in different iOS versions. Starting in iOS 11, setting nil for a key works as I expected and the following prints nil:
UserDefaults.standard.set(nil, forKey: "myKey")
print(UserDefaults.standard.data(forKey: "myKey"))
// nil
However, in iOS 10, UserDefaults actually tries to serialize nil into a Data:
UserDefaults.standard.set(nil, forKey: "myKey")
print(UserDefaults.standard.data(forKey: "myKey"))
// 135 bytes
If we look at this data, it seems like it might be a plist:
print(String(
data: UserDefaults.standard.data(forKey: "myKey")!,
encoding: .ascii
))
Optional("bplist00Ô\u{01}\u{02}\u{03}\u{04}\u{05}\u{08}\n\u{0B}T$topX$objectsX$versionY$archiverÑ\u{06}\u{07}Troot\0¡\tU$null\u{12}\0\u{01} _\u{10}\u{0F}NSKeyedArchiver\u{08}\u{11}\u{16}\u{1F}(25:<>DI\0\0\0\0\0\0\u{01}\u{01}\0\0\0\0\0\0\0\u{0C}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0[")
Turns out that we can use PropertyListSerialization to actually print the serialized value of nil:
let data = UserDefaults.standard.data(forKey: "myKey")!
let propertyList = try! PropertyListSerialization.propertyList(
from: data,
options: [],
format: nil
)
print(propertyList)
{
"$archiver" = NSKeyedArchiver;
"$objects" = (
"$null"
);
"$top" = {
root = "<CFKeyedArchiverUID 0x79e7c420 [0xf126e8]>{value = 0}";
};
"$version" = 100000;
}
For whatever reason, this was totally unexpected to me. It’s particularly tricky when you’re trying to interpret the value of a previously cleared UserDefaults key as JSON:
if let data = UserDefaults.standard.data(forKey: "myKey") {
let json = try JSONDecoder().decode(MyClass.self, from: data)
print(json)
} else {
print("No data found")
}
This will work just fine on iOS 11+, but throw an error on iOS 10.
Here’s an example project which demonstrates this behavior. I ended up finding one stackoverflow post about it, but as far as I can tell this isn’t documented anywhere. If you have any more info about it, let me know 👋
Lesson learned — setting nil isn’t a good way to clear a UserDefaults value. Instead, use the recommended approach:
UserDefaults.standard.removeObject(forKey: "myKey")
I'm Noah, a software developer based in the San Francisco Bay Area. I focus mainly on full stack web and iOS development