Reputation: 5230
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([])
}
}
}
_ completionHandler
argument in fetchOrders
mean?(orders: () throws -> [Order])
mean? PS : I am new to iOS
and Swift
Upvotes: 9
Views: 2611
Reputation: 299595
There's quite a lot in here, so we'll break it down one piece at a time:
func fetchOrders(_ completionHandler: (_ orders: [Order]) -> Void)
fetchOrders
.completionHandler
) and returns nothing._
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.)completionHandler
is the "internal name," what the parameter is called inside the function.completionHandler
is (_ orders: [Order]) -> Void
. We'll break that down now.
[Order]
(array of Order
) and returns Void
. Informally this means "returns nothing" but literally means it returns the empty tuple ()
._ 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.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
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.{ orders in
, but then the reader would probably have been surprised by this somewhat unusual code.
orders
that takes nothing and returns [Order]
or throws an error. Basically this is a way to say that fetchOrders
might fail.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([])
}
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.
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.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.)
Upvotes: 11
Reputation: 16278
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:
Order
as a parameter and acts as a callback.Upvotes: 2
Reputation: 56727
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.
Upvotes: 2