Swift
programming
completion handler
function
iOS development

How could I create a function with a completion handler in Swift?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Creating a function with a completion handler in Swift is a fundamental programming concept, particularly when dealing with asynchronous code. A completion handler is essentially a closure that you pass as an argument to a function, which will be executed after the function has finished executing. This is particularly useful for tasks such as network requests, where you want to perform an action after the task completes.

Understanding Completion Handlers

In Swift, functions can take closures as parameters. A completion handler is a type of closure that usually returns Void and can carry out tasks once a particular function call is complete. Here’s an overview of a simple function with a completion handler:

swift
1func fetchData(completion: @escaping (String) -> Void) {
2    let data = "Important data"
3    // Simulate a network request or some asynchronous task
4    DispatchQueue.global().async {
5        // Perform some task
6        sleep(2) // Simulating delay
7        DispatchQueue.main.async {
8            completion(data)
9        }
10    }
11}
12
13fetchData { result in
14    print("Received data: \(result)")
15}

Key Components:

  1. Closure Parameter:
    • @escaping (String) -> Void indicates a closure that takes a String parameter and returns Void. The @escaping attribute signifies that the closure can outlive the function call's execution, which is often the case for asynchronous callbacks.
  2. Asynchronous Tasks:
    • The function uses DispatchQueue to execute the time-consuming task asynchronously, simulating what would happen in a network request.
  3. Executing the Completion Handler:
    • After the asynchronous task completes, the completion handler is called on the main queue. This is critical for ensuring UI updates are performed on the main thread.

Synchronous vs Asynchronous

Understanding the difference between synchronous and asynchronous operations is important when working with completion handlers.

  • Synchronous Operations: These run in a sequence. Each operation must finish before the next one starts.
  • Asynchronous Operations: These can operate independently without waiting for the previous operation to complete.

Completion handlers are particularly useful in the context of asynchronous operations, where you want to know when a task has finished even though your program may continue executing other tasks.

Error Handling with Completion Handlers

It's common to handle errors in functions utilizing completion handlers. Here’s an example:

swift
1enum DataError: Error {
2    case networkError
3}
4
5func fetchDataWithError(completion: @escaping (Result<String, DataError>) -> Void) {
6    // Simulate a network request
7    DispatchQueue.global().async {
8        let success = true // Assume we determine success or failure
9        DispatchQueue.main.async {
10            if success {
11                completion(.success("Fetched data successfully"))
12            } else {
13                completion(.failure(.networkError))
14            }
15        }
16    }
17}
18
19fetchDataWithError { result in
20    switch result {
21    case .success(let data):
22        print("Received data: \(data)")
23    case .failure(let error):
24        print("Error encountered: \(error)")
25    }
26}

Error Handling Key Points:

  • Result Type: Swift’s Result type is useful to encapsulate an operation's outcome, either successful with associated value or an error.
  • Enum for Errors: Using a custom enumeration helps organize potential error cases.

Practical Applications

  • Network Calls: Handle API responses asynchronously.
  • Disk I/O: Cache data or read files without blocking the main thread.
  • Animations: Execute code after an animation completes.

Table Summary of Completion Handlers

AspectDescription
DefinitionA closure passed to a function to execute later.
@escapingAttribute indicating closure execution may outlive the function context.
Use CasesAsynchronous operations like network calls, file I/O, etc.
Error HandlingUse Result type to handle success and failure states gracefully.
Key BenefitsEnhances code readability, aids in non-blocking UI updates, and simplifies error handling.

Conclusion

Implementing functions with completion handlers in Swift is a vital skill for modern app development, especially when dealing with asynchronous operations. By mastering this pattern, you enable your apps to perform complex operations efficiently while maintaining a responsive user interface. Integrating @escaping closure parameters, as well as utilizing Swift’s Result type for error handling, further enhances robustness and readability in your applications.


Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.