I have a call to a URLSession:
var someVar = "Are you in my scope?"
let task = session.dataTask(with: url, completionHandler: updatePlaces)
Where updatePlaces is a function defined as:
private func updatePlaces(data: Data?, response: URLResponse?, error: Error?) -> Void
I split it up into another function because I really don't like having this as one large block of code with my "updatePlaces" function as a closure in my original session.dataTask. It just seems to be doing too much in one function. I want to split it up.
I can also make a closure using the syntax below, but I see no difference between this and the function (I understand a function is a special type of closure, and I see no difference):
lazy var updatePlaces: (Data?, URLResponse?, Error?) -> Void = {(data, response, error) in
However, earlier you saw I added someVar to the scope. With the function and the lazy var, I do not have access to someVar. However, if I make the closure inside the session.dataTask, someVar is captured and I can reference it per the docs:
Closures can capture and store references to any constants and
variables from the context in which they are defined. This is known as
closing over those constants and variables. Swift handles all of the
memory management of capturing for you.
So... I have two specific questions.
Are there ANY differences between my function declaration and my closure declaration and just passing that to completion handler.
Is there any way to capture the references in the original function if I don't define the closure in it? I'm guessing not.
And a more... generic question: What is the preferred way of handling this?
Related
I have been following a video tutorial, and have written the following code:
func downloadWeatherDetails(completed: ()->() ) {
let currentWeatherURL = URL(string: CURRENT_WEATHER_URL)!
Alamofire
.request(currentWeatherURL)
.responseJSON(completionHandler: { response in
let result = response.result
print(result)
})
completed()
}
So basically, my understanding is as follows. The .responseJSON handler lets you call code after the request has been fired. It allows you to specify a completionHandler, which in my case, is the closure:
{ response in
let result = response.result
print(result)
}
However, what I don't understand is what the "response" keyword actually signifies. I researched the usage of closures and saw that the syntax is:
{(param) -> returnType in { code here }
Thus, is the "response" keyword a parameter? If so, how is it being declared and where is the data coming from? How is the data passed into the "response" object? Also, why is only one parameter allowed? The code did not work if I made it as follows, for example:
{ (response, test) in
let result = response.result
print(result)
}
I would really appreciate a thorough explanation on this as I've found no help elsewhere online. I've gone through Apple's "The Swift Programming Language", a multitude of different explanations, and similar questions, but still do not understand completely.
Just to clarify, I do not believe my question is a duplicate since my question revolves primarily on the captured value stored in response rather than the syntax of closures as a whole. I went through the linked question while trying to figure out my own problem, but it did not help me sufficiently.
Minor clarification needed:
Is it always the case that when a method takes a closure as one of its parameters, for example, .testMethod(testParam: (String) -> ()) and would thus in practice be used: .testMethod(testParam: { (capturedVar) in statements} (correct me if im wrong), is it always the case that the parameter of the closure ((String) in this case) will be captured and stored in capturedVar? Will there always be data passed into the variable you define? Or is this cycle specific to alamofire?
Swift closures are defined as:
{ (parameters) -> return_type in
statements
}
That is, the names in parenthesis are the variables the closure has captured, and the -> type is the optional return type (optional because the compiler can usually infer it). Alamofire's responseJSON method captures a DataResponse<Any> parameter, which you can name whatever you want, but which is usually just named response. You can then access it inside that closure.
Also, your completed() call should be inside the responseJSON call, not outside, otherwise it just gets called immediately.
I have the following function in Swift 3
func fetchOrders(_ completionHandler: (_ orders: [Order]) -> Void)
{
ordersStore.fetchOrders { (orders: () throws -> [Order]) -> Void in
do {
let orders = try orders()
completionHandler(orders)
} catch {
completionHandler([])
}
}
}
What does _ completionHandler argument in fetchOrders mean?
What does (orders: () throws -> [Order]) mean?
PS : I am new to iOS and Swift
There's quite a lot in here, so we'll break it down one piece at a time:
func fetchOrders(_ completionHandler: (_ orders: [Order]) -> Void)
This is a function called fetchOrders.
It has one parameter (completionHandler) and returns nothing.
The first _ indicates that there is no "external name" of the first parameter. That is, you do not have to label it (in fact, you cannot). (For subtle reasons that don't really matter here, I believe the author made a mistake using _ there, and I would not have done that.)
The completionHandler is the "internal name," what the parameter is called inside the function.
The type of completionHandler is (_ orders: [Order]) -> Void. We'll break that down now.
This value is a closure that takes an [Order] (array of Order) and returns Void. Informally this means "returns nothing" but literally means it returns the empty tuple ().
The _ orders: syntax is in practice a comment. In principle the _ is an external name (but that's the only legal external name for a closure), and orders is an internal name, but in reality, closures parameters do not have names in any meaningful way, so this is purely informational.
I believe this is a poor use of the closure parameter commenting system. Since orders tells us nothing more than [Order], I would have omitted it, and made the type just ([Order]) -> Void.
Now we'll turn to the next line:
ordersStore.fetchOrders { (orders: () throws -> [Order]) -> Void in
This calls the fetchOrders method on ordersStore. We can tell from this code that fetchOrders takes a closure parameter. This is called "trailing closure" syntax in Swift, and is why I would not have used the _ for our closure. With trailing closure syntax, the external name of the parameter is not needed.
The author has provided type information here that probably wasn't necessary, but we can explore it anyway. This could likely have been written as just { orders in, but then the reader would probably have been surprised by this somewhat unusual code.
We have been passed a closure called orders that takes nothing and returns [Order] or throws an error. Basically this is a way to say that fetchOrders might fail.
The author is working around an awkwardness in Swift's throws system, which does not have a natural way to express an asynchronous action that might fail. This is one way to fix it; you pass a throwing (i.e. a possibly failing) function. I don't favor this approach, I favor using a Result enum for this case because I think it scales better and avoids possible unintended side effects, but that's a debatable point (and the Swift community hasn't really decided how to deal with this common problem).
This all leads us to:
do {
let orders = try orders()
completionHandler(orders)
} catch {
completionHandler([])
}
This is where the orders closure is evaluated. (This is very important; if orders has side effects, this is when they occur, which may be on a different queue than was intended. That's one reason I don't favor this pattern.) If the closure succeeds, we return its result, otherwise we return [] in the catch below.
In this particular case, the throws approach is slightly silly, because it's silently flattened into [] without even a log message. If we don't care about the errors, then failure should have just returned [] to start with and not messed with throws. But it's possible that other callers do check the errors.
In either case, we call the completionHandler closure with our result, chaining this back to our original caller.
This do/catch block could have been more simply written as:
let completedOrders = try? orders() ?? []
completionHandler(completedOrders)
This makes it clearer that we're ignoring errors by turning it into an optional, and avoids code duplication of the call to completionHandler.
(I just add the extra let binding to make the code a little easier to read; it isn't needed.)
The completionHandler argument means that the expected parameter (named completionHandler) must be a function that takes a list of Order objects and does not return any value.
completionHandler is the a variable name. In this specific example, this variable is a callback. You know is a callback function because (orders: [Order]) -> Void is it's data type; in this particular case, said data type is a function that receives an array of Order objects in a variable _orders and doesn't have a return value (the Void part).
TL;DR:
it's the variable name, of type:
function which receives an array of Order as a parameter and acts as a callback.
In the following code (using the Parse library), I have:
query.findObjectsInBackgroundWithBlock({(objects: [AnyObject]?, error: NSError?) -> Void in
// yada yada
})
Is the -> Void in necessary in the code (rather, is there something else I can use? Removing it throws errors in Xcode.)?
I am very new to Swift, so this may be a dumb question...
The in line is how you get the parameters into the block:
(objects: [AnyObject]?, error: NSError?) -> Void in
If the types are known in some other way, you can omit them:
objects, error in
But you cannot omit the in line entirely unless you pick up the parameters in some other way in your code (as $0 and $1), and that would make your code difficult to understand. It's better to keep the in line so you know what the parameters are.
This -> Void in is saying the closure is not returning anything and what follows the in is the body of the closure. The -> Void can be removed because Swift is very good at inferring types. The in is necessary.
The start of the closure’s body is introduced by the in keyword. This keyword indicates that the definition of the closure’s parameters and return type has finished, and the body of the closure is about to begin.
You can go read more about closures here: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html
I am building an app using Swift for the first time and using the AlamoFire library. I've built apps using Obj-C and AFNetworking before, but I'm having a hard time groking this Swift response method:
Alamofire.request(.GET, "http://someapi.com/thing.json")
.responseJSON { _, _, JSON, _ in
println(JSON)
}
The actual method definition is:
public func responseJSON(options: NSJSONReadingOptions = .AllowFragments, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
return response(serializer: Request.JSONResponseSerializer(options: options), completionHandler: { request, response, JSON, error in
completionHandler(request, response, JSON, error)
})
}
I don't really understand what's going on here when I use this response method.
Why am I not using parens in the method call?
Am I just passing a block or anonymous function into this method?
What is the significance of passing underscores (_)?
What is the in keyword doing?
It's all here:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID102
No parens: that's trailing closure syntax. Idea is that closures can get long, hard to remember/determine long-distance paren pair.
Yes, block = anonymous function = (anonymous) closure. Yes, you're passing that as the only parameter. Since it's omitted, 'options' gets its default value of '.AllowFragments'. Trailing closure { ... } gets bound to completionHandler parameter.
The '_' is the 'don't care about this parameter' syntax ... e.g. if the function doesn't use the parameter, no point to giving it a local name.
in is part of closure syntax: indicates start of function body. Definitely read the whole closure syntax chapter above. It's designed to be extremely terse, nothing intuitive about it.
I just have a quick conceptual question about this closure here:
func getRandomUser(onCompletion: (JSON) -> Void) {
let route = baseURL
makeHTTPGetRequest(route, onCompletion: { json, err in
onCompletion(json)
})
}
What does the line onCompletion(json) do exactly? Is this a recursive call to onCompletion?
Let's look at your function line-by-line:
1. func getRandomUser(onCompletion: (JSON) -> Void) {
2. let route = baseURL
3. makeHTTPGetRequest(route, onCompletion: { json, err in
4. onCompletion(json)
5. })
6. }
Line 1: this gives the signature of your function; it takes one argument, and that argument is of type (JSON) -> Void, which means the argument it accepts is a closure that itself takes one argument of type JSON and does not have a return value, i.e. "returns Void", -> Void or -> (); note that the function definition also includes a local parameter name for that argument: within the function body, that closure is assigned to the constant onCompletion
Line 2: constant assignment...
Line 3: this calls the function makeHTTPGetRequest(_:onCompletion:), which takes two arguments: a route (which does not use an external label in the call, hence the _ in the function name as given previously), and a closure - this closure is of type (JSON, NSError?) -> Void; note that on this line where onCompletion occurs, this is the external label required for the second argument when calling the makeHTTPGetRequest(_:onCompletion:) function, there is not any assignment taking place (as occurred on Line 1)
Line 4: the closure assigned to the constant onCompletion is called with one argument...
So while it may be confusing that the text onCompletion occurs twice in this section of code (before the closure is actually invoked on Line 4), there is only one constant that goes by that name - the other occurrence is simply an external label for an argument.
If it helps at all, we can actually eliminate the use of the onCompletion label altogether by taking advantage of Swift's "trailing closure syntax" and rewrite it like so:
func getRandomUser(onCompletion: (JSON) -> Void) {
let route = baseURL
makeHTTPGetRequest(route) { json, err in onCompletion(json) }
}
...but that's a topic for another post :)
The onCompletion parameter of makeHTTPGetRequest is not available in its own scope. It's basically declaring what onCompletion is. It would then be calling onCompletion on getRandomUser.