In a previous tutorial, you delved into the fundamentals of Swift closures, understanding their syntax, and exploring their practical applications. Building upon that knowledge, let’s explore the powerful realm of closures with multiple parameters.
Understanding Closures
According to Swift’s Official Documentation, closures are functional blocks in Swift that can be assigned to variables, passed as parameters, and returned from functions. They capture and store references to variables and constants from the context where they are defined, enabling them to access and manipulate these values even after the original scope has concluded.
To explore Swift closures more thoroughly, understand their structure, and see practical examples, check out the Swift Closure: A Detailed Guide For Beginners tutorial.
Closures with Multiple Parameters in Swift
Closures, just like normal functions, can take multiple parameters, enabling developers to create flexible and dynamic pieces of code. Let’s take a look at an example:
// Example of a closure with multiple parameters
let multiply: (Int, Int) -> Int = { (a, b) in
return a * b
}
// Calling the closure
let result = multiply(5, 3)
print("Multiplication result: \(result)")
In this example, multiply is a closure that takes two Int parameters and returns their product.
This code example is neat, but I know what you’re thinking, “I could just write a function named multiply, accept multiple parameters and call it a day”, and you’re right!
The strength of Swift closures is their flexibility. You can write a function that alters or performs actions on its parameters without knowing the specific operation in advance. You declare the closure signature to pass as a parameter, and within the function, you call the closure with the defined parameters. This way, the closure executes the code you specified for that particular case, allowing future improvements or complete changes to the code.
Let’s implement a function called forEachElement that accepts an array of Int values, and a closure that would perform an action on each element on the array, so you can see closures in action displaying their full potential.
// Function that performs an action on each element of an array
func forEachElement(on numbers: [Int], perform action: (Int) -> Void) -> Void {
// Loop through each number in the array
for number in numbers {
// Call the provided closure (action) with the current number as an argument
action(number)
}
}
// Example 1: Print each number multiplied by 2
forEachElement(on: [1, 2, 3, 4], perform: { number in
print(number * 2)
})
// Example 2: Print whether each number is even or odd
forEachElement(on: [1, 2, 3, 4], perform: { number in
print(number % 2 == 0)
})
// Example 3: Print each number as is
forEachElement(on: [1, 2, 3, 4], perform: { number in
print(number)
})
Defining a closure as an argument for the forEachElement function adds versatility to its functionality. By allowing you to specify different actions within the closure, you can customize how each element in the array is processed. Whether it’s multiplying numbers, checking for evenness, or simply printing the elements, this flexibility empowers you to adapt the function’s behavior based on your specific needs, making your code more adaptable and reusable.
The forEachElement function you just declared is like a mini-version of Swift’s built-in forEach method for arrays. It simplifies the process of iterating through an array, allowing you to apply a specific action to each element using a provided closure. It’s a neat encapsulation of array iteration, reminiscent of the built-in forEach method.
Improving Closures Readability in Swift
When working with closures in Swift, readability is crucial for better code comprehension. Let’s take the forEachElement function you and improve upon its readability with two different approaches that can be combined.
Closure’s Trailing Syntax in Swift
Swift provides a convenient syntax for trailing closures, making your code cleaner and more expressive. Since the action parameter is a closure, and it also is at the end of the parameter list for the forEachElement function, you can leverage trailing closure syntax to improve the overall readability of your code. This syntax allows you to move the closure outside the parentheses, enhancing the clarity of your function calls.
// Using closure trailing syntax for better readability
forEachElement(on: [1, 2, 3, 4]) { number in
print(number * 2)
}
Closure’s Shorthand Parameter Name in Swift
In Swift, you can simplify closure parameters using shorthand names, marked by symbols like $0. In the forEachElement function, $0 represents the closure’s single parameter. This concise syntax proves handy, especially when the closure performs basic operations on each element. The $0 notation is a convention, and you can use $1, $2, and so on for multiple parameters if needed. This approach enhances code readability, making your Swift code more straightforward and efficient.
// Using closure shorthand parameter name $0 to double each element in the array
forEachElement(on: [1, 2, 3, 4]) { print($0 * 2) }
Practical Example: UIKit and Variadic Parameters
Now, let’s apply closures with multiple parameters in a UIKit scenario. Consider a function that sets up a gradient background for a UIViewController using variadic parameters (you should remember it, it’s a variation of the one you reviewed on the Create a Function in Swift).
import UIKit
extension UIViewController {
// Function to set up a gradient background using multiple UIColor parameters
func setupGradientBackground(colors: UIColor...) {
let gradientLayer = CAGradientLayer()
// Convert UIColors to CGColors using closure and map
gradientLayer.colors = colors.map { $0.cgColor }
// No specific locations set, allowing even distribution
gradientLayer.locations = nil
// Set the frame of the gradient layer to match the view controller's frame
gradientLayer.frame = view.frame
// Add the gradient layer as a sublayer to the view's layer
view.layer.addSublayer(gradientLayer)
}
}
// Example of using the setupGradientBackground function with multiple UIColor parameters
let viewController = UIViewController()
viewController.setupGradientBackground(colors: UIColor.green, UIColor.yellow, UIColor.orange)
This code extends the functionality of UIViewController with a method called setupGradientBackground. The method takes multiple UIColor as variadic parameters, converts them to CGColors using a closure, and the map function (an array method that iterates over all the elements of the array and returns a new array based on the operations you performed on each one), sets up a gradient layer and adds it to the view’s layer.
The example at the end demonstrates how to use this method with various UIColors to create a gradient background.

Conclusion
In conclusion, using closures with multiple parameters in Swift empowers developers to craft more dynamic and adaptable code. The ability to pass and manipulate various parameters within closures enhances the flexibility and functionality of your Swift applications.
To further enhance your Swift programming skills, check out Swift Code Examples page.