Closures in Swift: A Comprehensive Guide with Code Examples

Closures are a powerful feature in Swift that allow you to create self-contained blocks of code that can be stored, passed around, and executed at a later time.

In this comprehensive guide, we explore closures in Swift, providing a thorough understanding of their syntax, usage, and practical applications.

Closures in Swift

An Example of closure usage in Swift

// Closure with no parameters and no return value
let simpleClosure: () -> Void = {
    print("This is a simple closure")

// Closure with parameters and return value
let multiplyClosure: (Int, Int) -> Int = { (a, b) in
    return a * b

// Closure as a parameter to a function
func performOperation(a: Int, b: Int, operation: (Int, Int) -> Int) {
    let result = operation(a, b)
    print("Result: \(result)")

// Calling closures and passing them as parameters
simpleClosure() // Output: "This is a simple closure"

let product = multiplyClosure(4, 5)
print("Product: \(product)") // Output: "Product: 20"

performOperation(a: 10, b: 3, operation: { (a, b) in
    return a % b
}) // Output: "Result: 1"

In the above code, we define two closures: simpleClosure and multiplyClosure. The simpleClosure takes no parameters and has no return value, simply printing a message. The multiplyClosure takes two integers as parameters and returns their product.

We then have a function performOperation that takes two integers and a closure as parameters. Inside the function, we call the closure with the provided integers and print the result.

Finally, we demonstrate calling the closures and passing them as parameters to the performOperation function. The simpleClosure is invoked directly, while the multiplyClosure is called with two integers, resulting in the product being printed. We also pass a closure as a parameter to performOperation to calculate the modulus and display the result.

More examples of closure usage in Swift:

// Closure as a property
struct MathOperations {
    var square: (Int) -> Int = { number in
        return number * number

let math = MathOperations()
let squaredNumber = math.square(5)
print("Squared Number: \(squaredNumber)") // Output: "Squared Number: 25"

// Closure capturing values from the surrounding context
func makeIncrementer(incrementAmount: Int) -> () -> Int {
    var total = 0

    let incrementer: () -> Int = {
        total += incrementAmount
        return total

    return incrementer

let incrementByTwo = makeIncrementer(incrementAmount: 2)
print(incrementByTwo()) // Output: 2
print(incrementByTwo()) // Output: 4

// Trailing closure syntax
func performOperation(a: Int, b: Int, operation: (Int, Int) -> Int) {
    let result = operation(a, b)
    print("Result: \(result)")

performOperation(a: 10, b: 3) { $0 % $1 } // Output: "Result: 1"

// Closure with capturing list
func makeCounter() -> () -> Int {
    var count = 0

    let counter: () -> Int = { [count] in
        return count

    return counter

let counter = makeCounter()
print(counter()) // Output: 0

// Escaping closure
var completionHandlers: [() -> Void] = []

func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {

func doSomething() {
    print("Doing something...")

    for handler in completionHandlers {

someFunctionWithEscapingClosure {
    print("Closure executed")

doSomething() // Output: "Doing something..." and "Closure executed"

These examples showcase different aspects of closures, including closures as properties, closures capturing values from the surrounding context, trailing closure syntax, capturing lists to avoid reference cycles, and escaping closures.

An Advanced example that demonstrates the usage of closures in a practical scenario:

import UIKit

// Closure-based network request
func makeNetworkRequest(completion: @escaping (Result<Data, Error>) -> Void) {
    // Simulating asynchronous network request {
        // Simulating success response
        let response = """
            "message": "Hello, World!"
        """.data(using: .utf8)!

        // Simulating error response
        // let error = NSError(domain: "", code: 500, userInfo: [NSLocalizedDescriptionKey: "Internal Server Error"])

        // Handling success or error based on the response
        if let error = nil {
        } else {

// Usage of the network request closure
makeNetworkRequest { result in
    switch result {
    case .success(let data):
        if let json = try? JSONSerialization.jsonObject(with: data, options: []),
           let dict = json as? [String: Any],
           let message = dict["message"] as? String {
            print("Response: \(message)") // Output: "Response: Hello, World!"
    case .failure(let error):
        print("Error: \(error.localizedDescription)")

// Closure-based animation
UIView.animate(withDuration: 0.5, animations: {
    // Perform animations here
    // For example, change the view's background color
    view.backgroundColor = .red
}) { _ in
    // Animation completion handler
    print("Animation completed")

In this example, we demonstrate two advanced use cases of closures. First, we have a closure-based network request using makeNetworkRequest. The closure takes a completion handler as a parameter, which is called asynchronously with either a success response containing data or a failure response containing an error. Depending on the response, the closure performs appropriate actions.

Next, we showcase a closure-based animation using UIView.animate. The closure specifies the animations to be performed, such as changing the background color of a view. Upon completion of the animation, the completion handler closure is called, where you can perform any desired actions.

These examples highlight how closures can be utilized in asynchronous operations and animation tasks, providing concise and flexible code execution.



Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *