Consider the following protocol:
protocol Foo: RawRepresentable, Codable where RawValue == Int {}
With a concrete type implementation:
final class Bar: Foo {
let rawValue: Int
init?(rawValue: Int) {
if rawValue == 0 {
return nil
}
self.rawValue = rawValue
}
}
Now suppose we provide default implementation of Codable
for any class adopting Foo
:
extension Foo {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let value = try container.decode(Int.self)
if let instance = Self.init(rawValue: value) {
self = instance
} else {
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Corrupt data."
)
}
}
func encode(to encoder: Encoder) throws {
var container = try encoder.singleValueContainer()
try container.encode(rawValue)
}
}
Note that I use self = instance
assignment because we have a fallible init?(rawValue: Data)
initializer. Now that seems to be legal for struct
s however, what's going on with class
es in that case? How does Swift handle that internally?
Certainly if I add AnyObject
requirement to Foo
, then this code does not compile. But it works just fine if I don't.
The source above can be tested with:
struct Baz: Codable {
var bar: Bar
}
for i in 0 ... 1 {
do {
let baz = try JSONDecoder().decode(
Baz.self,
from: "{ \"bar\": \(i) }".data(using: .utf8)!
)
print("Baz.bar (\(i)) = \(baz.bar.rawValue)")
} catch {
print("Got error (\(i): \(error)")
}
}
which outputs:
Got error (0: dataCorrupted(Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "bar", intValue: nil)], debugDescription: "Corrupt data.", underlyingError: nil))
Baz.bar (1) = 1