📃
@Published を Codable にする
CodingKeys 書くのめんどくさいよね。
Published+Decodable
extension Published: Decodable where Value: Decodable {
public init(from decoder: Decoder) throws {
self.init(wrappedValue: try Value(from: decoder))
}
}
Published+Encodable
extension Published: Encodable where Value: Encodable {
public func encode(to encoder: Encoder) throws {
try Published.extract(from: self).encode(to: encoder)
}
private static func extract(from published: Self) -> Value {
var published = published
let semaphore = DispatchSemaphore(value: 0)
var value: Value!
let _ = published.projectedValue.sink {
defer { semaphore.signal() }
value = $0
}
semaphore.wait()
return value
}
}
Playground
import Foundation
class Sample: ObservableObject, Codable {
@Published var name: String
init(name: String) {
self.name = name
}
}
extension Published: Decodable where Value: Decodable {
public init(from decoder: Decoder) throws {
self.init(wrappedValue: try Value(from: decoder))
}
}
extension Published: Encodable where Value: Encodable {
public func encode(to encoder: Encoder) throws {
try Published.extract(from: self).encode(to: encoder)
}
private static func extract(from published: Self) -> Value {
var published = published
let semaphore = DispatchSemaphore(value: 0)
var value: Value!
let _ = published.projectedValue.sink {
defer { semaphore.signal() }
value = $0
}
semaphore.wait()
return value
}
}
let o = Sample(name: "Nia")
let s = try JSONEncoder().encode(o)
print(String(data: s, encoding: .utf8)!)
let r = try JSONDecoder().decode(Sample.self, from: s)
print(r.name)
let _ = o.$name.sink {
print($0)
}
参考
Discussion
この方法には意味不明なバグがある。Date型をパースしようとすると、JSONDecoderのdateDecodingStrategyが適用されない。まあおそらく Value(from:) の時点で Date 型の型情報が欠落するんだろう。