Swift callAsFunction

Apr 17, 2023#swift

The callAsFunction method is a special method that allows you to call an instance of a type as if it were a function. For example, you can define a struct with a callAsFunction method that takes some arguments and returns a value:

struct Adder {
    var base: Int

    func callAsFunction(_ x: Int) -> Int {
        return x + base
    }
}

let add3 = Adder(base: 3)
let result = add3(10) // result is 13

This is equivalent to calling the callAsFunction method explicitly:

let result = add3.callAsFunction(10) // result is 13

Create custom operators in Swift

You can use callAsFunction to implement custom operators in Swift. For example, you can create a struct that represents a matrix and define a callAsFunction method that performs matrix multiplication:

struct Matrix {
    var elements: [[Double]]

    func callAsFunction(_ other: Matrix) -> Matrix {
        // Perform matrix multiplication and return the result
    }
}

let A = Matrix(elements: [[1, 2], [3, 4]])
let B = Matrix(elements: [[5, 6], [7, 8]])
let C = A(B) // C is the matrix product of A and B

Create custom view modifiers in SwiftUI

The callAsFunction method can be useful for creating callable objects that encapsulate some logic or state. For example, you can use it to create a custom view modifier in SwiftUI that takes a closure as an argument:

struct OnTap: ViewModifier {
    var action: () -> Void

    func body(content: Content) -> some View {
        content.onTapGesture(perform: action)
    }

    func callAsFunction(_ action: @escaping () -> Void) -> OnTap {
        OnTap(action: action)
    }
}

let onTap = OnTap()

Text("Hello")
    .modifier(onTap {
        print("Tapped")
    })

This is equivalent to calling the modifier method with an instance of OnTap:

Text("Hello")
    .modifier(OnTap {
        print("Tapped")
    })

Environment values in SwiftUI

One common SwiftUI environment value that uses callAsFunction is DismissAction. This is a structure that you can use to dismiss a view that is presented modally, such as a sheet or a full screen cover.

@Environment(\.dismiss) private var dismiss

Then you can call the dismiss value as a function to dismiss the view:

Button("Done") {
    dismiss() // Implicitly calls dismiss.callAsFunction()
}

This is equivalent to calling the callAsFunction method explicitly:

Button("Done") {
    dismiss.callAsFunction()
}

Another common SwiftUI environment value that uses callAsFunction is OpenURLAction. This is a structure that you can use to open a URL, following system conventions.

@Environment(\.openURL) private var openURL

Then you can call the openURL value as a function with a URL argument to open it:

Button("Visit Website") {
    if let url = URL(string: "https://www.apple.com") {
        openURL(url) // Implicitly calls openURL.callAsFunction(url)
    }
}

This is equivalent to calling the callAsFunction method explicitly:

Button("Visit Website") {
    if let url = URL(string: "https://www.apple.com") {
        openURL.callAsFunction(url)
    }
}

These are some examples of how SwiftUI uses callAsFunction to simplify the syntax for calling environment values as actions.