Anonymous functions in Swift iOS 27.08.2018

An anonymous function is a function definition that is not bound to an identifier. Consider this example:

func whatToAnimate() { 
    self.myButton.frame.origin.y += 20
}

func whatToDoLater(finished:Bool) {
    print("finished: \(finished)")
}

UIView.animate(withDuration:0.4, 
    animations: whatToAnimate, completion: whatToDoLater)

There’s a slight bit of ugliness in that code. I’m declaring functions whatToAnimate and whatToDoLater, just because I want to pass those functions in the last line. But I don’t really need the names whatToAnimate and whatToDoLater for anything, except to refer to them in the last line; neither the names nor the functions will ever be used again. In my call to UIView.animate(withDuration:animations:completion:), it would be nice to be able to pass just the body of those functions without a declared name.

That’s called an anonymous function, and it’s legal and common in Swift. To form an anonymous function, you do two things:

  1. Create the function body itself, including the surrounding curly braces, but with no function declaration.
  2. If necessary, express the function’s parameter list and return type as the first thing inside the curly braces, followed by the keyword in.

Let’s practice by transforming our named function declarations into anonymous functions. Here’s the named function declaration for whatToAnimate:

func whatToAnimate() {
    self.myButton.frame.origin.y += 20
}

Here’s an anonymous function that does the same thing. Notice how I’ve moved the parameter list and return type inside the curly braces:

{
    () -> () in
    self.myButton.frame.origin.y += 20
}

Here’s the named function declaration for whatToDoLater:

func whatToDoLater(finished:Bool) {
    print("finished: \(finished)")
}

Here’s an anonymous function that does the same thing:

{
    (finished:Bool) -> () in
    print("finished: \(finished)")
}

Now that we know how to make anonymous functions, let’s use them. The point where we need the functions is the point where we’re passing the second and third arguments to animate(withDuration:animations:completion:). We can create and pass anonymous functions right at that point, like this:

UIView.animate(withDuration:0.4,
    animations: {
        () -> () in
        self.myButton.frame.origin.y += 20
    },
    completion: {
        (finished:Bool) -> () in
        print("finished: \(finished)")
    }
)

Anonymous functions are very commonly used in Swift, so make sure you can read and write that code! Anonymous functions, in fact, are so common and so important, that some shortcuts for writing them are provided.

Omission of the return type. If the anonymous function’s return type is already known to the compiler, you can omit the arrow operator and the specification of the return type:

UIView.animate(withDuration:0.4,
    animations: {
        () in 
        self.myButton.frame.origin.y += 20
    }, 
    completion: {
        (finished:Bool) in 
        print("finished: \(finished)")
    }
)

Omission of the in expression when there are no parameters. If the anonymous function takes no parameters, and if the return type can be omitted, the in expression itself can be omitted:

UIView.animate(withDuration:0.4,
    animations: { 
        self.myButton.frame.origin.y += 20
    }, 
    completion: {
        (finished:Bool) in
        print("finished: \(finished)")
    }
)

Omission of the parameter types. If the anonymous function takes parameters and their types are already known to the compiler, the types can be omitted:

UIView.animate(withDuration:0.4,
    animations: {
        self.myButton.frame.origin.y += 20
    }, 
    completion: {
        (finished) in
        print("finished: \(finished)")
    }
)

Omission of the parentheses. If the parameter types are omitted, the parentheses around the parameter list can be omitted:

UIView.animate(withDuration:0.4,
    animations: {
        self.myButton.frame.origin.y += 20
    }, 
    completion: {
        finished in 
        print("finished: \(finished)")
    }
)

Omission of the in expression even when there are parameters. If the return type can be omitted, and if the parameter types are already known to the compiler, you can omit the in expression and refer to the parameters directly within the body of the anonymous function by using the magic names $0 , $1 , and so on, in order:

UIView.animate(withDuration:0.4,
    animations: {
        self.myButton.frame.origin.y += 20
    }, 
    completion: {
        print("finished: \($0)") 
    }
)

Omission of the parameter names. If the anonymous function body doesn’t need to refer to a parameter, you can substitute an underscore for its name in the parameter list in the in expression:

UIView.animate(withDuration:0.4,
    animations: {
        self.myButton.frame.origin.y += 20
    }, 
    completion: {
        _ in 
        print("finished!")
    }
)

But note that if the anonymous function takes parameters, you must acknowledge them somehow. You can omit the in expression and use the parameters by the magic names $0 and so on, or you can keep the inexpression and ignore the parameters with an underscore, but you can’t omit the in expression altogether and not use the parameters by their magic names! If you do, your code won’t compile.

Omission of the function argument label. If, as will just about always be the case, your anonymous function is the last argument being passed in this function call, you can close the function call with a right parenthesis before this last argument, and then put just the anonymous function body without a label (this is called a trailing function):

UIView.animate(withDuration:0.4,
    animations: {
        self.myButton.frame.origin.y += 20
    }) { 
        _ in
        print("finished!")
    }

Omission of the calling function parentheses. If you use the trailing function syntax, and if the function you are calling takes no parameters other than the function you are passing to it, you can omit the empty parentheses from the call. This is the only situation in which you can omit the parentheses from a function call! To illustrate, I’ll declare and call a different function:

func doThis(_ f:() -> ()) {
    f()
}
doThis { // no parentheses!
    print("Howdy")
}    

Omission of the keyword return. If the anonymous function body consists of exactly one statement and that statement consists of returning a value with the keyword return , the keyword return can be omitted. To put it another way, in a context that expects a function that returns a value, if an anonymous function body consists of exactly one expression with no return , Swift assumes that this expression’s value is to be returned from the anonymous function:

func greeting() -> String {
    return "Howdy"
}

func performAndPrint(_ f:()->String) {
    let s = f()
    print(s)
}

performAndPrint {
    greeting() // meaning: return greeting()
}    

When writing anonymous functions, you will frequently find yourself taking advantage of all the omissions you are permitted. In addition, you’ll often shorten the layout of the code (though not the code itself) by putting the whole anonymous function together with the function call on one line. Thus, Swift code involving anonymous functions can be extremely compact.

Here’s a typical example. We start with an array of Int values and generate a new array consisting of all those values multiplied by 2, by calling the map(_:) instance method. The map(_:) method of an array takes a function that takes one parameter of the same type as the array’s elements, and returns a new value; here, our array is made of Int values, and we are passing to the map(_:) method a function that takes one Int parameter and returns an Int. We could write out the whole function, like this:

let arr = [2, 4, 6, 8]
func doubleMe(i:Int) -> Int {
    return i*2
}
let arr2 = arr.map(doubleMe)

That, however, is not very Swifty. We don’t need the name doubleMe for anything else, so this may as well be an anonymous function:

let arr = [2, 4, 6, 8]
let arr2 = arr.map ({
    (i:Int) -> Int in
    return i*2
})

Fine, but now let’s start shortening our anonymous function. Its parameter type is known in advance, so we don’t need to specify that. Its return type is known by inspection of the function body, so we don’t need to specify that. There’s just one parameter and we are going to use it, so we don’t need the in expression as long we refer to the parameter as $0. Our function body consists of just one statement, and it is a return statement, so we can omit return. And map(_:) doesn’t take any other parameters, so we can omit the parentheses and follow the name directly with a trailing function:

let arr = [2, 4, 6, 8]
let arr2 = arr.map {$0*2}

It doesn’t get any Swiftier than that!