Reputation: 1431
I'm currently going through the Landmarks app tutorial and I'm at the section where you load in JSON into a Struct. They give the following code.
Reference: https://developer.apple.com/tutorials/swiftui/building-lists-and-navigation
import Foundation
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
I specifically don't understand the T.self
reference nor the <T: Decodable>
at the beginning of the func. Would someone be able to break the function down? Thank you.
Upvotes: 3
Views: 2005
Reputation: 1
import Foundation
var landMarks: [Landmark] = load("landmarkData.json")
func load<T: Decodable>(_ filename: String) -> T {
...
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
...
}
you missing var landMarks: [Landmark] = load("landmarkData.json")
this code, because the func load(_:)->T
return value type is [Landmark] type ,so the T is [Landmark] Array type.
this is my understand
Upvotes: -1
Reputation: 2785
I see that this question was answered in comments, but I felt it was worth an actual answer for people who are new to Swift trying to understand generics.
To answer the question, let's look at what T
is, which is defined in the function's signature
func load<T: Decodable>(_ filename: String) -> T
This says that the generic function load(_:String) -> T
returns some type T
that conforms to Decodable
, so T
is a generic way of referring to the type of the return value, where that type will be determined from the context of the call site, specifically, by the type of whatever is receiving the returned value.
The line being asked about is
return try decoder.decode(T.self, from: data)
Here T.self
is a way of referring to the type, T
, itself. If you look at the function signature for decode
you'll find it looks something like this:
func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T
Note that it says T.Type
for the type of the parameter, type
. That means that the parameter type
will hold the type T
rather than a value of type T
. This is Swift's way of differentiating between specifying that a variable is a value of a type vs its value being the type itself. This can be a little confusing at first, but it makes sense once you get your head around generics.
Because Swift is a strongly typed language, where types are defined at compile-time, decode
needs to know the type of the thing its attempting to decode. In a typeless language, like JavaScript or Python, this isn't necessary. In those languages, objects are basically dictionaries, so it can always decode a dictionary (or an array, or one of a small set of primitive types). In Swift, struct
and class
are not just syntactic sugar for a dictionary. They are more like C-style struct
- a bundle of binary data with a specific memory layout determined by the type. In order to decode properly, decode
has to know what it's decoding, and most often, how to tell that thing to decode itself via its init(from: Decoder) throws
method (which may have been synthesized by the compiler). Does it call NSImage.init(from: Decoder)
, or String.init(from: Decoder)
, etc... ?
If you're familiar with the way run-time polymorphism is implemented in OOP, providing the type, even generically, provides a means of obtaining the type's protocol witness table - which is sort of the protocol equivalent of the "vtable" that classes use for runtime dynamic dispatch for virtual methods. So it let's it do something analogous to calling a virtual method in OOP, except often it can figure it out at compile-time, and it can work for both value types and reference types. Understanding how his works also gives some insight into different but related issues like why you get different polymorphic behavior for the required methods declared directly in a protocol than for those declared only in a protocol extension (basically the ones declared in the protocol directly are in the witness table, so they can be dynamically dispatched, whereas the ones only in the extension aren't in the witness table, so any type-specific implementations are lost, and instead only the implementation in the protocol's extension can be called).
The following function uses this to print the type of its parameter (there are better ways of doing this, but this illustrates the roles of T.Type
and T.self
):
func foo<T>(_ value: T)
{
let typeOfValue: T.Type = T.self
print("The value, \(value), is of type, \(typeOfValue).")
}
Upvotes: 14