Making a CompletionHandler Safer in Swift

Making a CompletionHandler Safer in Swift

Asynchronous programming in Swift often involves using completionHandler within functions to handle events. While this approach is common, there are potential pitfalls that developers need to be mindful of. One such pitfall is that there is no guarantee all code paths will eventually call the completionHandler. This means if we forget to call it, the code will still build successfully, leading to unexpected behaviors or bugs.

To create safer code that ensures the completionHandler is always called, we can follow a simple two-step process:

  1. Declare a constant using the let keyword to store the result.

  2. Use a defer statement to call the completionHandler.

The defer statement is a powerful tool in Swift. It ensures that a block of code is executed when the current scope exits, which in this case is just before the function returns. This guarantees that the completionHandler will always be called, even if an error occurs during the function's execution.

Here's an example of how you can implement this in your code:

import Foundation

func fetchData(url: URL, _ completion: @escaping (Result<Data, Error>) -> Void) {
    URLSession.shared.dataTask(with: url) { data, _, error in
        let result: Result<Data, Error>

        defer {
            completion(result)
        }

        guard error == nil else {
            result = .failure(error!)
            return
        }

        guard let data else {
            result = .failure(NetworkError.noData)
            return
        }

        result = .success(data)
    }
    .resume()
}

In the above code, we first declare a constant result to store the outcome of our network call. We then use a defer statement to call the completionHandler with the result just before the function exits. This ensures that regardless of the path our code takes, our completionHandler is always called.

By incorporating these steps, we can create a safer and more reliable asynchronous code that guarantees our completionHandler is always executed.


Thanks for Reading! ✌️

If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account Pham Trung Huy.

Happy coding 🍻