This syntax is indeed hard nut to crack for Swift novices. It involves implicit getters, constructors, several kind of closure shortcuts, implicit return types, generic type inference and opaque types. Let's go step by step.
Getter syntax for computed properties
LandmarksApp
conforms to the App
protocol. This requires the definition of a body
property of a type complying with the Scene
protocol. Moreover, we are told in the documentation that the body
is expected to be a read-only computed property.
The general syntax of a computed property's getter is:
var X : T {
get { ... }
...
}
But if a it's a read-only propery, a shortcut may be used: put the getter code in a block directly after the type and without the get
:
var CX : T { ... } // that's a computed property with its getter
Moreover, you do not need a return if the getter code is made of a single expression: the return is implicit.
WindowGroup constructor using a closure
WindowsGroup
is the type we are going to use. It complies with Scene
. It is a generic type based on the type parameter Content
that conforms to the View
protocol, and we have a ContentView
for that. So here the first try:
struct LandmarksApp: App {
var body : WindowGroup<ContentView> { // read-only getter
WindowGroup<ContentView>(content: myhelper) // return a newly constructed object
}
}
The constructor for WindowGroup<T>
requires as argument a function that takes no argument and returns a T
. To start simple, an ordinary function was used. It's defined as follows:
func myhelper() -> ContentView {
return ContentView()
}
Simplification based on closures and generics
We can simplify with a closure to replace the helper function:
var body : WindowGroup<ContentView> {
WindowGroup<ContentView>(content: { ()->ContentView in return ContentView() })
}
Swift can infer the types used in the closure from the context and it also allows an implicit return for closures made of a single expression. In absence of parameters, we remove in
. All those simplification lead to:
var body : WindowGroup<ContentView> {
WindowGroup<ContentView>(content: { ContentView() })
}
Swift is also able to infer the generic type from the context. So this expression could be simplified to:
var body : WindowGroup<ContentView> {
WindowGroup (content: { ContentView() })
}
Last but not least, the most tricky part: Swift allows a special syntax if the closure is the last argument of a function. It's called trailing closures. This leads to:
var body : WindowGroup<ContentView> {
WindowGroup () { ContentView() }
}
This syntax also allows to get rid of the empty parenthesis:
var body : WindowGroup<ContentView> {
WindowGroup { ContentView() }
}
Opaque types
Until now, we used a body
of a concrete well-known type. But couldn't the type of this variable be inferred? It would thus be tempting to write:
var body { // OUCH !!!
WindowGroup { ContentView() }
}
But Swift requires computed properties to have an explicit type. To keep it as general as possible but ensure compliance with the required protocol, an opaque type may be used:
var body : some Scene {
WindowGroup { ContentView() }
}
More on opaque types in this question.
Wrap up
