Clean up some docs
This commit is contained in:
parent
534157f1b5
commit
4a0f5e7c3e
|
@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
||||||
s.authors = { 'Sindre Sorhus' => 'sindresorhus@gmail.com' }
|
s.authors = { 'Sindre Sorhus' => 'sindresorhus@gmail.com' }
|
||||||
s.source = { :git => 'https://github.com/sindresorhus/Defaults.git', :tag => "v#{s.version}" }
|
s.source = { :git => 'https://github.com/sindresorhus/Defaults.git', :tag => "v#{s.version}" }
|
||||||
s.source_files = 'Sources/**/*.swift'
|
s.source_files = 'Sources/**/*.swift'
|
||||||
s.swift_version = '5.3'
|
s.swift_version = '5.4'
|
||||||
s.macos.deployment_target = '10.12'
|
s.macos.deployment_target = '10.12'
|
||||||
s.ios.deployment_target = '10.0'
|
s.ios.deployment_target = '10.0'
|
||||||
s.tvos.deployment_target = '10.0'
|
s.tvos.deployment_target = '10.0'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// swift-tools-version:5.3
|
// swift-tools-version:5.4
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
|
|
|
@ -33,16 +33,14 @@ extension Defaults.CodableBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Any `Value` which protocol conforms to `Codable` and `Defaults.Serializable` will use `CodableBridge`
|
Any `Value` that conforms to `Codable` and `Defaults.Serializable` will use `CodableBridge` to do the serialization and deserialization.
|
||||||
to do the serialization and deserialization.
|
|
||||||
*/
|
*/
|
||||||
extension Defaults {
|
extension Defaults {
|
||||||
public struct TopLevelCodableBridge<Value: Codable>: CodableBridge {}
|
public struct TopLevelCodableBridge<Value: Codable>: CodableBridge {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
`RawRepresentableCodableBridge` is indeed because if `enum SomeEnum: String, Codable, Defaults.Serializable`
|
`RawRepresentableCodableBridge` is needed because, for example, with `enum SomeEnum: String, Codable, Defaults.Serializable`, the compiler will be confused between `RawRepresentableBridge` and `TopLevelCodableBridge`.
|
||||||
the compiler will confuse between `RawRepresentableBridge` and `TopLevelCodableBridge`.
|
|
||||||
*/
|
*/
|
||||||
extension Defaults {
|
extension Defaults {
|
||||||
public struct RawRepresentableCodableBridge<Value: RawRepresentable & Codable>: CodableBridge {}
|
public struct RawRepresentableCodableBridge<Value: RawRepresentable & Codable>: CodableBridge {}
|
||||||
|
@ -84,7 +82,7 @@ extension Defaults {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version below macOS 10.13 and iOS 11.0 does not support `archivedData(withRootObject:requiringSecureCoding:)`.
|
// Version below macOS 10.13 and iOS 11.0 does not support `archivedData(withRootObject:requiringSecureCoding:)`.
|
||||||
// We need to set `requiresSecureCoding` by ourself.
|
// We need to set `requiresSecureCoding` ourselves.
|
||||||
if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) {
|
if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) {
|
||||||
return try? NSKeyedArchiver.archivedData(withRootObject: object, requiringSecureCoding: true)
|
return try? NSKeyedArchiver.archivedData(withRootObject: object, requiringSecureCoding: true)
|
||||||
} else {
|
} else {
|
||||||
|
@ -182,15 +180,7 @@ extension Defaults {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
We need both `SetBridge` and `SetAlgebraBridge`.
|
We need both `SetBridge` and `SetAlgebraBridge` because `Set` conforms to `Sequence` but `SetAlgebra` does not. `Set` conforms to `Sequence`, so we can convert it into an array with `Array.init<S>(S)` and store it in the `UserDefaults`. But `SetAlgebra` does not, so it is hard to convert it into an array. Thats why we need the `Defaults.SetAlgebraSerializable` protocol to convert it into an array.
|
||||||
|
|
||||||
Because `Set` conforms to `Sequence` but `SetAlgebra` not.
|
|
||||||
|
|
||||||
Set conforms to `Sequence`, so we can convert it into an array with `Array.init<S>(S)` and store it in the `UserDefaults`.
|
|
||||||
|
|
||||||
But `SetAlgebra` does not, so it is hard to convert it into an array.
|
|
||||||
|
|
||||||
Thats why we need `Defaults.SetAlgebraSerializable` protocol to convert it into an array.
|
|
||||||
*/
|
*/
|
||||||
extension Defaults {
|
extension Defaults {
|
||||||
public struct SetBridge<Element: Defaults.Serializable & Hashable>: Defaults.Bridge {
|
public struct SetBridge<Element: Defaults.Serializable & Hashable>: Defaults.Bridge {
|
||||||
|
|
|
@ -115,7 +115,6 @@ extension Set: Defaults.Serializable where Element: Defaults.Serializable {
|
||||||
public static var bridge: Defaults.SetBridge<Element> { Defaults.SetBridge() }
|
public static var bridge: Defaults.SetBridge<Element> { Defaults.SetBridge() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extension Array: Defaults.Serializable where Element: Defaults.Serializable {
|
extension Array: Defaults.Serializable where Element: Defaults.Serializable {
|
||||||
public static var isNativelySupportedType: Bool { Element.isNativelySupportedType }
|
public static var isNativelySupportedType: Bool { Element.isNativelySupportedType }
|
||||||
public static var bridge: Defaults.ArrayBridge<Element> { Defaults.ArrayBridge() }
|
public static var bridge: Defaults.ArrayBridge<Element> { Defaults.ArrayBridge() }
|
||||||
|
@ -127,9 +126,9 @@ extension Dictionary: Defaults.Serializable where Key: LosslessStringConvertible
|
||||||
}
|
}
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
/// `NSColor` conforms to `NSSecureCoding`, so it goes to `NSSecureCodingBridge`
|
/// `NSColor` conforms to `NSSecureCoding`, so it goes to `NSSecureCodingBridge`.
|
||||||
extension NSColor: Defaults.Serializable {}
|
extension NSColor: Defaults.Serializable {}
|
||||||
#else
|
#else
|
||||||
/// `UIColor` conforms to `NSSecureCoding`, so it goes to `NSSecureCodingBridge`
|
/// `UIColor` conforms to `NSSecureCoding`, so it goes to `NSSecureCodingBridge`.
|
||||||
extension UIColor: Defaults.Serializable {}
|
extension UIColor: Defaults.Serializable {}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/**
|
/**
|
||||||
All type that able to work with `Defaults` should conform this protocol.
|
Types that conform to this protocol can be used with `Defaults`.
|
||||||
|
|
||||||
It should have a static variable bridge which protocol should conform to `Defaults.Bridge`.
|
The type should have a static variable `bridge` which should reference an instance of a type that conforms to `Defaults.Bridge`.
|
||||||
|
|
||||||
```
|
```
|
||||||
struct User {
|
struct User {
|
||||||
|
@ -21,22 +21,22 @@ public protocol DefaultsSerializable {
|
||||||
typealias Serializable = Bridge.Serializable
|
typealias Serializable = Bridge.Serializable
|
||||||
associatedtype Bridge: DefaultsBridge
|
associatedtype Bridge: DefaultsBridge
|
||||||
|
|
||||||
/// Static bridge for the `Value` which cannot store natively
|
/// Static bridge for the `Value` which cannot be stored natively.
|
||||||
static var bridge: Bridge { get }
|
static var bridge: Bridge { get }
|
||||||
|
|
||||||
/// A flag to determine whether `Value` can be store natively or not
|
/// A flag to determine whether `Value` can be stored natively or not.
|
||||||
static var isNativelySupportedType: Bool { get }
|
static var isNativelySupportedType: Bool { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A Bridge can do the serialization and de-serialization.
|
A `Bridge` is responsible for serialization and deserialization.
|
||||||
|
|
||||||
Have two associate types `Value` and `Serializable`.
|
It has two associated types `Value` and `Serializable`.
|
||||||
|
|
||||||
- `Value`: the type user want to use it.
|
- `Value`: The type you want to use.
|
||||||
- `Serializable`: the type stored in `UserDefaults`.
|
- `Serializable`: The type stored in `UserDefaults`.
|
||||||
- `serialize`: will be executed before storing to the `UserDefaults` .
|
- `serialize`: Executed before storing to the `UserDefaults` .
|
||||||
- `deserialize`: will be executed after retrieving its value from the `UserDefaults`.
|
- `deserialize`: Executed after retrieving its value from the `UserDefaults`.
|
||||||
|
|
||||||
```
|
```
|
||||||
struct User {
|
struct User {
|
||||||
|
@ -44,6 +44,10 @@ struct User {
|
||||||
password: String
|
password: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension User {
|
||||||
|
static let bridge = UserBridge()
|
||||||
|
}
|
||||||
|
|
||||||
struct UserBridge: Defaults.Bridge {
|
struct UserBridge: Defaults.Bridge {
|
||||||
typealias Value = User
|
typealias Value = User
|
||||||
typealias Serializable = [String: String]
|
typealias Serializable = [String: String]
|
||||||
|
@ -53,7 +57,10 @@ struct UserBridge: Defaults.Bridge {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return ["username": value.username, "password": value.password]
|
return [
|
||||||
|
"username": value.username,
|
||||||
|
"password": value.password
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
func deserialize(_ object: Serializable?) -> Value? {
|
func deserialize(_ object: Serializable?) -> Value? {
|
||||||
|
@ -65,7 +72,10 @@ struct UserBridge: Defaults.Bridge {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return User(username: username, password: password)
|
return User(
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -75,12 +85,11 @@ public protocol DefaultsBridge {
|
||||||
associatedtype Serializable
|
associatedtype Serializable
|
||||||
|
|
||||||
func serialize(_ value: Value?) -> Serializable?
|
func serialize(_ value: Value?) -> Serializable?
|
||||||
|
|
||||||
func deserialize(_ object: Serializable?) -> Value?
|
func deserialize(_ object: Serializable?) -> Value?
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol DefaultsCollectionSerializable: Collection, Defaults.Serializable {
|
public protocol DefaultsCollectionSerializable: Collection, Defaults.Serializable {
|
||||||
/// `Collection` does not have initializer, but we need initializer to convert an array into the `Value`
|
/// `Collection` does not have a initializer, but we need a initializer to convert an array into the `Value`.
|
||||||
init(_ elements: [Element])
|
init(_ elements: [Element])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,5 +98,5 @@ public protocol DefaultsSetAlgebraSerializable: SetAlgebra, Defaults.Serializabl
|
||||||
func toArray() -> [Element]
|
func toArray() -> [Element]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience protocol for `Codable`
|
/// Convenience protocol for `Codable`.
|
||||||
public protocol DefaultsCodableBridge: DefaultsBridge where Serializable == String, Value: Codable {}
|
public protocol DefaultsCodableBridge: DefaultsBridge where Serializable == String, Value: Codable {}
|
||||||
|
|
|
@ -6,7 +6,7 @@ extension Defaults {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Migration the given key's value from json string to `Value`.
|
Migrate the given key's value from JSON string to `Value`.
|
||||||
|
|
||||||
```
|
```
|
||||||
extension Defaults.Keys {
|
extension Defaults.Keys {
|
||||||
|
|
|
@ -12,9 +12,7 @@ extension Data: Defaults.NativeType {
|
||||||
extension Data: Defaults.CodableType {
|
extension Data: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Date: Defaults.NativeType {
|
extension Date: Defaults.NativeType {
|
||||||
|
@ -23,9 +21,7 @@ extension Date: Defaults.NativeType {
|
||||||
extension Date: Defaults.CodableType {
|
extension Date: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Bool: Defaults.NativeType {
|
extension Bool: Defaults.NativeType {
|
||||||
|
@ -34,9 +30,7 @@ extension Bool: Defaults.NativeType {
|
||||||
extension Bool: Defaults.CodableType {
|
extension Bool: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Int: Defaults.NativeType {
|
extension Int: Defaults.NativeType {
|
||||||
|
@ -45,9 +39,7 @@ extension Int: Defaults.NativeType {
|
||||||
extension Int: Defaults.CodableType {
|
extension Int: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UInt: Defaults.NativeType {
|
extension UInt: Defaults.NativeType {
|
||||||
|
@ -56,9 +48,7 @@ extension UInt: Defaults.NativeType {
|
||||||
extension UInt: Defaults.CodableType {
|
extension UInt: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Double: Defaults.NativeType {
|
extension Double: Defaults.NativeType {
|
||||||
|
@ -67,9 +57,7 @@ extension Double: Defaults.NativeType {
|
||||||
extension Double: Defaults.CodableType {
|
extension Double: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Float: Defaults.NativeType {
|
extension Float: Defaults.NativeType {
|
||||||
|
@ -78,9 +66,7 @@ extension Float: Defaults.NativeType {
|
||||||
extension Float: Defaults.CodableType {
|
extension Float: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension String: Defaults.NativeType {
|
extension String: Defaults.NativeType {
|
||||||
|
@ -89,9 +75,7 @@ extension String: Defaults.NativeType {
|
||||||
extension String: Defaults.CodableType {
|
extension String: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CGFloat: Defaults.NativeType {
|
extension CGFloat: Defaults.NativeType {
|
||||||
|
@ -100,9 +84,7 @@ extension CGFloat: Defaults.NativeType {
|
||||||
extension CGFloat: Defaults.CodableType {
|
extension CGFloat: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Int8: Defaults.NativeType {
|
extension Int8: Defaults.NativeType {
|
||||||
|
@ -111,9 +93,7 @@ extension Int8: Defaults.NativeType {
|
||||||
extension Int8: Defaults.CodableType {
|
extension Int8: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UInt8: Defaults.NativeType {
|
extension UInt8: Defaults.NativeType {
|
||||||
|
@ -122,9 +102,7 @@ extension UInt8: Defaults.NativeType {
|
||||||
extension UInt8: Defaults.CodableType {
|
extension UInt8: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Int16: Defaults.NativeType {
|
extension Int16: Defaults.NativeType {
|
||||||
|
@ -133,9 +111,7 @@ extension Int16: Defaults.NativeType {
|
||||||
extension Int16: Defaults.CodableType {
|
extension Int16: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UInt16: Defaults.NativeType {
|
extension UInt16: Defaults.NativeType {
|
||||||
|
@ -144,9 +120,7 @@ extension UInt16: Defaults.NativeType {
|
||||||
extension UInt16: Defaults.CodableType {
|
extension UInt16: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Int32: Defaults.NativeType {
|
extension Int32: Defaults.NativeType {
|
||||||
|
@ -155,9 +129,7 @@ extension Int32: Defaults.NativeType {
|
||||||
extension Int32: Defaults.CodableType {
|
extension Int32: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UInt32: Defaults.NativeType {
|
extension UInt32: Defaults.NativeType {
|
||||||
|
@ -166,9 +138,7 @@ extension UInt32: Defaults.NativeType {
|
||||||
extension UInt32: Defaults.CodableType {
|
extension UInt32: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Int64: Defaults.NativeType {
|
extension Int64: Defaults.NativeType {
|
||||||
|
@ -177,9 +147,7 @@ extension Int64: Defaults.NativeType {
|
||||||
extension Int64: Defaults.CodableType {
|
extension Int64: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UInt64: Defaults.NativeType {
|
extension UInt64: Defaults.NativeType {
|
||||||
|
@ -188,9 +156,7 @@ extension UInt64: Defaults.NativeType {
|
||||||
extension UInt64: Defaults.CodableType {
|
extension UInt64: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension URL: Defaults.NativeType {
|
extension URL: Defaults.NativeType {
|
||||||
|
@ -199,9 +165,7 @@ extension URL: Defaults.NativeType {
|
||||||
extension URL: Defaults.CodableType {
|
extension URL: Defaults.CodableType {
|
||||||
public typealias NativeForm = Self
|
public typealias NativeForm = Self
|
||||||
|
|
||||||
public func toNative() -> Self {
|
public func toNative() -> Self { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Optional: Defaults.NativeType where Wrapped: Defaults.NativeType {
|
extension Optional: Defaults.NativeType where Wrapped: Defaults.NativeType {
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Only for migration.
|
Only exists for migration.
|
||||||
|
|
||||||
Represents the type after migration and its protocol should conform to `Defaults.Serializable`.
|
Represents the type after migration and its protocol should conform to `Defaults.Serializable`.
|
||||||
|
|
||||||
It should have an associated type name `CodableForm` which protocol conform to `Codable`.
|
It should have an associated type name `CodableForm` where its protocol conform to `Codable`.
|
||||||
So we can convert the json string into `NativeType` like this.
|
|
||||||
|
So we can convert the JSON string into a `NativeType` like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
guard
|
guard
|
||||||
let jsonString = string,
|
let jsonString = string,
|
||||||
|
@ -24,13 +26,11 @@ public protocol DefaultsNativeType: Defaults.Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Only for migration.
|
Only exists for migration.
|
||||||
|
|
||||||
Represents the type before migration an its protocol should conform to `Codable`.
|
Represents the type before migration an its protocol should conform to `Codable`.
|
||||||
|
|
||||||
The main purposed of `CodableType` is trying to infer the `Codable` type to do `JSONDecoder().decode`
|
The main purposed of `CodableType` is trying to infer the `Codable` type to do `JSONDecoder().decode`. It should have an associated type name `NativeForm` which is the type we want it to store in `UserDefaults`. nd it also have a `toNative()` function to convert itself into `NativeForm`.
|
||||||
It should have an associated type name `NativeForm` which is the type we want it to store in `UserDefaults`.
|
|
||||||
And it also have a `toNative()` function to convert itself into `NativeForm`.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
struct User {
|
struct User {
|
||||||
|
|
|
@ -14,18 +14,27 @@ extension UserDefaults {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get json string in `UserDefaults` and decode it into the `NativeForm`.
|
Get a JSON string in `UserDefaults` and decode it into its `NativeForm`.
|
||||||
|
|
||||||
How it works?
|
How does it work?
|
||||||
For example:
|
|
||||||
Step1. If `Value` is `[String]`, `Value.CodableForm` will covert into `[String].CodableForm`.
|
|
||||||
`JSONDecoder().decode([String].CodableForm.self, from: jsonData)`
|
|
||||||
|
|
||||||
Step2. `Array`conform to `NativeType`, its `CodableForm` is `[Element.CodableForm]` and `Element` is `String`.
|
1. If `Value` is `[String]`, `Value.CodableForm` will covert into `[String].CodableForm`.
|
||||||
`JSONDecoder().decode([String.CodableForm].self, from: jsonData)`
|
|
||||||
|
|
||||||
Step3. `String`'s `CodableForm` is `self`, because `String` is `Codable`.
|
```
|
||||||
`JSONDecoder().decode([String].self, from: jsonData)`
|
JSONDecoder().decode([String].CodableForm.self, from: jsonData)
|
||||||
|
```
|
||||||
|
|
||||||
|
2. If `Array` conforms to `NativeType`, its `CodableForm` is `[Element.CodableForm]` and `Element` is `String`.
|
||||||
|
|
||||||
|
```
|
||||||
|
JSONDecoder().decode([String.CodableForm].self, from: jsonData)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. `String`'s `CodableForm` is `self`, because `String` is `Codable`.
|
||||||
|
|
||||||
|
```
|
||||||
|
JSONDecoder().decode([String].self, from: jsonData)
|
||||||
|
```
|
||||||
*/
|
*/
|
||||||
func migrateCodableToNative<Value: Defaults.NativeType>(forKey key: String, of type: Value.Type) {
|
func migrateCodableToNative<Value: Defaults.NativeType>(forKey key: String, of type: Value.Type) {
|
||||||
guard
|
guard
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
extension Decodable {
|
extension Decodable {
|
||||||
init?(jsonData: Data) {
|
init?(jsonData: Data) {
|
||||||
guard let value = try? JSONDecoder().decode(Self.self, from: jsonData) else {
|
guard let value = try? JSONDecoder().decode(Self.self, from: jsonData) else {
|
||||||
|
@ -147,6 +148,7 @@ extension DispatchQueue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extension Sequence {
|
extension Sequence {
|
||||||
/// Returns an array containing the non-nil elements.
|
/// Returns an array containing the non-nil elements.
|
||||||
func compact<T>() -> [T] where Element == T? {
|
func compact<T>() -> [T] where Element == T? {
|
||||||
|
@ -155,10 +157,12 @@ extension Sequence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extension Defaults.Serializable {
|
extension Defaults.Serializable {
|
||||||
/**
|
/**
|
||||||
Cast `Serializable` value to `Self`.
|
Cast a `Serializable` value to `Self`.
|
||||||
Convert the native support type from `UserDefaults` into `Self`.
|
|
||||||
|
Converts a natively supported type from `UserDefaults` into `Self`.
|
||||||
|
|
||||||
```
|
```
|
||||||
guard let anyObject = object(forKey: key) else {
|
guard let anyObject = object(forKey: key) else {
|
||||||
|
@ -169,7 +173,7 @@ extension Defaults.Serializable {
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
static func toValue(_ anyObject: Any) -> Self? {
|
static func toValue(_ anyObject: Any) -> Self? {
|
||||||
// Return directly if `anyObject` can cast to Value, means `Value` is Native supported type.
|
// Return directly if `anyObject` can cast to Value, since it means `Value` is a natively supported type.
|
||||||
if Self.isNativelySupportedType, let anyObject = anyObject as? Self {
|
if Self.isNativelySupportedType, let anyObject = anyObject as? Self {
|
||||||
return anyObject
|
return anyObject
|
||||||
} else if let value = Self.bridge.deserialize(anyObject as? Serializable) {
|
} else if let value = Self.bridge.deserialize(anyObject as? Serializable) {
|
||||||
|
@ -181,14 +185,15 @@ extension Defaults.Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Cast `Self` to `Serializable`.
|
Cast `Self` to `Serializable`.
|
||||||
Convert `Self` into `UserDefaults` native support type.
|
|
||||||
|
Converts `Self` into `UserDefaults` native support type.
|
||||||
|
|
||||||
```
|
```
|
||||||
set(Value.toSerialize(value), forKey: key)
|
set(Value.toSerialize(value), forKey: key)
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
static func toSerializable(_ value: Self) -> Any? {
|
static func toSerializable(_ value: Self) -> Any? {
|
||||||
// Return directly if `Self` is native supported type, since it does not need serialization.
|
// Return directly if `Self` is a natively supported type, since it does not need serialization.
|
||||||
if Self.isNativelySupportedType {
|
if Self.isNativelySupportedType {
|
||||||
return value
|
return value
|
||||||
} else if let serialized = Self.bridge.serialize(value as? Self.Value) {
|
} else if let serialized = Self.bridge.serialize(value as? Self.Value) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Defaults
|
||||||
import Foundation
|
import Foundation
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
// Create an unique id to test whether LosslessStringConvertible works.
|
// Create an unique ID to test whether `LosslessStringConvertible` works.
|
||||||
private struct UniqueID: LosslessStringConvertible, Hashable {
|
private struct UniqueID: LosslessStringConvertible, Hashable {
|
||||||
var id: Int64
|
var id: Int64
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ private struct TimeZone: Hashable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TimeZone: Defaults.NativeType {
|
extension TimeZone: Defaults.NativeType {
|
||||||
/// Associated `CodableForm` to `CodableTimeZone`
|
/// Associated `CodableForm` to `CodableTimeZone`.
|
||||||
typealias CodableForm = CodableTimeZone
|
typealias CodableForm = CodableTimeZone
|
||||||
|
|
||||||
static let bridge = TimeZoneBridge()
|
static let bridge = TimeZoneBridge()
|
||||||
|
@ -37,7 +37,7 @@ private struct CodableTimeZone {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CodableTimeZone: Defaults.CodableType {
|
extension CodableTimeZone: Defaults.CodableType {
|
||||||
/// Convert from `Codable` to `Native`
|
/// Convert from `Codable` to `Native`.
|
||||||
func toNative() -> TimeZone {
|
func toNative() -> TimeZone {
|
||||||
TimeZone(id: id, name: name)
|
TimeZone(id: id, name: name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import Defaults
|
||||||
let fixtureURL = URL(string: "https://sindresorhus.com")!
|
let fixtureURL = URL(string: "https://sindresorhus.com")!
|
||||||
let fixtureFileURL = URL(string: "file://~/icon.png")!
|
let fixtureFileURL = URL(string: "file://~/icon.png")!
|
||||||
let fixtureURL2 = URL(string: "https://example.com")!
|
let fixtureURL2 = URL(string: "https://example.com")!
|
||||||
|
|
||||||
let fixtureDate = Date()
|
let fixtureDate = Date()
|
||||||
|
|
||||||
extension Defaults.Keys {
|
extension Defaults.Keys {
|
||||||
|
|
185
migration.md
185
migration.md
|
@ -1,76 +1,68 @@
|
||||||
# Migration Guide From v4 to v5
|
# Migration guide from v4 to v5
|
||||||
|
|
||||||
## Warning
|
**Warning: Test the migration thoroughly in your app. It might cause unintended data loss if you're not careful.**
|
||||||
|
|
||||||
If the migration is not success or incomplete. Edit `Defaults.Key` might cause data loss.
|
|
||||||
**Please back up your UserDefaults data before migration.**
|
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
Before v4, `Defaults` store `Codable` types as a JSON string.
|
We have improved the stored representation of some types. Some types will require migration. Previously, all `Codable` types were serialized to a JSON string and stored as a `UserDefaults` string. `Defaults` is now able to store more types using the appropriate native `UserDefaults` type.
|
||||||
After v5, `Defaults` store `Defaults.Serializable` types with `UserDefaults` native supported type.
|
|
||||||
|
- Primitive types (`Int`, `Double`, `Bool`, `String`, etc) require no changes.
|
||||||
|
- Custom types (`struct`, `enum`, etc.) must now conform to `Defaults.Serializable` (in addition to `Codable`).
|
||||||
|
- `Array`, `Set`, and `Dictionary` will need to be manually migrated with `Defaults.migrate()`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
In v4, `Defaults` stored `Codable` types as a JSON string.\
|
||||||
|
In v5, `Defaults` stores many `Codable` types as native `UserDefaults` types.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
// Before
|
// v4
|
||||||
let key = Defaults.Key<[String: Int]>("key", default: ["0": 0])
|
let key = Defaults.Key<[String: Int]>("key", default: ["0": 0])
|
||||||
|
|
||||||
UserDefaults.standard.string(forKey: "key") //=> "["0": 0]"
|
UserDefaults.standard.string(forKey: "key")
|
||||||
|
//=> "[\"0\": 0]"
|
||||||
// After v5
|
|
||||||
let key = Defaults.Key<[String: Int]>("key", default: ["0": 0])
|
|
||||||
|
|
||||||
UserDefaults.standard.dictionary(forKey: "key") //=> [0: 0]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
All types should conform to `Defaults.Serializable` in order to work with `Defaults`.
|
```swift
|
||||||
So this will require some migrations to resolve **TWO** major issues.
|
// v5
|
||||||
|
let key = Defaults.Key<[String: Int]>("key", default: ["0": 0])
|
||||||
|
|
||||||
|
UserDefaults.standard.dictionary(forKey: "key")
|
||||||
|
//=> [0: 0]
|
||||||
|
```
|
||||||
|
|
||||||
## Issues
|
## Issues
|
||||||
|
|
||||||
1. **Compiler complain that `Defaults.Key<Value>` is not conform to `Defaults.Serializable`.**
|
1. **The compiler complains that `Defaults.Key<Value>` does not conform to `Defaults.Serializable`.**
|
||||||
Since we replace `Codable` with `Defaults.Serializable`, `Key<Value>` will have to conform to `Value: Defaults.Serializable`.
|
Since we replaced `Codable` with `Defaults.Serializable`, `Key<Value>` will have to conform to `Value: Defaults.Serializable`.
|
||||||
For this situation, please follow the guide below:
|
For this situation, please follow the guides below:
|
||||||
- [From `Codable` struct in Defaults v4 to `Codable` struct in Defaults v5](#from-codable-struct-in-defaults-v4-to-codable-struct-in-defaults-v5)
|
- [From `Codable` struct in Defaults v4 to `Codable` struct in Defaults v5](#from-codable-struct-in-defaults-v4-to-codable-struct-in-defaults-v5)
|
||||||
- [From `Codable` enum in Defaults v4 to `Codable` enum in Defaults v5](#from-codable-enum-in-defaults-v4-to-codable-enum-in-defaults-v5)
|
- [From `Codable` enum in Defaults v4 to `Codable` enum in Defaults v5](#from-codable-enum-in-defaults-v4-to-codable-enum-in-defaults-v5)
|
||||||
|
|
||||||
2. **Previous value in UserDefaults is not readable. (ex. `Defaults[.array]` return `null`).**
|
2. **The previous value in `UserDefaults` is not readable. (for example, `Defaults[.array]` returns `nil`).**
|
||||||
In v5, `Defaults` reads value from `UserDefaults` as a native supported type.
|
In v5, `Defaults` reads value from `UserDefaults` as a natively supported type, but since `UserDefaults` only contains JSON string before migration for `Codable` types, `Defaults` will not be able to work with it. For this situation, `Defaults` provides the `Defaults.migrate()` method to automate the migration process.
|
||||||
But `UserDefaults` only contains JSON string before migration, `Defaults` will not be able to work with it.
|
- [From `Codable` `Array/Dictionary/Set` in Defaults v4 to native `Array/Dictionary/Set`(with natively supported elements) in Defaults v5](#from-codable-arraydictionaryset-in-defaults-v4-to-native-arraydictionarysetwith-native-supported-elements-in-defaults-v5)
|
||||||
For this situation, `Defaults` provides `Defaults.migrate` method to automate the migration process.
|
- [From `Codable` `Array/Dictionary/Set` in Defaults v4 to native `Array/Dictionary/Set` (with codable elements) in Defaults v5](#from-codable-arraydictionaryset-in-defaults-v4-to-native-arraydictionarysetwith-codable-elements-in-defaults-v5)
|
||||||
- [From `Codable Array/Dictionary/Set` in Defaults v4 to `Native Array/Dictionary/Set`(With Native Supported Elements) in Defaults v5](#from-codable-arraydictionaryset-in-defaults-v4-to-native-arraydictionarysetwith-native-supported-elements-in-defaults-v5)
|
|
||||||
- [From `Codable Array/Dictionary/Set` in Defaults v4 to `Native Array/Dictionary/Set`(With Codable Elements) in Defaults v5](#from-codable-arraydictionaryset-in-defaults-v4-to-native-arraydictionarysetwith-codable-elements-in-defaults-v5)
|
|
||||||
|
|
||||||
**Caution:**
|
|
||||||
- This is a breaking change, there is no way to convert it back to `Codable Array/Dictionary/Set` so far.
|
|
||||||
|
|
||||||
- **Optional migration**
|
|
||||||
`Defaults` also provide a migration guide to let users convert them `Codable` things into the UserDefaults native supported type, but it is optional.
|
|
||||||
- [From `Codable` enum in Defaults v4 to `RawRepresentable` in Defaults v5](#from-codable-enum-in-defaults-v4-to-rawrepresentable-in-defaults-v5-optional)
|
|
||||||
- [From `Codable` struct in Defaults v4 to `Dictionary` in Defaults v5](#from-codable-struct-in-defaults-v4-to-dictionary-in-defaults-v5-optional)
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
We recommend user doing some tests after migration.
|
We recommend doing some manual testing after migrating.
|
||||||
The most critical issue is the second one (Previous value in UserDefaults is not readable).
|
|
||||||
After migration, there is a need to make sure user can get the same value as before.
|
|
||||||
You can try to test it manually or making a test file to test it.
|
|
||||||
|
|
||||||
Here is the guide for making a migration test:
|
For example, let's say you are trying to migrate an array of `Codable` string to a native array.
|
||||||
For example you are trying to migrate a `Codable String` array to native array.
|
|
||||||
|
|
||||||
1. Get previous value in UserDefaults (using `defaults` command or whatever you want).
|
1. Get the previous value in `UserDefaults` (using `defaults` command or whatever you want).
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let string = "[\"a\",\"b\",\"c\"]"
|
let string = "[\"a\",\"b\",\"c\"]"
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Insert the value above into UserDefaults.
|
2. Insert the above value into `UserDefaults`.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
UserDefaults.standard.set(string, forKey: "testKey")
|
UserDefaults.standard.set(string, forKey: "testKey")
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Call `Defaults.migrate` and then using `Defaults` to get its value
|
3. Call `Defaults.migrate()` and then use `Defaults` to get its value.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let key = Defaults.Key<[String]>("testKey", default: [])
|
let key = Defaults.Key<[String]>("testKey", default: [])
|
||||||
|
@ -79,15 +71,15 @@ Defaults.migrate(key, to: .v5)
|
||||||
Defaults[key] //=> [a, b, c]
|
Defaults[key] //=> [a, b, c]
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
## Migrations
|
||||||
|
|
||||||
### From `Codable` struct in Defaults v4 to `Codable` struct in Defaults v5
|
### From `Codable` struct in Defaults v4 to `Codable` struct in Defaults v5
|
||||||
|
|
||||||
Before v4, `struct` have to conform to `Codable` to store it as a JSON string.
|
In v4, `struct` had to conform to `Codable` to store it as a JSON string.
|
||||||
|
|
||||||
After v5, `struct` have to conform to `Defaults.Serializable & Codable` to store it as a JSON string.
|
In v5, `struct` has to conform to `Codable` and `Defaults.Serializable` to store it as a JSON string.
|
||||||
|
|
||||||
#### Before migration, your code should be like this
|
#### Before migration
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private struct TimeZone: Codable {
|
private struct TimeZone: Codable {
|
||||||
|
@ -102,10 +94,10 @@ extension Defaults.Keys {
|
||||||
|
|
||||||
#### Migration steps
|
#### Migration steps
|
||||||
|
|
||||||
1. Let `TimeZone` conform to `Defaults.Serializable`.
|
1. Make `TimeZone` conform to `Defaults.Serializable`.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private struct TimeZone: Defaults.Serializable, Codable {
|
private struct TimeZone: Codable, Defaults.Serializable {
|
||||||
var id: String
|
var id: String
|
||||||
var name: String
|
var name: String
|
||||||
}
|
}
|
||||||
|
@ -113,15 +105,13 @@ private struct TimeZone: Defaults.Serializable, Codable {
|
||||||
|
|
||||||
2. Now `Defaults[.timezone]` should be readable.
|
2. Now `Defaults[.timezone]` should be readable.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### From `Codable` enum in Defaults v4 to `Codable` enum in Defaults v5
|
### From `Codable` enum in Defaults v4 to `Codable` enum in Defaults v5
|
||||||
|
|
||||||
Before v4, `enum` have to conform to `Codable` to store it as a JSON string.
|
In v4, `enum` had to conform to `Codable` to store it as a JSON string.
|
||||||
|
|
||||||
After v5, struct have to conform to `Defaults.Serializable & Codable` to store it as a JSON string.
|
In v5, `enum` has to conform to `Codable` and `Defaults.Serializable` to store it as a JSON string.
|
||||||
|
|
||||||
#### Before migration, your code should be like this
|
#### Before migration
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private enum Period: String, Codable {
|
private enum Period: String, Codable {
|
||||||
|
@ -137,7 +127,7 @@ extension Defaults.Keys {
|
||||||
|
|
||||||
#### Migration steps
|
#### Migration steps
|
||||||
|
|
||||||
1. Let `Period` conform to `Defaults.Serializable`.
|
1. Make `Period` conform to `Defaults.Serializable`.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private enum Period: String, Defaults.Serializable, Codable {
|
private enum Period: String, Defaults.Serializable, Codable {
|
||||||
|
@ -149,15 +139,13 @@ private enum Period: String, Defaults.Serializable, Codable {
|
||||||
|
|
||||||
2. Now `Defaults[.period]` should be readable.
|
2. Now `Defaults[.period]` should be readable.
|
||||||
|
|
||||||
---
|
### From `Codable` `Array/Dictionary/Set` in Defaults v4 to native `Array/Dictionary/Set` (with natively supported elements) in Defaults v5
|
||||||
|
|
||||||
### From `Codable Array/Dictionary/Set` in Defaults v4 to `Native Array/Dictionary/Set`(With Native Supported Elements) in Defaults v5
|
In v4, `Defaults` stored array/dictionary as a JSON string: `["a", "b", "c"]`.
|
||||||
|
|
||||||
Before v4, `Defaults` will store array/dictionary as a JSON string(`["a", "b", "c"]`).
|
In v5, `Defaults` stores it as a native array/dictionary with natively supported elements: `[a, b, c]`.
|
||||||
|
|
||||||
After v5, `Defaults` will store it as a native array/dictionary with native supported elements(`[a, b, c]` ).
|
#### Before migration
|
||||||
|
|
||||||
#### Before migration, your code should be like this
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
extension Defaults.Keys {
|
extension Defaults.Keys {
|
||||||
|
@ -170,25 +158,24 @@ extension Defaults.Keys {
|
||||||
|
|
||||||
#### Migration steps
|
#### Migration steps
|
||||||
|
|
||||||
1. **Call `Defaults.migration(.arrayString, to: .v5)`, `Defaults.migration(.setString, to: .v5)`, `Defaults.migration(.dictionaryStringInt, to: .v5)`, `Defaults.migration(.dictionaryStringIntInArray, to: .v5)`.**
|
1. **Call `Defaults.migrate(.arrayString, to: .v5)`, `Defaults.migrate(.setString, to: .v5)`, `Defaults.migrate(.dictionaryStringInt, to: .v5)`, `Defaults.migrate(.dictionaryStringIntInArray, to: .v5)`.**
|
||||||
2. Now `Defaults[.arrayString]`, `Defaults.[.setString]`, `Defaults[.dictionaryStringInt]`, `Defaults[.dictionaryStringIntInArray]` should be readable.
|
2. Now `Defaults[.arrayString]`, `Defaults.[.setString]`, `Defaults[.dictionaryStringInt]`, `Defaults[.dictionaryStringIntInArray]` should be readable.
|
||||||
|
|
||||||
---
|
### From `Codable` `Array/Dictionary/Set` in Defaults v4 to native `Array/Dictionary/Set` (with `Codable` elements) in Defaults v5
|
||||||
|
|
||||||
### From `Codable Array/Dictionary/Set` in Defaults v4 to `Native Array/Dictionary/Set`(With Codable Elements) in Defaults v5
|
In v4, `Defaults` would store array/dictionary as a single JSON string: `"{ "id": "0", "name": "Asia/Taipei" }"`, `"["10 Minutes", "30 Minutes"]"`.
|
||||||
|
|
||||||
Before v4, `Defaults` will store array/dictionary as a JSON string(`"{ "id": "0", "name": "Asia/Taipei" }"`, `"["10 Minutes", "30 Minutes"]"`).
|
In v5, `Defaults` will store it as a native array/dictionary with `Codable` elements: `{ id: 0, name: Asia/Taipei }`, `[10 Minutes, 30 Minutes]`.
|
||||||
|
|
||||||
After v5, `Defaults` will store it as a native array/dictionary with codable elements(`{ id: 0, name: Asia/Taipei }`, `[10 Minutes, 30 Minutes]`).
|
#### Before migration
|
||||||
|
|
||||||
#### Before migration, your code should be like this
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private struct TimeZone: Codable, Hashable {
|
private struct TimeZone: Hashable, Codable {
|
||||||
var id: String
|
var id: String
|
||||||
var name: String
|
var name: String
|
||||||
}
|
}
|
||||||
private enum Period: String, Codable, Hashable {
|
|
||||||
|
private enum Period: String, Hashable, Codable {
|
||||||
case tenMinutes = "10 Minutes"
|
case tenMinutes = "10 Minutes"
|
||||||
case halfHour = "30 Minutes"
|
case halfHour = "30 Minutes"
|
||||||
case oneHour = "1 Hour"
|
case oneHour = "1 Hour"
|
||||||
|
@ -206,33 +193,35 @@ extension Defaults.Keys {
|
||||||
|
|
||||||
#### Migration steps
|
#### Migration steps
|
||||||
|
|
||||||
1. Let `TimeZone` and `Period` conform to `Defaults.Serializable`
|
1. Make `TimeZone` and `Period` conform to `Defaults.Serializable`.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private struct TimeZone: Defaults.Serializable, Codable, Hashable {
|
private struct TimeZone: Hashable, Codable, Defaults.Serializable {
|
||||||
var id: String
|
var id: String
|
||||||
var name: String
|
var name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum Period: String, Defaults.Serializable, Codable, Hashable {
|
private enum Period: String, Hashable, Codable, Defaults.Serializable {
|
||||||
case tenMinutes = "10 Minutes"
|
case tenMinutes = "10 Minutes"
|
||||||
case halfHour = "30 Minutes"
|
case halfHour = "30 Minutes"
|
||||||
case oneHour = "1 Hour"
|
case oneHour = "1 Hour"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Call `Defaults.migration(.arrayTimezone, to: .v5)`, `Defaults.migration(.setTimezone, to: .v5)`, `Defaults.migration(.dictionaryTimezone, to: .v5)`, `Defaults.migration(.arrayPeriod, to: .v5)`, `Defaults.migration(.setPeriod, to: .v5)` , `Defaults.migration(.dictionaryPeriod, to: .v5)`.**
|
2. **Call `Defaults.migrate(.arrayTimezone, to: .v5)`, `Defaults.migrate(.setTimezone, to: .v5)`, `Defaults.migrate(.dictionaryTimezone, to: .v5)`, `Defaults.migrate(.arrayPeriod, to: .v5)`, `Defaults.migrate(.setPeriod, to: .v5)` , `Defaults.migrate(.dictionaryPeriod, to: .v5)`.**
|
||||||
3. Now `Defaults[.arrayTimezone]`, `Defaults[.setTimezone]`, `Defaults[.dictionaryTimezone]`, `Defaults[.arrayPeriod]`, `Defaults[.setPeriod]` , `Defaults[.dictionaryPeriod]` should be readable.
|
3. Now `Defaults[.arrayTimezone]`, `Defaults[.setTimezone]`, `Defaults[.dictionaryTimezone]`, `Defaults[.arrayPeriod]`, `Defaults[.setPeriod]` , `Defaults[.dictionaryPeriod]` should be readable.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### From `Codable` enum in Defaults v4 to `RawRepresentable` in Defaults v5 (Optional)
|
## Optional migrations
|
||||||
|
|
||||||
Before v4, `Defaults` will store `enum` as a JSON string(`"10 Minutes"`).
|
### From `Codable` enum in Defaults v4 to `RawRepresentable` enum in Defaults v5 *(Optional)*
|
||||||
|
|
||||||
After v5, `Defaults` will store `enum` as a `RawRepresentable`(`10 Minutes`).
|
In v4, `Defaults` will store `enum` as a JSON string: `"10 Minutes"`.
|
||||||
|
|
||||||
#### Before migration, your code should be like this
|
In v5, `Defaults` can store `enum` as a native string: `10 Minutes`.
|
||||||
|
|
||||||
|
#### Before migration
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private enum Period: String, Codable {
|
private enum Period: String, Codable {
|
||||||
|
@ -248,7 +237,7 @@ extension Defaults.Keys {
|
||||||
|
|
||||||
#### Migration steps
|
#### Migration steps
|
||||||
|
|
||||||
1. Create an enum call `CodablePeriod` and create an extension of it. Let it conform to `Defaults.CodableType` and associated `NativeForm` to `Period`.
|
1. Create another enum called `CodablePeriod` and create an extension of it. Make the extension conform to `Defaults.CodableType` and its associated type `NativeForm` to `Period`.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private enum CodablePeriod: String {
|
private enum CodablePeriod: String {
|
||||||
|
@ -262,7 +251,7 @@ extension CodablePeriod: Defaults.CodableType {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Remove `Codable`. So `Period` can be stored natively.
|
2. Remove `Codable` conformance so `Period` can be stored natively.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private enum Period: String {
|
private enum Period: String {
|
||||||
|
@ -272,7 +261,7 @@ private enum Period: String {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Create an extension of `Period`, let it conform to `Defaults.NativeType` and its `CodableForm` should be `CodablePeriod`.
|
3. Create an extension of `Period` that conforms to `Defaults.NativeType`. Its `CodableForm` should be `CodablePeriod`.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
extension Period: Defaults.NativeType {
|
extension Period: Defaults.NativeType {
|
||||||
|
@ -280,12 +269,10 @@ extension Period: Defaults.NativeType {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Call `Defaults.migration(.period)`**
|
4. **Call `Defaults.migrate(.period)`**
|
||||||
5. Now `Defaults[.period]` should be readable.
|
5. Now `Defaults[.period]` should be readable.
|
||||||
|
|
||||||
* hints: You can also implement `toNative` function at `Defaults.CodableType` in your own way.
|
You can also instead implement the `toNative` function in `Defaults.CodableType` for flexibility:
|
||||||
|
|
||||||
For example
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
extension CodablePeriod: Defaults.CodableType {
|
extension CodablePeriod: Defaults.CodableType {
|
||||||
|
@ -304,14 +291,11 @@ extension CodablePeriod: Defaults.CodableType {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### From `Codable` struct in Defaults v4 to `Dictionary` in Defaults v5 *(Optional)*
|
||||||
|
|
||||||
---
|
This happens when you have a struct which is stored as a `Codable` JSON string before, but now you want it to be stored as a native `UserDefaults` dictionary.
|
||||||
|
|
||||||
### From `Codable` struct in Defaults v4 to `Dictionary` in Defaults v5 (Optional)
|
#### Before migration
|
||||||
|
|
||||||
This happens when you have a struct which is stored as a codable JSON string before, but now you want it to be stored as a native UserDefaults dictionary.
|
|
||||||
|
|
||||||
#### Before migration, your code should be like this
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private struct TimeZone: Codable {
|
private struct TimeZone: Codable {
|
||||||
|
@ -329,7 +313,7 @@ extension Defaults.Keys {
|
||||||
|
|
||||||
#### Migration steps
|
#### Migration steps
|
||||||
|
|
||||||
1. Create a `TimeZoneBridge` which conform to `Defaults.Bridge` and its `Value` is TimeZone, `Serializable` is `[String: String]`.
|
1. Create a `TimeZoneBridge` which conforms to `Defaults.Bridge` and its `Value` is `TimeZone` and `Serializable` is `[String: String]`.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private struct TimeZoneBridge: Defaults.Bridge {
|
private struct TimeZoneBridge: Defaults.Bridge {
|
||||||
|
@ -341,7 +325,10 @@ private struct TimeZoneBridge: Defaults.Bridge {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return ["id": value.id, "name": value.name]
|
return [
|
||||||
|
"id": value.id,
|
||||||
|
"name": value.name
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
func deserialize(_ object: Serializable?) -> TimeZone? {
|
func deserialize(_ object: Serializable?) -> TimeZone? {
|
||||||
|
@ -353,12 +340,15 @@ private struct TimeZoneBridge: Defaults.Bridge {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return TimeZone(id: id, name: name)
|
return TimeZone(
|
||||||
|
id: id,
|
||||||
|
name: name
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Create an extension of `TimeZone`, let it conform to `Defaults.NativeType` and its static bridge is `TimeZoneBridge`(Compiler will complain that `TimeZone` is not conform to `Defaults.NativeType`, will resolve it later).
|
2. Create an extension of `TimeZone` that conforms to `Defaults.NativeType` and its static bridge is `TimeZoneBridge`. The compiler will complain that `TimeZone` does not conform to `Defaults.NativeType`. We will resolve that later.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private struct TimeZone: Hashable {
|
private struct TimeZone: Hashable {
|
||||||
|
@ -371,7 +361,7 @@ extension TimeZone: Defaults.NativeType {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Create an extension of `CodableTimeZone` and let it conform to `Defaults.CodableType`
|
3. Create an extension of `CodableTimeZone` that conforms to `Defaults.CodableType`.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private struct CodableTimeZone {
|
private struct CodableTimeZone {
|
||||||
|
@ -380,7 +370,7 @@ private struct CodableTimeZone {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CodableTimeZone: Defaults.CodableType {
|
extension CodableTimeZone: Defaults.CodableType {
|
||||||
/// Convert from `Codable` to `Native`
|
/// Convert from `Codable` to native type.
|
||||||
func toNative() -> TimeZone {
|
func toNative() -> TimeZone {
|
||||||
TimeZone(id: id, name: name)
|
TimeZone(id: id, name: name)
|
||||||
}
|
}
|
||||||
|
@ -391,14 +381,13 @@ extension CodableTimeZone: Defaults.CodableType {
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
extension TimeZone: Defaults.NativeType {
|
extension TimeZone: Defaults.NativeType {
|
||||||
/// Associated `CodableForm` to `CodableTimeZone`
|
|
||||||
typealias CodableForm = CodableTimeZone
|
typealias CodableForm = CodableTimeZone
|
||||||
|
|
||||||
static let bridge = TimeZoneBridge()
|
static let bridge = TimeZoneBridge()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
5. **Call `Defaults.migration(.timezone, to: .v5)`, `Defaults.migration(.arrayTimezone, to: .v5)`, `Defaults.migration(.setTimezone, to: .v5)`, `Defaults.migration(.dictionaryTimezone, to: .v5)`**.
|
5. **Call `Defaults.migrate(.timezone, to: .v5)`, `Defaults.migrate(.arrayTimezone, to: .v5)`, `Defaults.migrate(.setTimezone, to: .v5)`, `Defaults.migrate(.dictionaryTimezone, to: .v5)`**.
|
||||||
6. Now `Defaults[.timezone]`, `Defaults[.arrayTimezone]` , `Defaults[.setTimezone]`, `Defaults[.dictionaryTimezone]` should be readable.
|
6. Now `Defaults[.timezone]`, `Defaults[.arrayTimezone]` , `Defaults[.setTimezone]`, `Defaults[.dictionaryTimezone]` should be readable.
|
||||||
|
|
||||||
**See [DefaultsMigrationTests.swift](./Tests/DefaultsTests/DefaultsMigrationTests.swift) for more example.**
|
**See [DefaultsMigrationTests.swift](./Tests/DefaultsTests/DefaultsMigrationTests.swift) for more example.**
|
485
readme.md
485
readme.md
|
@ -52,26 +52,25 @@ pod 'Defaults'
|
||||||
|
|
||||||
## Support types
|
## Support types
|
||||||
|
|
||||||
| Single Value |
|
- `Int(8/16/32/64)`
|
||||||
|:------------------:|
|
- `UInt(8/16/32/64)`
|
||||||
| `Int(8/16/32/64)` |
|
- `Double`
|
||||||
| `UInt(8/16/32/64)` |
|
- `CGFloat`
|
||||||
| `Double` |
|
- `Float`
|
||||||
| `Float` |
|
- `String`
|
||||||
| `String` |
|
- `Bool`
|
||||||
| `CGFloat` |
|
- `Date`
|
||||||
| `Bool` |
|
- `Data`
|
||||||
| `Date` |
|
- `URL`
|
||||||
| `Data` |
|
- `NSColor` (macOS)
|
||||||
| `URL` |
|
- `UIColor` (iOS)
|
||||||
| `NSColor` (macOS) |
|
- `Codable`
|
||||||
| `UIColor` (iOS) |
|
|
||||||
| `Codable` |
|
|
||||||
|
|
||||||
The list above only show the type that does not need further more configuration.
|
Defaults also support the above types wrapped in `Array`, `Set`, `Dictionary`, and even wrapped in nested types. For example, `[[String: Set<[String: Int]>]]`.
|
||||||
We also support them wrapped in `Array`, `Set`, `Dictionary` even wrapped in nested type. ex. `[[String: Set<[String: Int]>]]`.
|
|
||||||
For more types, see [Enum Example](#enum-example), [Codable Example](#codable-example) or [Advanced Usage](#advanced-usage).
|
For more types, see the [enum example](#enum-example), [`Codable` example](#codable-example), or [advanced Usage](#advanced-usage). For more examples, see [Tests/DefaultsTests](./Tests/DefaultsTests).
|
||||||
For more examples, see [Tests/DefaultsTests](./Tests/DefaultsTests).
|
|
||||||
|
You can easily add support for any custom type.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -137,7 +136,9 @@ Defaults[.defaultDuration].rawValue
|
||||||
//=> "1 Hour"
|
//=> "1 Hour"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Codable Example
|
*(This works as long as the raw value of the enum is any of the supported types)*
|
||||||
|
|
||||||
|
### Codable example
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
struct User: Codable, Defaults.Serializable {
|
struct User: Codable, Defaults.Serializable {
|
||||||
|
@ -339,217 +340,6 @@ print(UserDefaults.standard.bool(forKey: Defaults.Keys.isUnicornMode.name))
|
||||||
//=> true
|
//=> true
|
||||||
```
|
```
|
||||||
|
|
||||||
## Advanced Usage
|
|
||||||
|
|
||||||
### Serialization of custom types
|
|
||||||
|
|
||||||
Although `Defaults` already support many types internal, there might have some situations where you want to use your own type.
|
|
||||||
The guide below will show you how to make your own custom type works with `Defaults`.
|
|
||||||
|
|
||||||
1. Create your own custom type.
|
|
||||||
|
|
||||||
```swift
|
|
||||||
struct User {
|
|
||||||
let name: String
|
|
||||||
let age: String
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Create a bridge which protocol conforms to `Defaults.Bridge`.
|
|
||||||
|
|
||||||
```swift
|
|
||||||
struct UserBridge: Defaults.Bridge {
|
|
||||||
typealias Value = User
|
|
||||||
typealias Serializable = [String: String]
|
|
||||||
|
|
||||||
public func serialize(_ value: Value?) -> Serializable? {
|
|
||||||
guard let value = value else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return ["name": value.name, "age": value.age]
|
|
||||||
}
|
|
||||||
|
|
||||||
public func deserialize(_ object: Serializable?) -> Value? {
|
|
||||||
guard
|
|
||||||
let object = object,
|
|
||||||
let name = object["name"],
|
|
||||||
let age = object["age"]
|
|
||||||
else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return User(name: name, age: age)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Create an extension of `User`, let its protocol conforms to `Defaults.Serializable` and its static bridge should be the bridge we created above.
|
|
||||||
|
|
||||||
```swift
|
|
||||||
struct User {
|
|
||||||
let name: String
|
|
||||||
let age: String
|
|
||||||
}
|
|
||||||
|
|
||||||
extension User: Defaults.Serializable {
|
|
||||||
static let bridge = UserBridge()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Create some keys and enjoy it.
|
|
||||||
|
|
||||||
```swift
|
|
||||||
extension Defaults.Keys {
|
|
||||||
static let user = Defaults.Key<User>("user", default: User(name: "Hello", age: "24"))
|
|
||||||
static let arrayUser = Defaults.Key<[User]>("arrayUser", default: [User(name: "Hello", age: "24")])
|
|
||||||
static let setUser = Defaults.Key<Set<User>>("user", default: Set([User(name: "Hello", age: "24")]))
|
|
||||||
static let dictionaryUser = Defaults.Key<[String: User]>("dictionaryUser", default: ["user": User(name: "Hello", age: "24")])
|
|
||||||
}
|
|
||||||
|
|
||||||
Defaults[.user].name //=> "Hello"
|
|
||||||
Defaults[.arrayUser][0].name //=> "Hello"
|
|
||||||
Defaults[.setUser].first?.name //=> "Hello"
|
|
||||||
Defaults[.dictionaryUser]["user"]?.name //=> "Hello"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Serialization of Collection
|
|
||||||
|
|
||||||
1. Create your Collection and its element should conforms to `Defaults.Serializable`.
|
|
||||||
|
|
||||||
```swift
|
|
||||||
struct Bag<Element: Defaults.Serializable>: Collection {
|
|
||||||
var items: [Element]
|
|
||||||
|
|
||||||
var startIndex: Int {
|
|
||||||
items.startIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
var endIndex: Int {
|
|
||||||
items.endIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
mutating func insert(element: Element, at: Int) {
|
|
||||||
items.insert(element, at: at)
|
|
||||||
}
|
|
||||||
|
|
||||||
func index(after index: Int) -> Int {
|
|
||||||
items.index(after: index)
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript(position: Int) -> Element {
|
|
||||||
items[position]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Create an extension of `Bag`. let it conforms to `Defaults.CollectionSerializable`
|
|
||||||
|
|
||||||
```swift
|
|
||||||
extension Bag: Defaults.CollectionSerializable {
|
|
||||||
init(_ elements: [Element]) {
|
|
||||||
self.items = elements
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Create some keys and enjoy it.
|
|
||||||
|
|
||||||
```swift
|
|
||||||
extension Defaults.Keys {
|
|
||||||
static let stringBag = Key<Bag<String>>("stringBag", default: Bag(["Hello", "World!"]))
|
|
||||||
}
|
|
||||||
|
|
||||||
Defaults[.stringBag][0] //=> "Hello"
|
|
||||||
Defaults[.stringBag][1] //=> "World!"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Serialization of SetAlgebra
|
|
||||||
|
|
||||||
1. Create your SetAlgebra and its element should conforms to `Defaults.Serializable & Hashable`
|
|
||||||
|
|
||||||
```swift
|
|
||||||
struct SetBag<Element: Defaults.Serializable & Hashable>: SetAlgebra {
|
|
||||||
var store = Set<Element>()
|
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
init(_ store: Set<Element>) {
|
|
||||||
self.store = store
|
|
||||||
}
|
|
||||||
|
|
||||||
func contains(_ member: Element) -> Bool {
|
|
||||||
store.contains(member)
|
|
||||||
}
|
|
||||||
|
|
||||||
func union(_ other: SetBag) -> SetBag {
|
|
||||||
SetBag(store.union(other.store))
|
|
||||||
}
|
|
||||||
|
|
||||||
func intersection(_ other: SetBag)
|
|
||||||
-> SetBag {
|
|
||||||
var setBag = SetBag()
|
|
||||||
setBag.store = store.intersection(other.store)
|
|
||||||
return setBag
|
|
||||||
}
|
|
||||||
|
|
||||||
func symmetricDifference(_ other: SetBag)
|
|
||||||
-> SetBag {
|
|
||||||
var setBag = SetBag()
|
|
||||||
setBag.store = store.symmetricDifference(other.store)
|
|
||||||
return setBag
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
mutating func insert(_ newMember: Element)
|
|
||||||
-> (inserted: Bool, memberAfterInsert: Element) {
|
|
||||||
store.insert(newMember)
|
|
||||||
}
|
|
||||||
|
|
||||||
mutating func remove(_ member: Element) -> Element? {
|
|
||||||
store.remove(member)
|
|
||||||
}
|
|
||||||
|
|
||||||
mutating func update(with newMember: Element) -> Element? {
|
|
||||||
store.update(with: newMember)
|
|
||||||
}
|
|
||||||
|
|
||||||
mutating func formUnion(_ other: SetBag) {
|
|
||||||
store.formUnion(other.store)
|
|
||||||
}
|
|
||||||
|
|
||||||
mutating func formSymmetricDifference(_ other: SetBag) {
|
|
||||||
store.formSymmetricDifference(other.store)
|
|
||||||
}
|
|
||||||
|
|
||||||
mutating func formIntersection(_ other: SetBag) {
|
|
||||||
store.formIntersection(other.store)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Create an extension of `SetBag`. Let it conforms to `Defaults.SetAlgebraSerializable`
|
|
||||||
|
|
||||||
```swift
|
|
||||||
extension SetBag: Defaults.SetAlgebraSerializable {
|
|
||||||
func toArray() -> [Element] {
|
|
||||||
Array(store)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Create some keys and enjoy it.
|
|
||||||
|
|
||||||
```swift
|
|
||||||
extension Defaults.Keys {
|
|
||||||
static let stringSet = Key<SetBag<String>>("stringSet", default: SetBag(["Hello", "World!"]))
|
|
||||||
}
|
|
||||||
|
|
||||||
Defaults[.stringSet].contains("Hello") //=> true
|
|
||||||
Defaults[.stringSet].contains("World!") //=> true
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### `Defaults`
|
### `Defaults`
|
||||||
|
@ -586,9 +376,9 @@ public protocol DefaultsSerializable {
|
||||||
|
|
||||||
Type: `protocol`
|
Type: `protocol`
|
||||||
|
|
||||||
All types conform to this protocol will be able to work with `Defaults`.
|
Types that conform to this protocol can be used with `Defaults`.
|
||||||
|
|
||||||
It should have a static variable `bridge` which protocol should conform to `Defaults.Bridge`.
|
The type should have a static variable `bridge` which should reference an instance of a type that conforms to `Defaults.Bridge`.
|
||||||
|
|
||||||
#### `Defaults.Bridge`
|
#### `Defaults.Bridge`
|
||||||
|
|
||||||
|
@ -596,6 +386,7 @@ It should have a static variable `bridge` which protocol should conform to `Defa
|
||||||
public protocol DefaultsBridge {
|
public protocol DefaultsBridge {
|
||||||
associatedtype Value
|
associatedtype Value
|
||||||
associatedtype Serializable
|
associatedtype Serializable
|
||||||
|
|
||||||
func serialize(_ value: Value?) -> Serializable?
|
func serialize(_ value: Value?) -> Serializable?
|
||||||
func deserialize(_ object: Serializable?) -> Value?
|
func deserialize(_ object: Serializable?) -> Value?
|
||||||
}
|
}
|
||||||
|
@ -603,17 +394,14 @@ public protocol DefaultsBridge {
|
||||||
|
|
||||||
Type: `protocol`
|
Type: `protocol`
|
||||||
|
|
||||||
A Bridge can do serialization and de-serialization.
|
A `Bridge` is responsible for serialization and deserialization.
|
||||||
|
|
||||||
Have two associate types `Value` and `Serializable`.
|
It has two associated types `Value` and `Serializable`.
|
||||||
|
|
||||||
`Value` is the type user want to use it.
|
- `Value`: The type you want to use.
|
||||||
|
- `Serializable`: The type stored in `UserDefaults`.
|
||||||
`Serializable` is the type stored in `UserDefaults`.
|
- `serialize`: Executed before storing to the `UserDefaults` .
|
||||||
|
- `deserialize`: Executed after retrieving its value from the `UserDefaults`.
|
||||||
`serialize` will be executed before storing to the `UserDefaults` .
|
|
||||||
|
|
||||||
`deserialize` will be executed after retrieving its value from the `UserDefaults`.
|
|
||||||
|
|
||||||
#### `Defaults.reset(keys…)`
|
#### `Defaults.reset(keys…)`
|
||||||
|
|
||||||
|
@ -621,8 +409,6 @@ Type: `func`
|
||||||
|
|
||||||
Reset the given keys back to their default values.
|
Reset the given keys back to their default values.
|
||||||
|
|
||||||
You can specify up to 10 keys. If you need to specify more, call this method multiple times.
|
|
||||||
|
|
||||||
You can also specify string keys, which can be useful if you need to store some keys in a collection, as it's not possible to store `Defaults.Key` in a collection because it's generic.
|
You can also specify string keys, which can be useful if you need to store some keys in a collection, as it's not possible to store `Defaults.Key` in a collection because it's generic.
|
||||||
|
|
||||||
#### `Defaults.observe`
|
#### `Defaults.observe`
|
||||||
|
@ -740,11 +526,9 @@ Type: `func`
|
||||||
|
|
||||||
Migrate the given keys to the specific version.
|
Migrate the given keys to the specific version.
|
||||||
|
|
||||||
You can specify up to 10 keys. If you need to specify more, call this method multiple times.
|
|
||||||
|
|
||||||
### `@Default(_ key:)`
|
### `@Default(_ key:)`
|
||||||
|
|
||||||
Get/set a `Defaults` item and also have the view be updated when the value changes.
|
Get/set a `Defaults` item and also have the SwiftUI view be updated when the value changes.
|
||||||
|
|
||||||
### Advanced
|
### Advanced
|
||||||
|
|
||||||
|
@ -776,6 +560,213 @@ A `SetAlgebra` which can store into the native `UserDefaults`.
|
||||||
|
|
||||||
It should have a function `func toArray() -> [Element]` to let `Defaults` do the serialization.
|
It should have a function `func toArray() -> [Element]` to let `Defaults` do the serialization.
|
||||||
|
|
||||||
|
## Advanced usage
|
||||||
|
|
||||||
|
### Custom types
|
||||||
|
|
||||||
|
Although `Defaults` already has built-in support for many types, you might need to be able to use your own custom type. The below guide will show you how to make your own custom type work with `Defaults`.
|
||||||
|
|
||||||
|
1. Create your own custom type.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
struct User {
|
||||||
|
let name: String
|
||||||
|
let age: String
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create a bridge that conforms to `Defaults.Bridge`, which is responsible for handling serialization and deserialization.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
struct UserBridge: Defaults.Bridge {
|
||||||
|
typealias Value = User
|
||||||
|
typealias Serializable = [String: String]
|
||||||
|
|
||||||
|
public func serialize(_ value: Value?) -> Serializable? {
|
||||||
|
guard let value = value else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
"name": value.name,
|
||||||
|
"age": value.age
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func deserialize(_ object: Serializable?) -> Value? {
|
||||||
|
guard
|
||||||
|
let object = object,
|
||||||
|
let name = object["name"],
|
||||||
|
let age = object["age"]
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return User(
|
||||||
|
name: name,
|
||||||
|
age: age
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Create an extension of `User` that conforms to `Defaults.Serializable`. Its static bridge should be the bridge we created above.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
struct User {
|
||||||
|
let name: String
|
||||||
|
let age: String
|
||||||
|
}
|
||||||
|
|
||||||
|
extension User: Defaults.Serializable {
|
||||||
|
static let bridge = UserBridge()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Create some keys and enjoy it.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
extension Defaults.Keys {
|
||||||
|
static let user = Defaults.Key<User>("user", default: User(name: "Hello", age: "24"))
|
||||||
|
static let arrayUser = Defaults.Key<[User]>("arrayUser", default: [User(name: "Hello", age: "24")])
|
||||||
|
static let setUser = Defaults.Key<Set<User>>("user", default: Set([User(name: "Hello", age: "24")]))
|
||||||
|
static let dictionaryUser = Defaults.Key<[String: User]>("dictionaryUser", default: ["user": User(name: "Hello", age: "24")])
|
||||||
|
}
|
||||||
|
|
||||||
|
Defaults[.user].name //=> "Hello"
|
||||||
|
Defaults[.arrayUser][0].name //=> "Hello"
|
||||||
|
Defaults[.setUser].first?.name //=> "Hello"
|
||||||
|
Defaults[.dictionaryUser]["user"]?.name //=> "Hello"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom `Collection` type
|
||||||
|
|
||||||
|
1. Create your `Collection` and make its elements conform to `Defaults.Serializable`.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
struct Bag<Element: Defaults.Serializable>: Collection {
|
||||||
|
var items: [Element]
|
||||||
|
|
||||||
|
var startIndex: Int { items.startIndex }
|
||||||
|
var endIndex: Int { items.endIndex }
|
||||||
|
|
||||||
|
mutating func insert(element: Element, at: Int) {
|
||||||
|
items.insert(element, at: at)
|
||||||
|
}
|
||||||
|
|
||||||
|
func index(after index: Int) -> Int {
|
||||||
|
items.index(after: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript(position: Int) -> Element {
|
||||||
|
items[position]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create an extension of `Bag` that conforms to `Defaults.CollectionSerializable`.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
extension Bag: Defaults.CollectionSerializable {
|
||||||
|
init(_ elements: [Element]) {
|
||||||
|
self.items = elements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Create some keys and enjoy it.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
extension Defaults.Keys {
|
||||||
|
static let stringBag = Key<Bag<String>>("stringBag", default: Bag(["Hello", "World!"]))
|
||||||
|
}
|
||||||
|
|
||||||
|
Defaults[.stringBag][0] //=> "Hello"
|
||||||
|
Defaults[.stringBag][1] //=> "World!"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom `SetAlgebra` type
|
||||||
|
|
||||||
|
1. Create your `SetAlgebra` and make its elements conform to `Defaults.Serializable & Hashable`
|
||||||
|
|
||||||
|
```swift
|
||||||
|
struct SetBag<Element: Defaults.Serializable & Hashable>: SetAlgebra {
|
||||||
|
var store = Set<Element>()
|
||||||
|
|
||||||
|
init() {}
|
||||||
|
|
||||||
|
init(_ store: Set<Element>) {
|
||||||
|
self.store = store
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(_ member: Element) -> Bool {
|
||||||
|
store.contains(member)
|
||||||
|
}
|
||||||
|
|
||||||
|
func union(_ other: SetBag) -> SetBag {
|
||||||
|
SetBag(store.union(other.store))
|
||||||
|
}
|
||||||
|
|
||||||
|
func intersection(_ other: SetBag) -> SetBag {
|
||||||
|
var setBag = SetBag()
|
||||||
|
setBag.store = store.intersection(other.store)
|
||||||
|
return setBag
|
||||||
|
}
|
||||||
|
|
||||||
|
func symmetricDifference(_ other: SetBag) -> SetBag {
|
||||||
|
var setBag = SetBag()
|
||||||
|
setBag.store = store.symmetricDifference(other.store)
|
||||||
|
return setBag
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element) {
|
||||||
|
store.insert(newMember)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func remove(_ member: Element) -> Element? {
|
||||||
|
store.remove(member)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func update(with newMember: Element) -> Element? {
|
||||||
|
store.update(with: newMember)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func formUnion(_ other: SetBag) {
|
||||||
|
store.formUnion(other.store)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func formSymmetricDifference(_ other: SetBag) {
|
||||||
|
store.formSymmetricDifference(other.store)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func formIntersection(_ other: SetBag) {
|
||||||
|
store.formIntersection(other.store)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create an extension of `SetBag` that conforms to `Defaults.SetAlgebraSerializable`
|
||||||
|
|
||||||
|
```swift
|
||||||
|
extension SetBag: Defaults.SetAlgebraSerializable {
|
||||||
|
func toArray() -> [Element] {
|
||||||
|
Array(store)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Create some keys and enjoy it.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
extension Defaults.Keys {
|
||||||
|
static let stringSet = Key<SetBag<String>>("stringSet", default: SetBag(["Hello", "World!"]))
|
||||||
|
}
|
||||||
|
|
||||||
|
Defaults[.stringSet].contains("Hello") //=> true
|
||||||
|
Defaults[.stringSet].contains("World!") //=> true
|
||||||
|
```
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
### How can I store a dictionary of arbitrary values?
|
### How can I store a dictionary of arbitrary values?
|
||||||
|
|
Loading…
Reference in New Issue