Type Enforced UserDefaults

Dave Poirier
ITNEXT
Published in
3 min readMar 25, 2022

--

On iOS the UserDefaults is a persistent dictionary stored and managed by iOS that survives the application runtime. It can be used to store and retrieve key-value pairs of different data types in-between user sessions of the app.

In this article, we will cover how to define a new class and type-specific enum to ensure that each key can only read/written by a specific data type.

Photo by Montse Monmo on Unsplash

Problem definition

Given a key for UserDefaults, you can store and retrieve any type. For example:

In the example above, we initially set an integer type value for the key “MyValue” but then we overwrite that value with a string. If the value is read back expecting an Integer we might not get what we expected:

UserDefaults will silently store different types for the same key. It will also silently return to you automatically converted values between the last stored type for a given key and the type you are trying to retrieve.

While this flexibility may be useful, developers have to manually ensure they are using the correct data types associated to each key. Below is a method by which we can guarantee at compile time that any given key is only accessed using the expected type.

Enforcing Type in UserDefaults

Let’s start by creating a new class which will supervise read/write operations to UserDefaults for two types: Integer and String.

So far, nothing would prevent us from encountering the same issue we did with UserDefaults. We could very easily set either an Integer or a String for any given key. Let’s solve this:

Success! Our TypeSafeUserDefaults now prevents us from storing a string in a key expected to be used to store integers. Better yet, we get immediate feedback directly in Xcode and our project will fail to compile if we attempt to use the wrong type.

Ensuring all keys are unique

While the TypeSafeUserDefaults prevents storing values of the wrong type, if the two enums define the same keys, we can still end up with the same issue. Consider:

Unfortunately I haven’t found a way to get compile time errors from this. Luckily, Xcode supports unit tests so we can confirm our keys are unique by making our enum CaseIterable and using some unit tests. Let’s generate some enum with duplicated keys:

Now let’s implement a unit test to confirm all keys are unique:

When the unit tests run, we will now get this failure:

XCTAssertFalse failed — StringKey myOtherValue conflicts with an already defined key

I hope this saves you hours of troubleshooting. Enjoy!

--

--

Senior iOS Developer | Mobile Security And Reliability Specialist