You could always try reading apple's documentation to see if it helps solidify some things. However, I usually find Paul's approches to be much easier to read and digest.
If you look at the documentation for the Decoder
protocol, you will see that container(keyedBy:)
is listed as an instance method of it. That means that container(keyedBy:)
is basically a function that you can call on any instance of Decoder
that you create.
Then, if you look at the documentation for the container(keyedBy:)
function, you can see its full declaration. Although, it might look a bit confusing.
func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey
I'm not sure if you have reached the part in the course that covers Type Generics yet. But this basically says that the container(keyedBy:)
function takes some type of CodingKey
as a parameter, and returns that same type of KeyedDecodingContainer
.
So, in your example...
let container = try decoder.container(keyedBy: CodingKeys.self)
we are giving it CodingKeys.self
as a parameter, and it will return a KeyedDecodingContainer
based on that.
The KeyedDecodingContainer
is basically just a container holding keys and the encoded values associated with them. You can think of it like a dictionary that might end up looking something like this...
[
id: 0100010101011001010101010010001111,
name: 000110101110110010101010101001010101010100101010,
age: 1101001011101010010101010101001
]
So, when you call the .container(keyedBy:)
function, it will return something like what is shown above. Notice that the values associated with the keys are not necessarily UUID
, String
, or Int
. Instead, they are just some type of encoded data.
That's why we have to use this afterward...
name = try container.decode(String.self, forKey: .name)
That basically says, "Take all those ones and zeroes stored in the .name
key of the container, and try to see if you can make a String
out of it them. If you can, then store it in the name
variable."