Swift callAsFunction

Apr 17, 2023#swift#ios

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.