322 lines
8.6 KiB
Swift
322 lines
8.6 KiB
Swift
import Foundation
|
|
#if os(macOS)
|
|
import AppKit
|
|
#else
|
|
import UIKit
|
|
#endif
|
|
|
|
extension Defaults.CodableBridge {
|
|
public func serialize(_ value: Value?) -> Serializable? {
|
|
guard let value = value else {
|
|
return nil
|
|
}
|
|
|
|
do {
|
|
// Some codable values like URL and enum are encoded as a top-level
|
|
// string which JSON can't handle, so we need to wrap it in an array
|
|
// We need this: https://forums.swift.org/t/allowing-top-level-fragments-in-jsondecoder/11750
|
|
let data = try JSONEncoder().encode([value])
|
|
return String(String(data: data, encoding: .utf8)!.dropFirst().dropLast())
|
|
} catch {
|
|
print(error)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public func deserialize(_ object: Serializable?) -> Value? {
|
|
guard let jsonString = object else {
|
|
return nil
|
|
}
|
|
|
|
return [Value].init(jsonString: "[\(jsonString)]")?.first
|
|
}
|
|
}
|
|
|
|
/**
|
|
Any `Value` that conforms to `Codable` and `Defaults.Serializable` will use `CodableBridge` to do the serialization and deserialization.
|
|
*/
|
|
extension Defaults {
|
|
public struct TopLevelCodableBridge<Value: Codable>: CodableBridge {}
|
|
}
|
|
|
|
/**
|
|
`RawRepresentableCodableBridge` is needed because, for example, with `enum SomeEnum: String, Codable, Defaults.Serializable`, the compiler will be confused between `RawRepresentableBridge` and `TopLevelCodableBridge`.
|
|
*/
|
|
extension Defaults {
|
|
public struct RawRepresentableCodableBridge<Value: RawRepresentable & Codable>: CodableBridge {}
|
|
}
|
|
|
|
/**
|
|
This exists to avoid compiler ambiguity.
|
|
*/
|
|
extension Defaults {
|
|
public struct CodableNSSecureCodingBridge<Value: Codable & NSSecureCoding>: CodableBridge {}
|
|
}
|
|
|
|
extension Defaults {
|
|
public struct URLBridge: CodableBridge {
|
|
public typealias Value = URL
|
|
}
|
|
}
|
|
|
|
extension Defaults {
|
|
public struct RawRepresentableBridge<Value: RawRepresentable>: Bridge {
|
|
public typealias Value = Value
|
|
public typealias Serializable = Value.RawValue
|
|
|
|
public func serialize(_ value: Value?) -> Serializable? {
|
|
value?.rawValue
|
|
}
|
|
|
|
public func deserialize(_ object: Serializable?) -> Value? {
|
|
guard let rawValue = object else {
|
|
return nil
|
|
}
|
|
|
|
return Value(rawValue: rawValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Defaults {
|
|
public struct NSSecureCodingBridge<Value: NSSecureCoding>: Bridge {
|
|
public typealias Value = Value
|
|
public typealias Serializable = Data
|
|
|
|
public func serialize(_ value: Value?) -> Serializable? {
|
|
guard let object = value else {
|
|
return nil
|
|
}
|
|
|
|
// Version below macOS 10.13 and iOS 11.0 does not support `archivedData(withRootObject:requiringSecureCoding:)`.
|
|
// 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, *) {
|
|
return try? NSKeyedArchiver.archivedData(withRootObject: object, requiringSecureCoding: true)
|
|
} else {
|
|
let keyedArchiver = NSKeyedArchiver()
|
|
keyedArchiver.requiresSecureCoding = true
|
|
keyedArchiver.encode(object, forKey: NSKeyedArchiveRootObjectKey)
|
|
return keyedArchiver.encodedData
|
|
}
|
|
}
|
|
|
|
public func deserialize(_ object: Serializable?) -> Value? {
|
|
guard let data = object else {
|
|
return nil
|
|
}
|
|
|
|
do {
|
|
return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Value
|
|
} catch {
|
|
print(error)
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Defaults {
|
|
public struct OptionalBridge<Wrapped: Serializable>: Bridge {
|
|
public typealias Value = Wrapped.Value
|
|
public typealias Serializable = Wrapped.Serializable
|
|
|
|
public func serialize(_ value: Value?) -> Serializable? {
|
|
Wrapped.bridge.serialize(value)
|
|
}
|
|
|
|
public func deserialize(_ object: Serializable?) -> Value? {
|
|
Wrapped.bridge.deserialize(object)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Defaults {
|
|
public struct ArrayBridge<Element: Serializable>: Bridge {
|
|
public typealias Value = [Element]
|
|
public typealias Serializable = [Element.Serializable]
|
|
|
|
public func serialize(_ value: Value?) -> Serializable? {
|
|
guard let array = value as? [Element.Value] else {
|
|
return nil
|
|
}
|
|
|
|
return array.map { Element.bridge.serialize($0) }.compact()
|
|
}
|
|
|
|
public func deserialize(_ object: Serializable?) -> Value? {
|
|
guard let array = object else {
|
|
return nil
|
|
}
|
|
|
|
return array.map { Element.bridge.deserialize($0) }.compact() as? Value
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Defaults {
|
|
public struct DictionaryBridge<Key: LosslessStringConvertible & Hashable, Element: Serializable>: Bridge {
|
|
public typealias Value = [Key: Element.Value]
|
|
public typealias Serializable = [String: Element.Serializable]
|
|
|
|
public func serialize(_ value: Value?) -> Serializable? {
|
|
guard let dictionary = value else {
|
|
return nil
|
|
}
|
|
|
|
// `Key` which stored in `UserDefaults` have to be `String`
|
|
return dictionary.reduce(into: Serializable()) { memo, tuple in
|
|
memo[String(tuple.key)] = Element.bridge.serialize(tuple.value)
|
|
}
|
|
}
|
|
|
|
public func deserialize(_ object: Serializable?) -> Value? {
|
|
guard let dictionary = object else {
|
|
return nil
|
|
}
|
|
|
|
return dictionary.reduce(into: Value()) { memo, tuple in
|
|
// Use `LosslessStringConvertible` to create `Key` instance
|
|
guard let key = Key(tuple.key) else {
|
|
return
|
|
}
|
|
|
|
memo[key] = Element.bridge.deserialize(tuple.value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
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.
|
|
*/
|
|
extension Defaults {
|
|
public struct SetBridge<Element: Serializable & Hashable>: Bridge {
|
|
public typealias Value = Set<Element>
|
|
public typealias Serializable = Any
|
|
|
|
public func serialize(_ value: Value?) -> Serializable? {
|
|
guard let set = value else {
|
|
return nil
|
|
}
|
|
|
|
if Element.isNativelySupportedType {
|
|
return Array(set)
|
|
}
|
|
|
|
return set.map { Element.bridge.serialize($0 as? Element.Value) }.compact()
|
|
}
|
|
|
|
public func deserialize(_ object: Serializable?) -> Value? {
|
|
if Element.isNativelySupportedType {
|
|
guard let array = object as? [Element] else {
|
|
return nil
|
|
}
|
|
|
|
return Set(array)
|
|
}
|
|
|
|
guard
|
|
let array = object as? [Element.Serializable],
|
|
let elements = array.map({ Element.bridge.deserialize($0) }).compact() as? [Element]
|
|
else {
|
|
return nil
|
|
}
|
|
|
|
return Set(elements)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Defaults {
|
|
public struct SetAlgebraBridge<Value: SetAlgebraSerializable>: Bridge where Value.Element: Serializable {
|
|
public typealias Value = Value
|
|
public typealias Element = Value.Element
|
|
public typealias Serializable = Any
|
|
|
|
public func serialize(_ value: Value?) -> Serializable? {
|
|
guard let setAlgebra = value else {
|
|
return nil
|
|
}
|
|
|
|
if Element.isNativelySupportedType {
|
|
return setAlgebra.toArray()
|
|
}
|
|
|
|
return setAlgebra.toArray().map { Element.bridge.serialize($0 as? Element.Value) }.compact()
|
|
}
|
|
|
|
public func deserialize(_ object: Serializable?) -> Value? {
|
|
if Element.isNativelySupportedType {
|
|
guard let array = object as? [Element] else {
|
|
return nil
|
|
}
|
|
|
|
return Value(array)
|
|
}
|
|
|
|
guard
|
|
let array = object as? [Element.Serializable],
|
|
let elements = array.map({ Element.bridge.deserialize($0) }).compact() as? [Element]
|
|
else {
|
|
return nil
|
|
}
|
|
|
|
return Value(elements)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Defaults {
|
|
public struct CollectionBridge<Value: CollectionSerializable>: Bridge where Value.Element: Serializable {
|
|
public typealias Value = Value
|
|
public typealias Element = Value.Element
|
|
public typealias Serializable = Any
|
|
|
|
public func serialize(_ value: Value?) -> Serializable? {
|
|
guard let collection = value else {
|
|
return nil
|
|
}
|
|
|
|
if Element.isNativelySupportedType {
|
|
return Array(collection)
|
|
}
|
|
|
|
return collection.map { Element.bridge.serialize($0 as? Element.Value) }.compact()
|
|
}
|
|
|
|
public func deserialize(_ object: Serializable?) -> Value? {
|
|
if Element.isNativelySupportedType {
|
|
guard let array = object as? [Element] else {
|
|
return nil
|
|
}
|
|
|
|
return Value(array)
|
|
}
|
|
|
|
guard
|
|
let array = object as? [Element.Serializable],
|
|
let elements = array.map({ Element.bridge.deserialize($0) }).compact() as? [Element]
|
|
else {
|
|
return nil
|
|
}
|
|
|
|
return Value(elements)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Defaults {
|
|
public struct AnyBridge: Defaults.Bridge {
|
|
public typealias Value = Defaults.AnySerializable
|
|
public typealias Serializable = Any
|
|
|
|
public func deserialize(_ object: Serializable?) -> Value? {
|
|
Value(value: object)
|
|
}
|
|
|
|
public func serialize(_ value: Value?) -> Serializable? {
|
|
value?.value
|
|
}
|
|
}
|
|
}
|