Even though Swift isn't a functional programming language, it has first-class functions. What are first-class functions and what does that mean for the Swift programming language? Don't let yourself be thrown off by the term functional programming. The concepts we cover in this post are not difficult to grasp.

What Are First-Class Functions?

Swift has first-class functions, which means that:

  • functions can be stored in constants and variables
  • functions can be passed as arguments to other functions
  • functions can be returned by other functions

Functions are first-class citizens in Swift, which opens up a lot of possibilities. Let me show you what that means.

Storing Functions in Variables and Constants

Because functions are first-class citizens in Swift, it is possible to store a function in a variable or constant. Let's take a look at an example. The SessionManager class declares a property with name didSignOut. The property is of an optional type. The type of the property is a function that accepts no arguments and returns Void.

The destroySession() method invokes the function the didSignOut property references. It uses optional chaining to safely access the value of the didSignOut property.

internal final class SessionManager {

    // MARK: - Properties

    var didSignOut: (() -> Void)?

    // MARK: - Helper Methods

    private func destroySession() {
        didSignOut?()
    }

}

In this example, we set the value of the didSignOut property of a SessionManager instance by assigning a closure to it.

// Configure Session Manager
sessionManager.didSignOut = { [weak self] in
    self?.showSignInView()
}

This is starting to get confusing. You are probably wondering by now what the difference is between a closure and a function? Don't worry. The difference is quite simple.

A closure is a function. We use the term closure if the function captures constants and variables from the surrounding context in which it is defined. The closure we assign to the didSignOut property of the session manager, captures a reference to self. That is why we use the term closure.

Passing a Function As an Argument

We sometimes take it for granted how straightforward it is to perform animations using the UIView class. This example should look familiar. We call animate(withDuration:animations:) on UIView to animate the alpha property of likeButton.

// Hide Like Button
UIView.animate(withDuration: 1.0) {
    self.likeButton.alpha = 0.0
}

The first argument is the duration of the animation and the second argument is a closure. Passing a closure as the second argument of the animate(withDuration:animations:) method is only possible because functions are first-class citizens in Swift. If this sounds confusing, then remember that closures are functions.

Returning a Function From a Function

Returning a function from a function may sound odd and it sort of is if you consider the syntax. Let's take a look at an example. Don't let yourself be thrown off by the syntax. This is without a doubt the most complex code snippet of this episode. Bear with me. Let's break the function definition up into smaller pieces that make more sense.

func increment(by increment: Int) -> (Int) -> Int {
    { (value) in value + increment }
}

We define a function increment(by:) that accepts an argument of type Int. The return type of the increment(by:) function is a function type. The function that is returned accepts an argument of type Int and returns a value of type Int. That is why the return arrow appears twice in the function definition. We can make this more explicit by wrapping the return type in parentheses.

func increment(by increment: Int) -> ((Int) -> Int) {
    { (value) in value + increment }
}

The body of the increment(by:) function isn't that complex. We create a closure that accepts one argument and returns a value of type Int. We could make this more explicit like this.

func increment(by increment: Int) -> ((Int) -> Int) {
    { (value) -> Int in
        value + increment
    }
}

Let's put the increment(by:) function to use. We invoke the increment(by:) function, passing it a value, 5. The function the increment(by:) function returns is stored in a constant with name incrementByFive.

func increment(by increment: Int) -> ((Int) -> Int) {
    { (value) -> Int in
        value + increment
    }
}

let incrementByFive = increment(by: 5)

As we explored earlier, first-class functions can be stored in variables and constants. That is what is happening here. The incrementByFive constant holds a reference to a function. This means that we can call the function stored in incrementByFive.

incrementByFive(2) // 7