DEV Community

Wesley de Groot
Wesley de Groot

Posted on • Originally published at wesleydegroot.nl on

async/await

Let's delve into the fascinating world of Swift's async/await.

This powerful feature, introduced in Swift 5.5, revolutionizes asynchronous programming, making it more intuitive and readable. Buckle up as we explore the ins and outs of async/await!

What is async/await?

At its core, async/await simplifies handling asynchronous tasks. It allows you to write asynchronous code that looks almost synchronous. Here's how it works:

  1. async Functions: You mark a function as async. Inside this function, you can use await to pause execution until an asynchronous operation completes.

  2. await Expression: When you encounter an await expression, the function suspends execution until the awaited task finishes. Meanwhile, other tasks can continue running concurrently.

Example 1: Fetching Data

Let's start with a common scenario: fetching data from a remote API. Imagine we have a callback-based function for fetching data:

func fetchData(completion: @escaping (Data?, Error?) -> Void) {
    let url = URL(string: "https://api.example.com/data")!
    URLSession.shared.dataTask(with: url) { data, _, error in
        completion(data, error)
    }.resume()
}
Enter fullscreen mode Exit fullscreen mode

Now, let's convert this to an async function using async/await:

func fetchData() async throws -> Data {
    let url = URL(string: "https://api.example.com/data")!
    return try await URLSession.shared.data(from: url).0
}
Enter fullscreen mode Exit fullscreen mode

In the async version:

  • We use try await to wait for the data to be fetched.
  • The error handling is cleaner, thanks to Swift's throws.

Example 2: Multiple API Calls

Imagine we have a callback-based function for fetching data:

func fetchUser(id: Int, completion: @escaping (User?, Error?) -> Void) {
    // Fetch user details...
}

func fetchPosts(for user: User, completion: @escaping ([Post]?, Error?) -> Void) {
    // Fetch posts for the user...
}

// Usage:

fetchUser(id: 1) { user, error in
  guard let user = user else {
    print(error)
  }
  // Guard for no error
  self.fetchPosts(for: user) { posts, error in
    // Process user and posts
  }
}
Enter fullscreen mode Exit fullscreen mode

Suppose we need to fetch a user and then fetch their posts. In the callback-based world, this can get messy. But with async/await, it's elegant:

func fetchUser(id: Int) async throws -> User {
    // Fetch user details...
}

func fetchPosts(for user: User) async throws -> [Post] {
    // Fetch posts for the user...
}

// Usage
Task {
    do {
        let user = try await fetchUser(id: 1)
        let posts = try await fetchPosts(for: user)
        // Process user and posts
    } catch {
        // Handle errors
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

By embracing async/await, you create a better development experience and write more efficient code. Say goodbye to callback hell and welcome a cleaner, more expressive way of handling asynchronous tasks in Swift. 🚀

Resources https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html#async-await

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay