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
Related
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?
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.
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 downloaded Xcode 7 Beta 2 and am trying to use my knowledge of Swift to make an app that deletes a photo from the user's camera roll. I know how to do this normally with Swift 1.2, but I can't seem to get it in Swift 2.0. I tried searching through the documentation to learn how to use the 'performChange' function in Swift 2.0, but it won't work. Here's my Swift :
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.deleteAssets(arrayToDelete)
}, completionHandler: { (success, error) -> Void in
NSLog("Finished deleting asset. %#", (success ? "Success" : error))
})
Here's my error:
Cannot invoke performChanges with an argument list of type (() -> _, completionHandler: (_, _) -> Void)
Any help is appreciated!!
The Swift compiler generally has issues with reporting the correct root cause of a type-checking failure in a complex expression. Rather than simply show you the correct form of this code, I'll walk through how I found the way there, so you can reuse that process for future debugging. (Skip down for the TLDR if you must.)
First, you've got an error of the form:
Cannot invoke 'function' with an argument list of type 'params'
That means that something about your function call has failed to type-check. Because you're calling a function where the parameters include closures, you'll need to look at the behavior of the closures to narrow down the type-checking issues.
Let's start by making the closures explicitly return Void:
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.deleteAssets(arrayToDelete)
return
}, completionHandler: { (success, error) -> Void in
NSLog("Finished deleting asset. %#", (success ? "Success" : error))
return
})
What's going on here? You've already declared the type of the completion handler as returning Void, so why the extra return statement? Swift type checking works both bottom-up and top-down, and if there's an error along either way, it can't make assumptions about the other. Here, you have two single-expression closure, so Swift has to consider that each one's single expression could be an implicit return statement.
If one of those statements has a type-checking error, that statement's return type becomes <<error type>>, and because it's a single-statement closure, the closure's return type becomes <<error type>>, and therefore the function call to which the closure is a parameter fails, because the function is expecting a closure that returns Void, not a closure that returns <<error type>>.
Indeed that's what's happening — once we make the above change, we get a different error, on the NSLog statement (with "Success" highlighted):
'_' is not convertible to 'StringLiteralConvertible'
That's a bit unclear still, but we're closer to the root of the problem. If you replace the (success ? "Success" : error) part of the log statement with something static (say, just "Success"), it compiles. So, let's separate and dissect that ternary operation to see what's going wrong inside it.
let successString = "Success"
let report = (success ? successString : error)
NSLog("Finished deleting asset. %#", report)
This gets us a new error, on the ? of the ternary operator:
'NSString' is not a subtype of 'NSError'
Huh? Something to do with automatic conversion of Swift.String to NSString, maybe? Let's make that conversion explicit to be sure:
let successString = "Success" as NSString
let report = (success ? successString : error)
type of expression is ambiguous without more context
Now we reach the crux of the matter. What, indeed, is the type of report supposed to be? If success is true, it's NSString, but if false, it's NSError? (the type of error, inferred from the declaration of performChanges(_:completionHandler:)). This sort of type hand-waviness will fly in C, but Swift is much more strict about such things. (Someone very wise once said, "Incomplete type specification leads to unclear memory layout, unclear memory layout leads to undefined behavior, undefined behavior leads to suffering." Or something like that.)
The only supertype of both NSString and NSError? is Any, and Swift is reluctant to infer that type. (Because if you infer that everything can be anything, your type information is worthless.) And if you try to use that type manually, you get errors when you try to pass it to NSLog:
let report: Any = (success ? successString : error)
NSLog("Finished deleting asset. %#", report)
cannot invoke 'NSLog' with an argument list of type '(String, Any)'
expected an argument list of type '(String, [CVarArgType])'
Those errors take us off down the rabbit hole of C vararg functions, so let's step back a bit — what type does NSLog really want for this argument? NSLog is an ObjC function, with format string substitution (the %# business) built on NSString stringWithFormat. Per the docs, when you use a %# token, NSString looks for an object in the corresponding parameter and calls its description method. Because that implementation is ObjC and part of the Cocoa frameworks (dating back to before the dinosaurs were wiped out), not a Swift thing, it stands to reason that a pure-Swift type like Any won't work here.
NSObject would be a good Cocoa type to pass to the NSLog function. However, declaring that as the type of report won't fly, either — you can't implicitly convert both branches of the ternary operator to NSObject. The error parameter is an optional — its inferred type is NSError?, remember? And that's a Swift type, not a Cocoa type.
So that's the final problem — you have a ternary operator that's trying to have one branch be a perfectly sensible object, and the other branch a still-wrapped optional. Indeed, force-unwrapping the optional clears all the compiler errors:
let report = (success ? successString : error!) // report now type-infers NSObject
NSLog("Finished deleting asset. %#", report)
Now that we've fixed everything, we can put the wheels back on and collapse everything back down...
TLDR: Unwrap your optionals.
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.deleteAssets(arrayToDelete)
}, completionHandler: { success, error in
NSLog("Finished deleting asset. %#", (success ? "Success" : error!))
})
We know it's safe to force-unwrap here because of the API contract: if success is true, error will be nil, but if success is false, there will actually be an error.
(This may have even worked for you without unwrapping on previous SDK versions because the closure type in the performChanges(_:completionHandler:) would have used an implicitly-unwrapped optional before Apple audited all their APIs for nullability.)
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.