How to use trailing closure in if condition?
You have to put parentheses around the function call:
if "SMITH" == (lastNameForPerson(Person()) {$0.uppercaseString}) {
print("It's bob")
}
Or you put them around the ==
comparison (around the if
condition) in a C-style manner:
if ("SMITH" == lastNameForPerson(Person()) {$0.uppercaseString}) {
print("It's bob")
}
Alternatively, you can move the closure inside the parameter list (though this requires you to explicitly name the parameter):
if "SMITH" == lastNameForPerson(Person(), caseFolding: {$0.uppercaseString}) {
print("It's bob")
}
The reason this problem arises is that the if
statement 'claims' the {}
block, i.e. it doesn't belong to the lastNameForPerson
call any more. For the compiler, the second code block now looks like a normal block that wasn't properly separated from the previous (if
) statement.
You should probably consider avoiding a construct like this in general, since it might be hard to read (at first). Instead, you could store the result of the function call in a variable and compare that instead:
let lastName = lastNameForPerson(Person()) {$0.uppercaseString}
if "SMITH" == lastName {
print("It's bob")
}
Guard with trailing closure - Swift Syntax
Or is there a way to somehow have Xcode do autocompletion without the shortened syntax?
Xcode is quite smart. It chooses the non-trailing closure version if you do the auto completion in a control flow statement that needs a {
. For example, if I type:
guard let a = reminders.first
The auto complete shows:
If I choose the highlighted option, I get:
guard let a = reminders.first(where: <#T##(String) throws -> Bool#>)
If I then press enter, I get:
guard let a = reminders.first(where: { (<#String#>) -> Bool in
<#code#>
})
It doesn't turn it into a trailing closure, as long as you autocomplete within a guard/if statement.
Implementing Trailing Closure Syntax In A View Modifier That Wraps Another View
Each of your item
parameters is defined as (V) -> V
-- this means a closure that takes V
as a parameter and returns V
. In fact, all you want is () -> V
(a closure that takes no parameters and returns V
). That gets you the trailing closure you want -- then, you can pass just the V
to the view modifiers.
@available(iOS 14, *)
struct ToolbarAvailable14ViewModifier<V>: ViewModifier where V: View {
var placement: ToolbarItemPlacement
var item: V
func body(content: Content) -> some View {
content
.toolbar {
ToolbarItemGroup(placement: placement) {
item
}
}
}
}
@available(iOS 15, *)
struct ToolbarAvailable15ViewModifier<V>: ViewModifier where V: View {
var placement: ToolbarItemPlacement
var item: V
func body(content: Content) -> some View {
content
.toolbar {
ToolbarItemGroup(placement: placement) {
item
}
}
}
}
extension View {
@ViewBuilder
func toolbarIfAvailable<V>(placement: ToolbarItemPlacement, _ item: @escaping () -> V) -> some View where V: View {
if #available(iOS 14, *) {
self
.modifier(ToolbarAvailable14ViewModifier(placement: placement, item: item()))
}
if #available(iOS 15, *) {
self
.modifier(ToolbarAvailable15ViewModifier(placement: placement, item: item()))
}
else {
self
}
}
}
This also takes care of your _ in
issue since the closure no longer takes an argument/parameter.
Using trailing closure in for-in loop
This is because there would be ambiguity as to the context in which the trailing function should operate. An alternative syntax which works is:
let numbers = [2, 4, 6, 8, 10]
for doubled in (numbers.map { $0 * 2 }) // All good :)
{
print(doubled)
}
I would say this is likely because the 'in' operator has a higher precedence than trailing functions.
It's up to you which you think is more readable.
Super simple trailing closure syntax in Swift
Here is your example fixed:
func someFunctionThatTakesAClosure(closure: () -> Void) {
// function body goes here
print("we do something here and then go back")
// don't forget to call the closure
closure()
}
print("about to call function")
// call the function using trailing closure syntax
someFunctionThatTakesAClosure() {
print("we did what was in the function and can now do something else")
}
print("after calling function")
Output:
about to call function
we do something here and then go back
we did what was in the function and can now do something else
after calling function
Using if-let with trailing closure
If you really want to do the closure inline, I find going back to putting the trailing closure inside the function parentheses is better than wrapping the whole function + closure in parenthesis.
So:
if let newString = myFunction("hello world", { _->() in println("Do nothing") }) {
println(newString)
}
instead of:
if let newString = (myFunction("hello world") { _->() in println("Do nothing") }) {
println(newString)
}
Do nothing
is printed just fine if I paste your code into a playground – though only in the timeline (cmd-opt-enter), not in the right-hand margin.
Implicit parameters in trailing closure
In
doMathOperation(4, 5, operation: multiply)
...you are being asked for a function that takes two Double parameters, and multiply
is the name of just such a function, so it compiles and works.
In
doMathOperation(4, 5) { ... }
the curly braces themselves are the (body of the) function that takes two Double parameters. It makes no sense to put the name of another such function inside the curly braces.
But of course you are free to call any function you like, passing along the parameters that were passed to you; hence this works:
doMathOperation(4, 5) { multiply($0,$1) }
So, just to sum up: In the first one
doMathOperation(4, 5, operation: multiply)
there is one function, multiply
, and 4 and 5 are passed to it. In the second one
doMathOperation(4, 5) { multiply($0,$1) }
there are two functions, "anonymous" (the curly braces) and multiply
, and the 4 and 5 are passed into the first one and then it calls the second one.
Trailing closure issue in swift?
As @rob-napier states, you can do it just because the language allows it.
But, note your example is also doing two different things in that last part:
sum(from: 1, to: 10) {
$0
}
Not only are you omitting the return
statement, you're also omitting the named parameters, so the $0
is not dependant on the omitting the return
feature.
This would be a more accurate example for just omitting return
:
sum(from: 1, to: 10) { (num) -> (Int) in
num
}
That said, I wouldn't recommend using either of these features
. In most cases, it's better to make the code easier to read later. Your future self (and others who use the code after you) will thank you.
Swift completion handler - escaping trailing closure
To pass a trailing closure you need to end/close your function call and omit the closure argument label. For instance:
func foo(first: Int, second: Int, handler: (Int) -> Void) {
...
}
call syntax:
foo(first: 10, second: 20) { result in
/* trailing closure body */
}
By the way, you should simply your handler
argument declaration from:
handler: @escaping (_ imageURL: (ImageURL)) -> ()
to this:
handler: @escaping (ImageURL) -> Void
Using Void
or ()
is matter of style since they are logically the same. I prefer the former ;)
Related Topics
Difference Between Text("") and Text(Verbatim: "") Initializers in Swiftui
Swift 2.0 'Inout' Function Parameters and Computed Properties
Accessor Gives the Wrong Value in Swift 1.2/2.0 Release Build Only
Nsdata Contentsofurl Constructor Returns Nil
How to Check If a Property Has Been Set Using Swift Reflection
What Is the Swift Equivalent of -[Nsobject Description]
Pagination with Firebase Firestore - Swift 4
Add a Border with Cornerradius to an Image in Swiftui Xcode Beta 5
Show Nsmenu Only on Nsstatusbarbutton Right Click
How to Rotate Sprites Around a Joint
How to Use Trailing Closure in If Condition
Uialertview Is Crashing App on iOS 7
How to Subclass Nsoperation in Swift to Queue Skaction Objects for Serial Execution
How to Send Push Notifications Without Using Firebase Console
"Cgfloat Is Not Convertible to Int" When Trying to Calculate an Expression