Weekly Swift articles, podcasts and tips by John Sundell.

Swift clip: First class functions

Published on 16 Jan 2020

Welcome to Swift Clips — a new series of shorter videos showcasing interesting and useful Swift tips and techniques. In this first episode we’ll take a look at first class functions, which is a language feature that enables us to use functions in really powerful ways.

Sample code

Adding a series of views as subviews of another view:

let view: UIView = ...
let subviews: [UIView] = [button, label, imageView]

// Using a closure
subviews.forEach { subview in
    view.addSubview(subview)
}

// Using first class functions
subviews.forEach(view.addSubview)

Converting an array of strings into URL values:

let strings = ["swiftbysundell.com", "apple.com"]

// Using a closure
let urls = strings.compactMap { string in
    URL(string: string)
}

// Using first class functions
let urls = strings.compactMap(URL.init)

Sorting an array of Int values in descending order:

let scores: [Int] = [9, 20, 2, 1, 5]

// Using a closure
let highScores = scores.sorted(by: { $0 > $1 })

// Using first class functions
let highScores = scores.sorted(by: >)

Applying a value to a closure, to avoid the classic “weak self dance”:

// A view controller that currently captures 'self' weakly, in
// order to call a method on a 'productManager' property object:
class ProductViewController: UIViewController {
    ...

    override func viewDidLoad() {
        super.viewDidLoad()

        buyButton.handler = { [weak self] in
            guard let self = self else {
                return
            }

            self.productManager.startCheckout(for: self.product)
        }
    }
}

// Introducing a 'combine' function for applying a value to
// any function or closure:
func combine<A, B>(
    _ value: A,
    with closure: @escaping (A) -> B
) -> () -> B {
    return { closure(value) }
}

// Using our new combine function:
class ProductViewController: UIViewController {
    ...

    override func viewDidLoad() {
        super.viewDidLoad()
        
        buyButton.handler = combine(product,
            with: productManager.startCheckout
        )
    }
}