I'm working my way through the beginning SwiftUI tutorial on the Apple Developer website. The example program uses a NavigationLink
, like so:
NavigationLink {
LandmarkDetail(landmark: landmark)
} label: {
LandmarkRow(landmark: landmark)
}
I understand what this code does, but I don't understand the "label:" syntax. This seems to be some sort of initialization shorthand. For example, I found that the above can be rewritten as:
NavigationLink(destination: { LandmarkDetail(landmark: landmark) },
label: { LandmarkRow(landmark: landmark) })
But I cannot do something like this:
NavigationLink {
LandmarkRow(landmark: landmark)
} destination: {
LandmarkDetail(landmark: landmark)
}
It gives me a compile error:
Type '() -> LandmarkDetail' cannot conform to View
Why does it work the first way, but not this way? I thought maybe it was because "destination:" is the first init() argument, but there are other overloaded inits in which it isn't, and I can't make it work with those either even though they seem to follow the same logic.
The initialiser that you are calling is this.
init(@ViewBuilder destination: () -> Destination, @ViewBuilder label: () -> Label)
Notice that the last parameters of the initialiser are all function types (in fact, those are the only parameters in this case). For such a parameter list, you can pass closures for these parameters using the trailing closure syntax, i.e. writing the closure outside the ()
, where you would normally pass non-function type parameters. (But in this case there are no non-function type parameters, so you don't even need the ()
)
For the first closure written with the trailing closure syntax, you don't need to write its argument label, but for the rest of them, you do need to write their argument labels. That's why you don't need destination:
, but you do need label:
.
Another example:
func foo(p: Int, x: () -> Void, y: () -> Void, z: () -> Void) { }
This can be called like this:
foo(p: 1) {
// this is the closure for the x parameter...
} y: {
// this is the closure for the y parameter...
} z: {
// this is the closure for the z parameter...
}
You can also only start using trailing closures for y
, in which case you don't need to write y:
:
foo(p: 1, x: { /*closure for x*/ }) {
// closure for y
} z: {
// closure for z
}
See also the language reference about this:
A function call expression can include trailing closures in the form of closure expressions immediately after the closing parenthesis. The trailing closures are understood as arguments to the function, added after the last parenthesized argument. The first closure expression is unlabeled; any additional closure expressions are preceded by their argument labels.
This is invalid:
NavigationLink {
LandmarkRow(landmark: landmark)
} destination: {
LandmarkDetail(landmark: landmark)
}
because there is no initialiser that:
destination