I am going through the following tutorial on RxSwift
:
http://adamborek.com/thinking-rxswift/
and having trouble understanding the following pattern:
searchBar.rx.text.orEmpty
------------> .flatMap { [spotifyClient] query in
return spotifyClient.rx.search(query: query)
}.map { tracks in
return tracks.map(TrackRenderable.init)
}
This square brackets input parameter: [spotifyClient] query
seems very weird for me. I looked over official Apple
documentation for closures and functions and I can not see any examples of such input parameters. In Objective C
this would not bother me much, but it is Swift
. Could anyone explain, what this parameter means here?
Syntax Of Closure: We're declaring the closure on the first line, then assign it to the constant birthday , and call the closure on the last line. The closure now has one parameter of type String . It's declared within the closure type (String) -> () . You can then use the parameter name within the closure.
If we wanted to pass that closure into a function so it can be run inside that function, we would specify the parameter type as () -> Void . That means “accepts no parameters, and returns Void ” – Swift's way of saying “nothing”.
$0 and $1 are closure's first and second Shorthand Argument Names (SAN for short) or implicit parameter names, if you like. The shorthand argument names are automatically provided by Swift. The first argument is referenced by $0 , the second argument is referenced by $1 , the third one by $2 , and so on.
You will need to understand the variable capturing of closure idea.
struct Calculator {
var a: Int
var b: Int
var sum: Int {
return a + b
}
}
Then you use this as:
let calculator = Calculator(a: 3, b: 5)
// You define a closure where you will use this calculator instance
let closure = {
// closure captures the variables that are declared prior to the declaration of the closure.
// your calculator instance is being captured here
// it's default variable capture
print("The result is \(calculator.sum)")
}
closure() // Prints "The result is 8"
Till now, everything is okay. You get what's expected.
Now consider you declare the calculator instance as var
because in some point you need to mutate it's state. This is the case where complexity arises. Look:
var calculator = Calculator(a: 3, b: 5)
let closure = {
print("The result is \(calculator.sum)")
}
// You change the state of your calculator instance anytime before the closure gets executed
calculator.b = 20
// When the closure actually executes, you will be affected by any changes outside the closure
closure() // Prints "The result is 23"
So, the default variable capture isn't really helping you, instead it's creating problem in your case.
If you want to prevent this behaviour and print 8 even if the properties change after their capturing inside the closure, we can explicitly capture the variable with a capture list like this:
// [calculator] is your capture list
let closure = { [calculator] in
print("The result is \(calculator.sum)")
}
// change anything with calculator instance
calculator.b = 20
// execute the closure
closure() // Prints "The result is 8"
Capture List keeps immutable copy of the variable(s). Thanks to this copy, further changes to calculator, outside the closure, will not affect the closure.
You can capture multiple variables at once, hence it's called Capture List. Example:
let closure = { [variable1, variable2, variable3] in
print(variable1)
print(variable2)
print(variable3)
}
I recommend you read this article Capturing Values In Swift Closures.
Now, in your case spotifyClient
is an instance of a class that may be responsible to make API calls. This instance may need some changes for calling different APIs. So, to prevent the affect of any changes to spotifyClient
outside this closure you capture this instance in a Capture List.
You are confusing the parameter list with the capture list. The generic syntax is:
{ [capture list] (parameter list) in
...
...
}
Now take a look at the modified version of the above example:
let closure: (String)-> Void = { [calculator] stringParameter in // When using single parameter, you can always omit the () parentheses
print("\(stringParameter). The result is \(calculator.sum)")
}
// change anything with calculator instance
calculator.b = 20
// execute the closure
closure("Hey") // Prints "Hey. The result is 8"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With