Last active
March 3, 2022 14:13
-
-
Save ericlewis/e7c7aa2b9d6d62490c718f52f08579b5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension Published: Encodable where Value: Encodable { | |
public func encode(to encoder: Encoder) throws { | |
var container = encoder.singleValueContainer() | |
let value = _PublishedValueExtractor.shared.extractValue(from: self) | |
try container.encode(value) | |
} | |
} | |
fileprivate struct _PublishedValueExtractor { | |
static let shared = _PublishedValueExtractor() | |
func extractValue<Value>(from published: Published<Value>) -> 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 | |
} | |
} |
Bonus ObservedObject:
extension ObservedObject: Codable where ObjectType: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self = try .init(initialValue: container.decode(ObjectType.self))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self)
}
}
The rest of our friends:
extension StateObject: Codable where ObjectType: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let value = try container.decode(ObjectType.self)
self = .init(wrappedValue: value)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self)
}
}
extension State: Codable where Value: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self = try .init(initialValue: container.decode(Value.self))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self)
}
}
Scene & AppStorage example:
// Note: we have to make MANY overloads
extension AppStorage: Decodable where Value == Bool {
public init(from decoder: Decoder) throws where Value == Bool {
let container = try decoder.singleValueContainer()
let result = try container.decode(_StorageContainer<Value>.self)
self = .init(wrappedValue: result.value, result.key)
}
}
extension AppStorage: Encodable where Value: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
guard let key = Mirror(reflecting: self).descendant(0, 0) as? String else {
throw EncodingError.invalidValue(self, .init(codingPath: [], debugDescription: ""))
}
try container.encode(_StorageContainer(key: key, value: self.wrappedValue))
}
}
extension SceneStorage: Decodable where Value == Bool {
public init(from decoder: Decoder) throws where Value == Bool {
let container = try decoder.singleValueContainer()
let result = try container.decode(_StorageContainer<Value>.self)
self = .init(wrappedValue: result.value, result.key)
}
}
extension SceneStorage: Encodable where Value: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
guard let key = Mirror(reflecting: self).descendant(0, 0) as? String else {
throw EncodingError.invalidValue(self, .init(codingPath: [], debugDescription: "Could not extract key."))
}
try container.encode(_StorageContainer(key: key, value: self.wrappedValue))
}
}
fileprivate struct _StorageContainer<Value: Codable>: Codable {
let key: String
let value: Value
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Decodable: