There are many ways that closures can be passed as arguments to an initializer. When I use layout types like VStack
it’s obvious that I’m not using the initializer syntax as written:
VStack(alignment: .center, spacing: nil, content: { Text("Hello") })
Since the first two parameters have default values, they don’t need to be included, so this can be shortened:
VStack(content: { Text("Hello") })
Note that the content
parameter label is required, because otherwise the compiler doesn’t know which of the parameters you’re passing in anonymously.
Instead of passing parameters in the brackets, I can close the brackets and add the closure at the end:
VStack() { Text("Hello") }
This is a trailing closure.
You’re probably aware that the opening and closing brackets at the end of VStack()
can also be omitted, and that’s how SwiftUI is usually written. But I thought I’d go over the definition of a trailing closure, before I provide some examples of how to use them.
I’m starting with a type, appropriately called TrailingClosureExample
, which has 3 closures as properties.
The first example, noBrackets
, proves that it’s possible to create a TrailingClosureExample
without any brackets. The first trailing closure does not need a label here, just as the body of a VStack
does not need to be labelled as content
. This is the opposite of bracketsEnclosingParameters
, where all of the parameters are inside the brackets.
Commas are only necessary for closures that are inside brackets.
I guess this technically isn’t an example of trailing closures, because you’re simply passing a bunch of closures as parameters.
Finally bracketsEnclosingFirstParameter
is halfway between these extremes, with the first closure inside the brackets while subsequent closures are trailing.
See how closure1
needs to be labelled, but closure2
doesn’t?
The first trailing closure never needs a label, even if other closures were passed inside the brackets, but all subsequent closures do.
Every structure has an automatically generated memberwise initializer, which is why I didn’t have to specify an initializer for TrailingClosureExample
.
Now I have another structure called TrailingClosureExample2
that declares an explicit initializer that makes closure1
anonymous by using an underscore like _
for the label.
This means that I won’t need to label it as closure1
.
This sneaky trick allows me to break the rules on labelling.
Even if I’m enclosing all of my parameters in brackets, or perhaps just the first one, I do not need to label the first closure in this case.