Top Swift Interview Questions for 2025

author image Hirely
at 09 Jan, 2025

Question: What is the difference between map, filter, and reduce in Swift?

Answer:

In Swift, map, filter, and reduce are powerful higher-order functions that operate on collections (such as arrays, sets, etc.) and allow you to transform, filter, or combine values in a concise and functional programming style. These functions are commonly used to manipulate data in a functional programming way.

1. map:

  • Purpose: Transforms each element of a collection using a closure and returns a new collection containing the transformed elements.
  • Operation: Applies the transformation to every element of the original collection, and returns a new collection of the same size with the transformed values.

Example:

let numbers = [1, 2, 3, 4, 5]
let doubledNumbers = numbers.map { $0 * 2 }
print(doubledNumbers)  // Output: [2, 4, 6, 8, 10]

In this example:

  • The map function takes a closure that doubles each element in the array numbers.
  • It returns a new array [2, 4, 6, 8, 10].

Key Points:

  • Input: A collection (e.g., array) of elements.
  • Output: A new collection of the same size, with each element transformed.
  • Use Case: When you need to transform each element of a collection into a new form.

2. filter:

  • Purpose: Filters elements from a collection based on a given condition (closure) and returns a new collection containing only the elements that satisfy the condition.
  • Operation: Tests each element with a closure. Only the elements that evaluate to true are included in the returned collection.

Example:

let numbers = [1, 2, 3, 4, 5, 6]
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers)  // Output: [2, 4, 6]

In this example:

  • The filter function filters the array numbers by keeping only the even numbers.
  • It returns the array [2, 4, 6].

Key Points:

  • Input: A collection (e.g., array) of elements.
  • Output: A new collection containing only the elements that satisfy the condition (i.e., the elements for which the closure returns true).
  • Use Case: When you need to extract a subset of elements from a collection based on some condition.

3. reduce:

  • Purpose: Combines all the elements of a collection into a single value by applying a closure that accumulates a result.
  • Operation: Takes two parameters in its closure: an accumulator (which stores the result of previous operations) and the current element. It performs the operation and returns a final accumulated result.

Example:

let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0) { $0 + $1 }
print(sum)  // Output: 15

In this example:

  • The reduce function starts with an initial value of 0 and then adds each element of the array numbers to the accumulator.
  • It returns the sum 15 (i.e., 0 + 1 + 2 + 3 + 4 + 5).

Key Points:

  • Input: A collection (e.g., array) and an initial starting value (the accumulator).
  • Output: A single value, which is the result of combining all the elements of the collection using the closure.
  • Use Case: When you need to combine or aggregate all elements of a collection into a single value (e.g., summing numbers, concatenating strings).

Summary of Differences:

FunctionPurposeInputOutputUse Case
mapTransforms each element of the collectionCollection of elementsA new collection of transformed elementsWhen you need to apply a transformation to each element
filterFilters elements based on a conditionCollection of elementsA new collection containing only the elements that satisfy the conditionWhen you need to select a subset of elements based on a condition
reduceCombines all elements into a single valueCollection of elements and an initial accumulator valueA single valueWhen you need to accumulate a single result (e.g., sum, product, concatenation)

Examples with Multiple Functions:

map, filter, and reduce Together:

You can combine these functions to perform more complex operations. For example, suppose you want to double all even numbers in an array and then calculate their sum:

let numbers = [1, 2, 3, 4, 5, 6]

// 1. Filter the even numbers
let evenNumbers = numbers.filter { $0 % 2 == 0 }

// 2. Double each even number
let doubledEvenNumbers = evenNumbers.map { $0 * 2 }

// 3. Calculate the sum of doubled even numbers
let sum = doubledEvenNumbers.reduce(0) { $0 + $1 }

print(sum)  // Output: 24 (because 2*2 + 4*2 + 6*2 = 24)

In this example:

  1. filter selects only the even numbers ([2, 4, 6]).
  2. map doubles the even numbers ([4, 8, 12]).
  3. reduce calculates the sum of the doubled numbers (4 + 8 + 12 = 24).

Conclusion:

  • map: Transforms each element in a collection.
  • filter: Selects elements based on a condition.
  • reduce: Combines elements into a single accumulated value.

These functions are essential tools in Swift’s functional programming toolkit and are highly useful for processing and manipulating collections.

Question: How is the @escaping attribute used in Swift?

Answer:

