#Palau: NSUserDefaults with Wings!
Features | Included Types | Installation | Validators and Defaults | Custom Types | DidSet Callback |
-------Features
- Easily store your Custom Types in NSUserDefaults
- Most Standard Types Out-of-the-box
- Per-property based Chainable Rules
- Supports NSCoding and RawRepresentable
- 300% Type Safe :P
- 100% Unit Test Coverage
- Swift 3 features coming!
Already Included Types
Swift
- Bool
- Int
- UInt
- Float
- Double
- String
- Array
- Dictionary
Foundation
- NSNumber
- NSString
- NSArray
- NSDictionary
- NSDate
- NSData
- UIColor
Requirements
- Swift 2.2
- iOS 8.0+ / tvOS 9.0+ / watchOS 2.0+
- Xcode 7.3+
Installation
Carthage
To integrate Palau into your project using Carthage, add to your Cartfile
:
github "symentis/Palau" ~> 1.0
Run carthage update
to build the framework and drag the built Palau.framework
into your Xcode project.
See more instructions on the Carthage page.
CocoaPods
To integrate Palau into your project using CocoaPods, add to your Podfile
:
use_frameworks!
pod 'Palau', '~> 1.0'
Run pod install
to install the framework into your Xcode workspace.
Usage
Import Palau
import Palau
Once you import the framework you can setup PalauDefaults like:
/// Your defaults are defined as extension of PalauDefaults
///
/// - The generic type of your PalauDefaultsEntry must conform
/// to the protocol PalauDefaultable.
/// - We provide support for the most common types.
/// - `value` is a helper function defined in PalauDefaults
/// - The String "backingName" is the key used in NSUserDefaults
/// - The empty `set` is used to please the compiler
extension PalauDefaults {
/// a NSUserDefaults Entry of Type String with the key "backingName"
public static var name: PalauDefaultsEntry<String> {
get { return value("backingName") }
set { }
}
}
Set
Every value of a PalauDefaultsEntry
will always be optional.
If you want to set a value you call:
PalauDefaults.name.value = "I am a great String value!"
Get
Getting your value back is as easy as:
/// name is an Optional<String>
let name = PalauDefaults.name.value
Delete
You can delete a property by setting it to nil
:
PalauDefaults.name.value = nil
Or
/// skip custom rules and delete
PalauDefaults.name.clear()
Custom Rules
Providing a Default Value
If you want to provide a default, when there is no value set, you can write a custom rule. This allows fine granular control on your values.
We include two rule types by default: whenNil
and ensure
import Palau
extension PalauDefaults {
public static var color: PalauDefaultsEntry<UIColor> {
/// whenNil provides a value that will be returned
/// when the related NSUserDefaults value is nil
/// (e.g. the 1st time, or after clear)
get { return value("color").whenNil(use: UIColor.redColor()) }
set { }
}
}
/// is UIColor.redColor()
let color: UIColor? = PalauDefaults.color.value
Providing a Validator
You can also build up arbitrary rules for your value like:
/// Custom Validator Closure
let lessThan10: Int? -> Bool = {
return $0.map { $0 < 10 } ?? false
}
/// the PalauDefaultsEntry with the key "intValueMin10" has 2 rules
/// - 1. when the value is nil - we will get or set 10
/// - 2. when the value is less than 10 (see lessThan10 closure) - we will also get or set 10
/// - Add as many chainable rules as you like
public static var intValueMin10: PalauDefaultsEntry<Int> {
get { return value("intValue")
.whenNil(use: 10)
.ensure(when: lessThan10, use: 10) }
set { }
}
/// try setting the property to 8
PalauDefaults.intValueMin10.value = 8
/// property ensured to be >= 10
assert(PalauDefaults.intValueMin10.value == 10)
/// try setting the property to 11
PalauDefaults.intValueMin10.value = 11
/// property changed to 11
assert(PalauDefaults.intValueMin10.value == 11)
Custom Types
In Swift 2.2 Classes and Protocols can be used to constrain the ValueType.
For example this is how Palau adds support for RawRepresentable
via an Extension:
/// Extension for RawRepresentable types aka enums
extension PalauDefaultable where ValueType: RawRepresentable {
public static func get(key: String, from defaults: NSUD) -> ValueType? {
guard let val = defaults.objectForKey(key) as? ValueType.RawValue else { return nil }
return ValueType(rawValue: val)
}
public static func set(value: ValueType?, forKey key: String, in defaults: NSUD) -> Void {
guard let value = value?.rawValue as? AnyObject else { return defaults.removeObjectForKey(key) }
defaults.setObject(value, forKey: key)
}
}
Generally for Types which conform to NSCoding
you can usually just provide an
extension like so:
/// Make UIColor PalauDefaultable
extension UIColor: PalauDefaultable {
public typealias ValueType = UIColor
}
Look Mum, even Structs!
For custom types you can provide an extension on your type for PalauDefaultable
,
to implement a get
and a get
function.
// example Struct called Structy for demonstrating we can save a Struct with Palau
public struct Structy {
let tuple: (String, String)
}
// our Structy PalauDefaultable extension allowing the mapping between PalauDefaults and the Type
// here we just map the two values to two keys named "1" and "2"
extension Structy: PalauDefaultable {
public static func get(key: String, from defaults: NSUD) -> Structy? {
guard let d = defaults.objectForKey(key) as? [String: AnyObject] ,
let t1 = d["1"] as? String,
let t2 = d["2"] as? String else { return nil }
return Structy(tuple: (t1, t2))
}
public static func set(value: Structy?, forKey key: String, in defaults: NSUD) -> Void {
guard let value = value else { return defaults.setObject(nil, forKey: key) }
defaults.setObject(["1": value.tuple.0, "2": value.tuple.1], forKey: key)
}
}
// now create a property on PalauDefaults
extension PalauDefaults {
public static var structWithTuple: PalauDefaultsEntry<Structy> {
get { return value("structy") }
set { }
}
}
DidSet Callback
You can easily register a didSet
callback, which gets fired when the value has changed.
extension PalauDefaults {
public static var strings: PalauDefaultsEntry<[String]> {
get { return value("strings").didSet({ print("changed to:", $0, "from:", $1) }) }
set { }
}
}
Limitations for Swift 2.2
We are waiting for more Swift 3 generics features like extensions on Generic types.... yay!
Then we get even more type saftey on arrays and dictionaries.
Plus we might be able to make generic types conform to PalauDefaultable
.
FAQ
What's the origin of the name Palau?
Palau is named after the Palau swiftlet, a species of swift, endemic to the island of Palau.
Btw - if you really don`t like the name, you can use a typealias
typealias Defaults = PalauDefaults
typealias Defaultable = PalauDefaultable
/// for Swift 3 even:
/// typealias DefaultsEntry<T> = PalauDefaultsEntry<T>
Credits
Palau is owned and maintained by Symentis GmbH.
Developed by: Elmar Kretzer & Madhava Jay
Follow for more Swift Goodness:
##Logo
Awesome Logo by: 4th motion
String Test Fixtures
License
Palau is released under the Apache 2.0 license. See LICENSE for details.