From my perspective, it's largely a misuse of the syntax to only pop out of the parenthesis an arbitrary subset of the trailing closures. I think it would be a mistake for us to weight this consideration too heavily.
The API guidelines emphasize clarity at the point of use, in particular:
When evaluating a design, reading a declaration is seldom sufficient; always examine a use case to make sure it looks clear in context.
I think it's important to go one step further and focus on "real-world" (or at least "realistic") uses cases. The striking thing about the APIs I found in my research was that they tended to have a "primary" closure, which didn't require an argument label in order to be clear (at least in my opinion):
UIView.animate(withDuration: 0.3) {
self.view.alpha = 0
} completion: { _ in
self.view.removeFromSuperview()
}
Props to @ricketson who was the first one (AFAIK) who observed this. An animations
argument label would be totally redundant:
UIView.animate(withDuration: 0.3) animations: {
self.view.alpha = 0
} completion: { _ in
self.view.removeFromSuperview()
}
... making it a violation of the API naming guidelines:
- Omit needless words. Every word in a name should convey salient information at the use site.
Combine has an unlabeled sink
variant:
ipAddressPublisher
.sink { identity in
self.hostnames.insert(identity.hostname!)
}
... and SwiftUI has an unlabeled Section
variant:
Section {
// content
}
The authors of these APIs established names and conventions to make the role of these closures clear without a label. Appending additional labeled closures does nothing to make the initial unlabeled closure less clear.
I see the appeal of the order of the closures matching the vertical display order for SwiftUI's Section
. Whenever possible we tried to craft the SwiftUI APIs to evoke the UIs they represented and this is in keeping with that.
However, I'm wary of us over-fitting to SwiftUI's Section
use case. It's my intuition that, most of the time, the label on the first closure would just be a "needless" word.
ipAddressPublisher
.sink { identity in
self.hostnames.insert(identity.hostname!)
}
ipAddressPublisher
.sink receiveValue: { identity in
self.hostnames.insert(identity.hostname!)
} receiveCompletion: { completion in
// handle error
}