Skip to content

Instantly share code, notes, and snippets.

@aainaj
Last active June 20, 2020 06:47
Show Gist options
  • Save aainaj/97411e5c5a919eb5ac469d6aa2e902d2 to your computer and use it in GitHub Desktop.
Save aainaj/97411e5c5a919eb5ac469d6aa2e902d2 to your computer and use it in GitHub Desktop.
FailableCodableArray Property wrapper
import Foundation
@propertyWrapper
struct FailableCodableArray<Element: Codable>: Codable {
private struct AnyDecodableValue: Codable {}
private struct FailableDecodableValue<Value: Codable>: Codable {
let value: Value
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
value = try container.decode(Value.self)
}
}
var wrappedValue: [Element]
init(wrappedValue: [Element]) {
self.wrappedValue = wrappedValue
}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var elements: [Element] = []
while !container.isAtEnd {
do {
let value = try container.decode(FailableDecodableValue<Element>.self).value
elements.append(value)
} catch {
/* Without writing this it turns into infinite loop due to the
implementation of UnkeyedDecodingContainer.currentIndex.
Because the currentIndex is not incremented unless a decode succeeds. */
_ = try? container.decode(AnyDecodableValue.self)
continue
}
}
self.wrappedValue = elements
}
func encode(to encoder: Encoder) throws {
try wrappedValue.encode(to: encoder)
}
}
struct Response: Codable {
@FailableCodableArray var favorites: [Favorite]
}
enum Favorite: String, Codable {
case falcon
case penguin
}
let json = #"{ "favorites": ["falcon", "penguin", "peacock"] }"#.data(using: .utf8)!
let result = try JSONDecoder().decode(Response.self, from: json)
print(result)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment