SafeDefaultsKit
Pure-Swift library for safe access to UserDefaults
Requirements
- Xcode 9
- Swift 4.0+
- iOS 10.0+
Installation
CocoaPods
CocoaPods is a dependency manager for Cocoa projects.
To integrate SafeDefaultsKit into your Xcode project using CocoaPods, add the following line to your Podfile and run pod install.
pod 'SafeDefaultsKit', git: 'https://github.com/jrsaruo/SafeDefaultsKit.git'Carthage
Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
To integrate SafeDefaultsKit into your Xcode project using Carthage, specify it in your Cartfile:
github "jrsaruo/SafeDefaultsKit"
Run carthage update to build the framework and drag the built SafeDefaultsKit.framework into your Xcode project.
Features
Safe access by using Enum as key
You can define original keys and safely access to UserDefaults by using them.
// Initialize your Defaults
let defaults = YourDefaults()
// Set value for Defaults
defaults.set("saved string", forKey: .definedKey)
// Get value from Defaults
print(defaults.string(forKey: .definedKey) ?? "no value") // -> "saved string"
// Remove value from Defaults
defaults.removeObject(forKey: .definedKey)
print(defaults.string(forKey: .definedKey) ?? "no value") // -> "no value"Divide Defaults
By dividing Defaults for each purposes, it becomes easier to manage keys.
let loginInfoDefaults = LoginInfoDefaults() // LoginInfoDefaults has "loginDate" key
loginInfoDefaults.set(Date(), forKey: .loginDate)
let settingDefaults = SettingDefaults() // SettingDefaults has "mainColor" key
settingDefaults.set("red", forKey: .mainColor)Usage
At first, declare the type that conforms to the SafeDefaults protocol. This requires nested Enum named Keys that conforms to the DefaultsKey protocol and stores string raw values, and its cases are used as keys for your Defaults.
Simplest Declaration
struct SampleDefaults: SafeDefaults {
enum Keys: String, DefaultsKey {
case userName
}
}
SampleDefaults().set("Tom", forKey: .userName)
// same as: UserDefaults.standard.set("Tom", forKey: "userName")Customize the key name for UserDefaults
DefaultsKey requires the uniqueValue property, used as the key name for UserDefaults.
uniqueValue's default value is its raw value.
struct SampleDefaults: SafeDefaults {
enum Keys: String, DefaultsKey {
case userName = "customizedKeyForUserName"
}
}
SampleDefaults().set("Tom", forKey: .userName)
// same as: UserDefaults.standard.set("Tom", forKey: "customizedKeyForUserName")If you declare uniqueValue explicitly, it is used as the key name instead of the raw value.
struct SampleDefaults: SafeDefaults {
enum Keys: String, DefaultsKey {
case userName
// customize the key name for UserDefaults
var uniqueValue: String {
return "SampleDefaults.\(Keys.self).\(self.rawValue)"
}
}
}
SampleDefaults().set("Tom", forKey: .userName)
// same as: UserDefaults.standard.set("Tom", forKey: "SampleDefaults.Keys.userName")⚠️ Caution
Difference from UserDefaults
getter methods for double, float, integer return optional value. If the specified key doesn‘t exist, these methods return nil.
UserDefaults.standard.removeObject(forKey: "removed")
UserDefaults.standard.integer(forKey: "removed") // -> 0
YourDefaults().integer(forKey: .removed) // -> nilKeys.uniqueValue is NOT guaranteed to be unique entirely
uniqueValue is used as key name for UserDefaults and default is its raw value, so there is fear that some keys conflict each other.
❌ NG sample
struct BookDefaults: SafeDefaults {
enum Keys: String, DefaultsKey {
case id
}
}
struct ComicDefaults: SafeDefaults {
enum Keys: String, DefaultsKey {
case id // MARK: This uniqueValue conflicts with that of BookDefaults.Keys.id
}
}
BookDefaults().set(5, forKey: .id) // same as: UserDefaults.set(5, forKey: "id")
ComicDefaults().set(10, forKey: .id) // same as: UserDefaults.set(10, forKey: "id")
print(BookDefaults().integer(forKey: .id)!) // -> 10 (not 5)⭕️ Avoid the same case name
struct BookDefaults: SafeDefaults {
enum Keys: String, DefaultsKey {
case bookID
}
}
struct ComicDefaults: SafeDefaults {
enum Keys: String, DefaultsKey {
case comicID
}
}
BookDefaults().set(5, forKey: .bookID) // same as: UserDefaults.set(5, forKey: "bookID")
ComicDefaults().set(10, forKey: .comicID) // same as: UserDefaults.set(10, forKey: "comicID")
print(BookDefaults().integer(forKey: .bookID)!) // -> 5⭕️ Set differerent rawValues explicitly
struct BookDefaults: SafeDefaults {
enum Keys: String, DefaultsKey {
case id = "bookID"
}
}
struct ComicDefaults: SafeDefaults {
enum Keys: String, DefaultsKey {
case id = "comicID"
}
}
BookDefaults().set(5, forKey: .id) // same as: UserDefaults.set(5, forKey: "bookID")
ComicDefaults().set(10, forKey: .id) // same as: UserDefaults.set(10, forKey: "comicID")
print(BookDefaults().integer(forKey: .id)!) // -> 5⭕️ Declare uniqueValue by yourself
It is possible to customize key name for UserDefaults by declaring uniqueValue. For example, you can use your Defaults' name as the namespace.
struct BookDefaults: SafeDefaults {
enum Keys: String, DefaultsKey {
case id
var uniqueValue: String {
// use "BookDefaults.Keys" as the namespace
return "BookDefaults.\(Keys.self).\(self.rawValue)"
}
}
}
struct ComicDefaults: SafeDefaults {
enum Keys: String, DefaultsKey {
case id
var uniqueValue: String {
return "ComicDefaults.\(Keys.self).\(self.rawValue)"
}
}
}
BookDefaults().set(5, forKey: .id) // same as: UserDefaults.set(5, forKey: "BookDefaults.Keys.id")
ComicDefaults().set(10, forKey: .id) // same as: UserDefaults.set(10, forKey: "ComicDefaults.Keys.id")
print(BookDefaults().integer(forKey: .id)!) // -> 5License
SafeDefaultsKit is under MIT license. See the LICENSE file for more info.