In Swift, the @escaping attribute is used to indicate that a closure passed as a function parameter can escape the scope of the function it is passed into. In simpler terms, an escaping closure is a closure that is executed after the function that takes it as a parameter has returned, typically because it is stored or called asynchronously.

The primary difference between escaping and non-escaping closures is how and when the closure is executed in relation to the lifetime of the function.

Key Concepts:

  1. Non-escaping closures:

    • These closures are executed within the function they are passed into, before the function returns.
    • Swift assumes closures are non-escaping by default.
    • Non-escaping closures cannot be stored or used outside of the function’s scope.
  2. Escaping closures:

    • These closures escape the function they are passed into and may be executed after the function returns.
    • An escaping closure can be stored in a variable or used asynchronously (e.g., in completion handlers or dispatched tasks).
    • You need to explicitly mark a closure as @escaping if you intend to use it in such a manner.

Syntax of @escaping:

The @escaping attribute is applied to a closure parameter in a function declaration. You typically use it when you’re passing closures as completion handlers or performing asynchronous operations.

func performAsyncTask(completion: @escaping () -> Void) {
    DispatchQueue.global().async {
        // Simulate an asynchronous task
        sleep(2)
        completion()  // The closure is called after the function returns
    }
}

In this example:

  • The closure passed to the performAsyncTask function is marked as @escaping because it will be called after the performAsyncTask function finishes executing.
  • The closure is stored in the dispatch queue and executed asynchronously, meaning it “escapes” the scope of the performAsyncTask function.

Why is @escaping necessary?

  • Swift is designed to prevent memory management issues, such as retain cycles, and ensures that closures are captured properly.
  • When a closure escapes, Swift cannot guarantee that the closure will be executed immediately or that it will not outlive the function’s scope.
  • By marking the closure as @escaping, you are explicitly indicating that the closure will outlive the function call, and Swift will allow it to be stored or executed later.

Example with @escaping:

class TaskManager {
    var tasks = [() -> Void]()

    func addTask(task: @escaping () -> Void) {
        tasks.append(task)  // Store the closure for later use
    }
    
    func runTasks() {
        for task in tasks {
            task()  // Execute the stored closures
        }
    }
}

let manager = TaskManager()
manager.addTask {
    print("Task 1 executed")
}
manager.addTask {
    print("Task 2 executed")
}

manager.runTasks()
// Output:
// Task 1 executed
// Task 2 executed

In this example:

  • The addTask method accepts an @escaping closure and stores it in an array.
  • Later, the runTasks method executes all the stored tasks. The closures “escape” the scope of the addTask method and are executed later, which is why they need to be marked as @escaping.

Key Points:

  1. Escaping closures are closures that can be executed after the function that takes them as a parameter has returned.
  2. Non-escaping closures are closures that are executed during the lifetime of the function.
  3. The @escaping attribute is required when a closure is stored or used asynchronously, ensuring proper memory management.
  4. When a closure is marked @escaping, it may capture references to variables from its surrounding context, which can lead to retain cycles if not managed carefully.

When to Use @escaping:

  • Asynchronous operations (e.g., when using DispatchQueue, network requests, etc.)
  • Completion handlers that are called after a function returns.
  • Storing closures in properties or variables for later execution.

Example: Using @escaping with an asynchronous network request:

func fetchDataFromServer(completion: @escaping (Data?, Error?) -> Void) {
    // Simulate network request
    DispatchQueue.global().async {
        // Simulating a delay
        sleep(2)
        let data = Data()  // Fake data
        completion(data, nil)  // The closure escapes the scope and is called after the function returns
    }
}

fetchDataFromServer { data, error in
    if let data = data {
        print("Data received: \(data)")
    } else if let error = error {
        print("Error: \(error)")
    }
}

In this example:

  • The closure passed to fetchDataFromServer is marked @escaping because it is called asynchronously after the network operation completes, which occurs outside the scope of the function.

Conclusion:

  • Escaping closures are closures that outlive the function they are passed into. They are commonly used for asynchronous operations, callbacks, and completion handlers.
  • Swift requires you to mark such closures with @escaping to ensure proper memory management and avoid unexpected behavior.

Read More

If you can’t get enough from this article, Aihirely has plenty more related information, such as Swift interview questions, Swift interview experiences, and details about various Swift job positions. Click here to check it out.

Related Posts

Trace Job opportunities

Hirely, your exclusive interview companion, empowers your competence and facilitates your interviews.

Get Started Now