SwiftyUserDefaults 4.0
May 1, 2019 18:01 · 4 minute read
I’m really happy to say that SwiftyUserDefaults 4 was finally released at the end of last week. Due to changes of Swift 4 and 5, the core of the library was rewritten from the ground up. Don’t worry, though, as the API is almost untouched and the new version introduces mostly improvements and additions. Here is a short summary of what’s inside.
Default values
Probably the most noticeable change in version 4 is handling of default values. In version 3 you would do a following:
let key = DefaultsKey<String>("username")
let username = Defaults[key]
And the username would be either the value saved to UserDefaults
or a default value (""
for String
). Version 4 removes that - now you have to make the default value explicit:
let key = DefaultsKey<String>("username", defaultValue: "admin1")
let username = Defaults[key]
Of course, you might want to use an optional type instead and we support that as well:
let key = DefaultsKey<String?>("username")
let username = Defaults[key]
You can also set a default value for an optional key. Be careful, though! The behavior of default value is “either return a value that is stored in UserDefaults
or return the default value”. This means that if you save nil
, you will receive the default value when accessing the key. In case you want to only set the value at the very first time, you would have to do it manually.
Codable, NSCoding, enums
Version 4 introduces a new protocol, DefaultsSerializable
. If you want to store any type that implements Codable
, NSCoding
or is an enum, just add DefaultsSerializable
to the implementation list of your type:
final class Frog: Codable, DefaultsSerializable {
let name: String
}
extension UIColor: DefaultsSerializable {}
No implementation needed! If you, however, would have a custom type that you would like to store in UserDefaults
, then…
Custom types
… worry no more. Version 4 was written with customization in mind - it uses “bridges” to know the get/set protocol for a type. You can check this part of the Readme to get the details of how to implement your own bridge or how to replace an existing one for a type, but here is an example of a bridge that persists the value as an Int
:
open class DefaultsIntBridge: DefaultsBridge<Int> {
open override func save(key: String, value: Int?, userDefaults: UserDefaults) {
userDefaults.set(value, forKey: key)
}
open override func get(key: String, userDefaults: UserDefaults) -> Int? {
if let int = userDefaults.number(forKey: key)?.intValue {
return int
}
// Fallback for launch arguments
if let string = userDefaults.object(forKey: key) as? String,
let int = Int(string) {
return int
}
return nil
}
}
KVO support
We also introduced a layer on top of UserDefaults
and KVO
so it feels more Swifty 😉
let key = DefaultsKey<String?>("username")
Defaults.observe(key: key) { update in
// here you can access `oldValue`/`newValue` etc.
}
Launch arguments support
Updating UserDefaults
using launch arguments seems to be a hot topic and we don’t want to take all the fun from you. Version 4 supports String
, Bool
, Double
and Int
based launch arguments.
func testExample() {
let app = XCUIApplication()
app.launchArguments = ["-skipLogin", "true", "-loginTries", "3", "-lastGameTime", "61.3", "-nickname", "sunshinejr"]
app.launch()
}
CocoaPods / Carthage / SwiftPackageManager support
It’s important for us to get this release out to everyone that wants to play with it. Because of that, we support all of the package managers mentioned above and have integration tests for each one of them that runs on every pull request.
Swift 4.1 / 4.2 / 5.0 & Xcode 10.2 support
And the CI integration doesn’t stop on package managers only. We also have multiple Swift version builds to compliment the package managers integration tests. This should make the maintenance of this library a lot easier in the future.
Migration guide
Here is a migration guide if you are transitioning from version 3 to 4.
-
All of this and more available at GitHub. Hope you all enjoy it.