Safer Swift Application Settings with Protocols and keyPath()
Protocols + #keyPath + Computed Properties = Powerful User Settings 💪
Keeping your application settings in sync, persisting, and assigning default values can become messy. UserDefaults is a great way to store non-sensitive application settings. However, due to its nature as a key-value structure, we need to resort to using strings to store our data.
This is a problem waiting to happen. A simple misspelling can cause us to access an invalid value, or fail to properly update a value.
Making UserDefaults Safer 👷
Swift 3 and above comes equipped with the wonderful
#keyPath() expression. This can allow us to avoid the use of strings with UserDefaults in a type-safe manner.
#keyPath() under the hood still uses the Objective-C runtime and will resolve to a string of the path it’s referencing. Therefore, the key needs to be exposed to Objective-C via the
@objc decorator. Wrapping our keys in a protocol can allow us to expose the entire protocol and cleanly access them:
Ok. So this works. It may appear a bit convoluted at the moment, but using protocols will allow us for a clean interface to build off for our next step.
Adding in Computed Properties
Using this convention we can have a protocol define our settings, whether they are Bools, Strings, or otherwise. We can then create an object
AppSettings which conforms to the protocol, and using computed properties we can set and obtain our variables directly from UserDefaults in a safe way!
The compiler will also enforce the protocol implementing, making sure you don’t forget to implement all your settings! 👍
Our settings are really starting to come to life now. You will also notice our
useAmbientLight variable will be set to
false if nothing exists in UserDefaults — this is how we can specify a default value for each setting!
Settings can be retrieved just as easy!
Great! We have a way to store our variables, set, and retrieve them easily and safely. But what about other types like Strings? What if you have a lot of settings?
Breaking up our protocols and using extensions can help clean up our class. Using the
value<T>(for key:) function as written above we can easily get settings for any other UserDefault supported object (like String), and even Optionals!
The combination of #keyPath, protocols, and computed properties is an amazingly power combination for powering settings in your Swift applications.
Adding a setting is as easy as appending it to existing protocol or creating a new one. The compiler will even warn you if you forget to implement one.
Hopefully, this fun way to implement settings will prove useful in your apps as well 😃.