Add `Defaults.PreferNSSecureCoding` and `Defaults.PreferRawRepresentable` (#83)
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
This commit is contained in:
parent
04f9b3f45e
commit
5f16cefebe
|
@ -90,14 +90,21 @@ extension Defaults.Serializable where Self: Codable & NSSecureCoding {
|
|||
public static var bridge: Defaults.CodableNSSecureCodingBridge<Self> { Defaults.CodableNSSecureCodingBridge() }
|
||||
}
|
||||
|
||||
extension Defaults.Serializable where Self: RawRepresentable {
|
||||
public static var bridge: Defaults.RawRepresentableBridge<Self> { Defaults.RawRepresentableBridge() }
|
||||
extension Defaults.Serializable where Self: Codable & NSSecureCoding & Defaults.PreferNSSecureCoding {
|
||||
public static var bridge: Defaults.NSSecureCodingBridge<Self> { Defaults.NSSecureCodingBridge() }
|
||||
}
|
||||
|
||||
extension Defaults.Serializable where Self: RawRepresentable & Codable {
|
||||
extension Defaults.Serializable where Self: Codable & RawRepresentable {
|
||||
public static var bridge: Defaults.RawRepresentableCodableBridge<Self> { Defaults.RawRepresentableCodableBridge() }
|
||||
}
|
||||
|
||||
extension Defaults.Serializable where Self: Codable & RawRepresentable & Defaults.PreferRawRepresentable {
|
||||
public static var bridge: Defaults.RawRepresentableBridge<Self> { Defaults.RawRepresentableBridge() }
|
||||
}
|
||||
|
||||
extension Defaults.Serializable where Self: RawRepresentable {
|
||||
public static var bridge: Defaults.RawRepresentableBridge<Self> { Defaults.RawRepresentableBridge() }
|
||||
}
|
||||
extension Defaults.Serializable where Self: NSSecureCoding {
|
||||
public static var bridge: Defaults.NSSecureCodingBridge<Self> { Defaults.NSSecureCodingBridge() }
|
||||
}
|
||||
|
|
|
@ -100,3 +100,21 @@ public protocol DefaultsSetAlgebraSerializable: SetAlgebra, Defaults.Serializabl
|
|||
|
||||
/// Convenience protocol for `Codable`.
|
||||
public protocol DefaultsCodableBridge: Defaults.Bridge where Serializable == String, Value: Codable {}
|
||||
|
||||
/**
|
||||
Ambiguous bridge selector protocol. This lets you select your preferred bridge when there are multiple possibilities.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
enum Interval: Int, Codable, Defaults.Serializable, Defaults.PreferRawRepresentable {
|
||||
case tenMinutes = 10
|
||||
case halfHour = 30
|
||||
case oneHour = 60
|
||||
}
|
||||
```
|
||||
|
||||
By default, if an `enum` conforms to `Codable` and `Defaults.Serializable`, it will use the `CodableBridge`, but by conforming to `Defaults.PreferRawRepresentable`, we can switch the bridge back to `RawRepresentableBridge`.
|
||||
*/
|
||||
public protocol DefaultsPreferRawRepresentable: RawRepresentable {}
|
||||
public protocol DefaultsPreferNSSecureCoding: NSSecureCoding {}
|
||||
|
|
|
@ -19,6 +19,8 @@ public enum Defaults {
|
|||
public typealias Serializable = DefaultsSerializable
|
||||
public typealias CollectionSerializable = DefaultsCollectionSerializable
|
||||
public typealias SetAlgebraSerializable = DefaultsSetAlgebraSerializable
|
||||
public typealias PreferRawRepresentable = DefaultsPreferRawRepresentable
|
||||
public typealias PreferNSSecureCoding = DefaultsPreferNSSecureCoding
|
||||
public typealias Bridge = DefaultsBridge
|
||||
typealias CodableBridge = DefaultsCodableBridge
|
||||
|
||||
|
|
|
@ -8,6 +8,12 @@ private enum FixtureCodableEnum: String, Defaults.Serializable & Codable & Hasha
|
|||
case oneHour = "1 Hour"
|
||||
}
|
||||
|
||||
private enum FixtureCodableEnumPreferRawRepresentable: Int, Hashable, Codable, Defaults.Serializable, Defaults.PreferRawRepresentable {
|
||||
case tenMinutes = 10
|
||||
case halfHour = 30
|
||||
case oneHour = 60
|
||||
}
|
||||
|
||||
extension Defaults.Keys {
|
||||
fileprivate static let codableEnum = Key<FixtureCodableEnum>("codable_enum", default: .oneHour)
|
||||
fileprivate static let codableEnumArray = Key<[FixtureCodableEnum]>("codable_enum", default: [.oneHour])
|
||||
|
@ -118,6 +124,13 @@ final class DefaultsCodableEnumTests: XCTestCase {
|
|||
XCTAssertEqual(Defaults[.codableEnumDictionary]["1"], .halfHour)
|
||||
}
|
||||
|
||||
func testFixtureCodableEnumPreferRawRepresentable() {
|
||||
let fixture: FixtureCodableEnumPreferRawRepresentable = .tenMinutes
|
||||
let keyName = "testFixtureCodableEnumPreferRawRepresentable"
|
||||
_ = Defaults.Key<FixtureCodableEnumPreferRawRepresentable>(keyName, default: fixture)
|
||||
XCTAssertNotNil(UserDefaults.standard.integer(forKey: keyName))
|
||||
}
|
||||
|
||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
|
||||
func testObserveKeyCombine() {
|
||||
let key = Defaults.Key<FixtureCodableEnum>("observeCodableEnumKeyCombine", default: .tenMinutes)
|
||||
|
|
|
@ -21,6 +21,19 @@ private final class UnicornCodableAndNSSecureCoding: NSObject, NSSecureCoding, C
|
|||
}
|
||||
}
|
||||
|
||||
@objc(UnicornCodableAndPreferNSSecureCoding)
|
||||
private final class UnicornCodableAndPreferNSSecureCoding: NSObject, NSSecureCoding, Codable, Defaults.Serializable, Defaults.PreferNSSecureCoding {
|
||||
static let supportsSecureCoding = true
|
||||
|
||||
func encode(with coder: NSCoder) {}
|
||||
|
||||
init?(coder: NSCoder) {}
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
|
||||
extension Defaults.Keys {
|
||||
fileprivate static let codable = Key<Unicorn>("codable", default: fixtureCodable)
|
||||
fileprivate static let codableArray = Key<[Unicorn]>("codable", default: [fixtureCodable])
|
||||
|
@ -135,7 +148,18 @@ final class DefaultsCodableTests: XCTestCase {
|
|||
|
||||
func testCodableAndNSSecureCoding() {
|
||||
let fixture = UnicornCodableAndNSSecureCoding()
|
||||
_ = Defaults.Key<UnicornCodableAndNSSecureCoding>("testCodableAndNSSecureCoding", default: fixture)
|
||||
let keyName = "testCodableAndNSSecureCoding"
|
||||
_ = Defaults.Key<UnicornCodableAndNSSecureCoding>(keyName, default: fixture)
|
||||
XCTAssertNil(UserDefaults.standard.data(forKey: keyName))
|
||||
XCTAssertNotNil(UserDefaults.standard.string(forKey: keyName))
|
||||
}
|
||||
|
||||
func testCodableAndPreferNSSecureCoding() {
|
||||
let fixture = UnicornCodableAndPreferNSSecureCoding()
|
||||
let keyName = "testCodableAndPreferNSSecureCoding"
|
||||
_ = Defaults.Key<UnicornCodableAndPreferNSSecureCoding>(keyName, default: fixture)
|
||||
XCTAssertNil(UserDefaults.standard.string(forKey: keyName))
|
||||
XCTAssertNotNil(UserDefaults.standard.data(forKey: keyName))
|
||||
}
|
||||
|
||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
|
||||
|
|
21
readme.md
21
readme.md
|
@ -789,6 +789,27 @@ if let mimeType: mime = Defaults[.magic]["enum"]?.get() {
|
|||
|
||||
For more examples, see [Tests/DefaultsAnySerializableTests](./Tests/DefaultsTests/DefaultsAnySeriliazableTests.swift).
|
||||
|
||||
### Serialization for ambiguous `Codable` type
|
||||
|
||||
You may have a type that conforms to `Codable & NSSecureCoding` or a `Codable & RawRepresentable` enum. By default, `Defaults` will prefer the `Codable` conformance and use the `CodableBridge` to serialize it into a JSON string. If you want to serialize it as a `NSSecureCoding` data or use the raw value of the `RawRepresentable` enum, you can conform to `Defaults.PreferNSSecureCoding` or `Defaults.PreferRawRepresentable` to override the default bridge:
|
||||
|
||||
```swift
|
||||
enum mime: String, Codable, Defaults.Serializable, Defaults.PreferRawRepresentable {
|
||||
case JSON = "application/json"
|
||||
}
|
||||
|
||||
extension Defaults.Keys {
|
||||
static let magic = Key<[String: Defaults.AnySerializable]>("magic", default: [:])
|
||||
}
|
||||
|
||||
print(UserDefaults.standard.string(forKey: "magic"))
|
||||
//=> application/json
|
||||
```
|
||||
|
||||
Had we not added `Defaults.PreferRawRepresentable`, the stored representation would have been `"application/json"` instead of `application/json`.
|
||||
|
||||
This can also be useful if you conform a type you don't control to `Defaults.Serializable` as the type could receive `Codable` conformance at any time and then the stored representation would change, which could make the value unreadable. By explicitly defining which bridge to use, you ensure the stored representation will always stay the same.
|
||||
|
||||
### Custom `Collection` type
|
||||
|
||||
1. Create your `Collection` and make its elements conform to `Defaults.Serializable`.
|
||||
|
|
Loading…
Reference in New Issue