Completion Handlers in Swift: A Comprehensive Guide

Swift completion handlers

A completion handler in Swift is a function that calls back when a task completes.

This is why it is also called a callback function.

A callback function is passed as an argument into another function.

When this function completes running a task, it executes the callback function.

This guide teaches you everything you need to know about completion handlers in Swift.

Let’s dive in!

Example

Here is a demonstrative (but not useful) example of using a completion handler in Swift:

func greet(_ completion: () -> ()) {
    print("Hello world!")
    completion()
}

greet({
  print("Task finished.")
})

Output:

Hello world!
Task finished.
  • Here the greet() function takes a callback function, completion, as its argument. It takes no parameters and returns nothing, thus the type () -> ().
  • This means we need to pass a callback function as an argument to greet() function when calling it.
  • The greet() function calls the completion handler right after it has printed “Hello world!” into the console.
  • This executes the completion handler we pass into the greet() function call.

However, because this example is about synchronous tasks, using a callback function is not useful. You could have directly called the second print function after the first one:

func greet() {
    print("Hello world!")
    print("Task finished.")
}

greet()

Output:

Hello world!
Task finished.

In reality, callbacks are useful in Swift when dealing with asynchronous tasks.

For example, an HTTP request takes a while and we need to wait for the response. This is where callbacks help. When an HTTP request finishes, the callback runs a piece of code to show the response.

An HTTP Request and Completion Handlers in Swift

In an asynchronous function, you can start the next task before the previous one completes. This way, you can deal with multiple requests simultaneously and complete more tasks in a shorter timeframe.

In Swift, completion handlers are used to run code after a task has been completed. They are commonly used to expose the result of an asynchronous call back to the caller.

For instance, let’s make an HTTP request that retrieves the HTML content of StackOverflow’s front page. As this request takes a while, we need a callback function that shows the HTML content once the HTTP request completes.

To retrieve a web page in Swift, use URLSession.shared.dataTask() function. This function takes the URL object as its first argument and a completion handler function as a second one. As per docs, this completion handler function takes the following parameters:

  1. data—The data returned by the request.
  2. response—Response metadata object that includes the HTTP headers and status code.
  3. error—An error object that with information about why the request failed. It returns nil if the request was successful.

To show the HTML content, you need to define a completion handler function with this recipe. The URLSession.shared.dataTask() function then calls it with the data, response, and error it got from the URL.

Here is how it looks in code:

import Foundation

let url = URL(string: "http://www.stackoverflow.com")!

let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
    // Check if there is any HTML data
    guard let data = data else { return }

    // Make the HTML data readable
    let HTMLContent = String(data: data, encoding: .utf8)!

    // Show the HTML data
    print(HTMLContent)
})

task.resume()

Notice how the completion handler function is defined directly into the URLSession.shared.dataTask() call. However, you could also define a separate completion handler function too. But this is usually a more “unconventional” way to use callbacks, even though it may be clearer for a beginner.

import Foundation

let url = URL(string: "http://www.stackoverflow.com")!

func showHTML(data: Data?, response: URLResponse?, error: Error?) {
    guard let data = data else { return }
    let HTMLContent = String(data: data, encoding: .utf8)!
    print(HTMLContent)
}

let task = URLSession.shared.dataTask(with: url, completionHandler: showHTML)

task.resume()

Anyway, run this code and see the HTML content of StackOverflow’s front page in your console.

Conclusion

A completion handler in Swift calls back when a task has been completed.

Completion handlers, or callbacks, are useful when dealing with asynchronous functions—The code can continue to the next task while the asynchronous code runs in the background. Once the async task completes, it calls a callback function with the result.

For example, when performing a network request, a callback function can be used to display the results of the request when they arrive.

Thanks for reading. I hope you find it useful. Happy coding!

Scroll to Top