It seems like decode(_forKey:)
ignores its first parameter, and instead relies on the generic parameter to decide what type to decode. If this is the case, what is the first parameter for?
class Cat: Codable {
func speak() -> String { return "Meow" }
}
class Lion: Cat {
override func speak() -> String { return "Roar!" }
}
class Person: Codable {
let firstPet: Cat
let secondPet: Cat
init(firstPet: Cat, secondPet: Cat) {
self.firstPet = firstPet
self.secondPet = secondPet
}
enum CodingKeys: CodingKey { case firstPet, secondPet }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.firstPet = try container.decode(Lion.self, forKey: .firstPet)
let typeOfCat: Cat.Type = Lion.self
self.secondPet = try container.decode(typeOfCat, forKey: .secondPet)
}
}
let before = Person(firstPet: Lion(), secondPet: Lion())
let after = try! JSONDecoder().decode(Person.self, from: JSONEncoder().encode(before))
after.firstPet.speak() //"Roar!"
after.secondPet.speak() //"Meow" ...really?
The metatype parameter to the
decode(...)
calls is used to specialize the generic parameter. Swift doesn't have syntax for manually specializing generics like C++ does (e.g.decode<Int>(forKey: ...)
), so this is a way to bind the generic parameter to a concrete type.The benefit of passing in a metatype (instead of, say, relying on the return type to provide resolution) is that the result of the expression is unambiguous. Relying on the return result can lead to some surprising situations:
results in
With an explicit metatype, this is a non-issue.
As for why the generic type is used over the concrete instance of the metatype you pass in — it's generally unexpected to have a concrete metatype instance have a different static type than itself.