This project is currently in beta and APIs are subject to change.
A Swift extension to use string-based API in a type-safe way.
All these fantastic API are compatible with traditional string-based API.
- Swift 5.2 (Xcode 11.4)
You can use
NSUbiquitousKeyValueStorewith almost the same API.
extension UserDefaults.DefaultKeys {
    static let intKey   = Key<Int>("intKey")
    static let colorKey = Key<Color>("colorKey", transformer: .keyedArchive)
    static let pointKey = Key<CGPoint>("pointKey", transformer: .json)
}let ud = UserDefaults.standard
// Get & Set
let value = ud[.intKey]
ud[.stringKey] = "foo"
// Modify
ud[.intKey] += 1
ud[.stringKey] += "bar"
// Typed array
ud[.stringArrayKey].contains("foo")
ud[.intArrayKey][0] += 1
// Work with NSKeyedArchiver
ud[.colorKey] = UIColor.orange
ud[.colorKey]?.redComponent
// Work with JSONEncoder
ud[.pointKey] = CGPoint(x: 1, y: 1)
ud[.pointKey]?.x += 1
// Modern Key-Value Observing
let observation = defaults.observe(.someKey, options: [.old, .new]) { (defaults, change) in
    print(change.newValue)
}
// KVO with deserializer
let observation = defaults.observe(.rectKey, options: [.old, .new]) { (defaults, change) in
    // deserialized automatically
    if let rect = change.newValue {
        someView.frame = rect
    }
}
// Register with serializer
ud.register(defaults: [
    .intKey: 42,
    .stringKey: "foo",
    .colorKey: UIColor.blue, // serialized automatically
    .pointKey: CGPoint(x: 1, y: 1),
])If associated type of a key conforms DefaultConstructible, a default value will be constructed for nil result.
public protocol DefaultConstructible {
    init()
}Here's types that conforms DefaultConstructible and its default value:
| Type | Default value | 
|---|---|
| Bool | false | 
| Int | 0 | 
| Float/Double | 0.0 | 
| String | "" | 
| Data | [empty data] | 
| Array | [] | 
| Dictionary | [:] | 
| Optional | nil | 
Note: Optional also conforms DefaultConstructible, therefore a key typed as DefaultKey<Any?> aka DefaultKey<Optional<Any>> will still returns nil, which is the result of default construction of Optional.
You can always associate an optional type if you want an optional value.
extension UITableView.CellReuseIdentifiers {
    static let customCell : ID<MyCustomCell> = "CustomCellReuseIdentifier"
}tableView.register(id: .customCell)let cell = tableView.dequeueReusableCell(withIdentifier: .customCell, for: indexPath)
// Typed as MyCustomCell// That's it!
extension MyCustomCell: UINibFromTypeGettable
// Or, incase your nib name is not the same as class name
extension MyCustomCell: UINibGettable {
    static var nibName = "MyNibName"
}
// Then register
tableView.registerNib(id: .customCell)extension UIStoryboard.Identifiers {
    static let customVC: ID<MyCustomViewController> = "CustomVCStoryboardIdentifier"
}// Also extend to get main storyboard
let sb = UIStoryboard.main()
let vc = sb.instantiateViewController(withIdentifier: .customVC)
// Typed as MyCustomViewController// Define your associate keys
extension YourClass.AssociateKeys {
    static let someKey: Key<Int> = "someKey"
}
// Use it!
yourObject[.someKey] = 42GenericID is available under the MIT license. See the LICENSE file.