Add CustomCodablePreferenceKey
This commit is contained in:
parent
70227a7fa1
commit
e5c4fceacd
|
@ -27,15 +27,24 @@ final class Preference<Key: PreferenceKey>: Codable {
|
|||
}
|
||||
|
||||
init(from decoder: any Decoder) throws {
|
||||
if let container = try? decoder.singleValueContainer() {
|
||||
if let keyType = Key.self as? any CustomCodablePreferenceKey.Type {
|
||||
self.storedValue = try keyType.decode(from: decoder) as! Key.Value?
|
||||
} else if let container = try? decoder.singleValueContainer() {
|
||||
self.storedValue = try? container.decode(Key.Value.self)
|
||||
}
|
||||
}
|
||||
|
||||
func encode(to encoder: any Encoder) throws {
|
||||
if let storedValue {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(storedValue)
|
||||
if let keyType = Key.self as? any CustomCodablePreferenceKey.Type {
|
||||
func encode<K: CustomCodablePreferenceKey>(_: K.Type) throws {
|
||||
try K.encode(value: storedValue as! K.Value, to: encoder)
|
||||
}
|
||||
return try _openExistential(keyType, do: encode)
|
||||
} else {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(storedValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,3 +28,8 @@ extension MigratablePreferenceKey {
|
|||
oldValue != defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
protocol CustomCodablePreferenceKey: PreferenceKey {
|
||||
static func encode(value: Value, to encoder: any Encoder) throws
|
||||
static func decode(from decoder: any Decoder) throws -> Value?
|
||||
}
|
||||
|
|
|
@ -15,11 +15,11 @@ final class PreferenceStoreTests: XCTestCase {
|
|||
static let defaultValue = false
|
||||
}
|
||||
|
||||
final class TestStore: Codable, ObservableObject {
|
||||
private var _test = Preference<TestKey>()
|
||||
final class TestStore<Key: PreferenceKey>: Codable, ObservableObject {
|
||||
private var _test = Preference<Key>()
|
||||
|
||||
// the acutal subscript expects the enclosingInstance to be a PreferenceStore, so do it manually
|
||||
var test: Bool {
|
||||
var test: Key.Value {
|
||||
get {
|
||||
Preference.get(enclosingInstance: self, storage: \._test)
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ final class PreferenceStoreTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
var testPublisher: some Publisher<TestKey.Value, Never> {
|
||||
var testPublisher: some Publisher<Key.Value, Never> {
|
||||
_test.projectedValue
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ final class PreferenceStoreTests: XCTestCase {
|
|||
|
||||
init(from decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self._test = try container.decode(Preference<TestKey>.self, forKey: .test)
|
||||
self._test = try container.decode(Preference<Key>.self, forKey: .test)
|
||||
}
|
||||
|
||||
enum CodingKeys: CodingKey {
|
||||
|
@ -52,18 +52,18 @@ final class PreferenceStoreTests: XCTestCase {
|
|||
|
||||
func testDecoding() throws {
|
||||
let decoder = JSONDecoder()
|
||||
let present = try decoder.decode(PreferenceCoding<TestStore>.self, from: Data("""
|
||||
let present = try decoder.decode(PreferenceCoding<TestStore<TestKey>>.self, from: Data("""
|
||||
{"test": true}
|
||||
""".utf8)).wrapped
|
||||
XCTAssertEqual(present.test, true)
|
||||
let absent = try decoder.decode(PreferenceCoding<TestStore>.self, from: Data("""
|
||||
let absent = try decoder.decode(PreferenceCoding<TestStore<TestKey>>.self, from: Data("""
|
||||
{}
|
||||
""".utf8)).wrapped
|
||||
XCTAssertEqual(absent.test, false)
|
||||
}
|
||||
|
||||
func testEncoding() throws {
|
||||
let store = TestStore()
|
||||
let store = TestStore<TestKey>()
|
||||
let encoder = JSONEncoder()
|
||||
XCTAssertEqual(String(data: try encoder.encode(PreferenceCoding(wrapped: store)), encoding: .utf8)!, """
|
||||
{}
|
||||
|
@ -83,7 +83,7 @@ final class PreferenceStoreTests: XCTestCase {
|
|||
let specificPref = expectation(description: "preference publisher")
|
||||
// initial and on change
|
||||
specificPref.expectedFulfillmentCount = 2
|
||||
let store = TestStore()
|
||||
let store = TestStore<TestKey>()
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
store.objectWillChange.sink {
|
||||
topLevel.fulfill()
|
||||
|
@ -96,5 +96,33 @@ final class PreferenceStoreTests: XCTestCase {
|
|||
store.test = true
|
||||
wait(for: [topLevel, specificPref])
|
||||
}
|
||||
|
||||
func testCustomCodable() throws {
|
||||
struct Key: CustomCodablePreferenceKey {
|
||||
static let defaultValue = 1
|
||||
static func encode(value: Int, to encoder: any Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(2)
|
||||
}
|
||||
static func decode(from decoder: any Decoder) throws -> Int? {
|
||||
3
|
||||
}
|
||||
}
|
||||
let store = TestStore<Key>()
|
||||
store.test = 123
|
||||
let encoder = JSONEncoder()
|
||||
XCTAssertEqual(String(data: try encoder.encode(PreferenceCoding(wrapped: store)), encoding: .utf8)!, """
|
||||
{"test":2}
|
||||
""")
|
||||
let decoder = JSONDecoder()
|
||||
let present = try decoder.decode(PreferenceCoding<TestStore<Key>>.self, from: Data("""
|
||||
{"test":2}
|
||||
""".utf8)).wrapped
|
||||
XCTAssertEqual(present.test, 3)
|
||||
let absent = try decoder.decode(PreferenceCoding<TestStore<Key>>.self, from: Data("""
|
||||
{}
|
||||
""".utf8)).wrapped
|
||||
XCTAssertEqual(absent.test, 1)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue