Network calls with Async/Await and Error Handling in Swift: A Practical Guide for Developers


Unlocking Swift's Concurrency Potential: Exploring async, throws, and await

Async/await is a new feature in Swift that allows you to write asynchronous code in a more concise and readable way  making it easier to work with tasks that may take time to complete, such as network requests, file I/O, or other async operations. It was introduced in Swift 5.5


What is asynchronous code?

Asynchronous code is code that can run concurrently with other code. This means that it can start running, and then pause while it waits for something to happen (such as a network request to complete), and then resume running when it is ready.

What is async/await?

Async/await is a way to write asynchronous code in Swift without having to use callbacks or completion handlers. It is a more concise and readable way to write asynchronous code, and it makes it easier to reason about how your code works.

How to use async/await

To use async/await, you first need to mark your function or method as asynchronous by adding the async keyword before the return type. You can also add the async keyword before a closure to indicate that it is asynchronous.

Next, you can use the await keyword to suspend the execution of your code until the asynchronous operation that you are waiting for is complete.

Certainly! Below is an example of making network calls using async/throws and the await keyword in Swift. This example demonstrates how to fetch data from a REST API using the URLSession API while handling errors

import SwiftUI

struct ContentView: View {
    
    @State private var user: GitHubUser?
    var body: some View {
        VStack(spacing: 20) {
            AsyncImage(url: URL(string: user?.avatar_url ?? "https://avatars.githubusercontent.com/u/65787178?v=4")) { image in
                image
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .clipShape(Circle())
            } placeholder: {
                Circle()
                    .foregroundColor (.secondary)
            }
            .frame(width: 200, height: 200)
            Text (user?.login ?? "")
                .bold ()
                .font (.title3)
            Text (user?.blog ?? "")
                .foregroundColor(.blue)
                .padding ()
                .onTapGesture {
                    let url = URL.init(string: user?.blog ?? "")
                    guard let stackOverflowURL = url, UIApplication.shared.canOpenURL(stackOverflowURL) else { return }
                    UIApplication.shared.open(stackOverflowURL)
                }
            
            Spacer()
                .padding ()
        }
        .task {
            do {
                user = try await getUser()
            } catch GHError.invalidURL {
                print ("invalid URL")
            } catch GHError.invalidResponse {
                print ("invalid response")
            } catch GHError.invalidData {
                print ("invalid data")
            } catch {
                print ("unexpected error")
            }
        }
    }
    
// Define an asynchronous function to make a network request
    func getUser() async throws -> GitHubUser {
        let endpoint = "https://api.github.com/users/appcodezip"
        guard let url = URL(string: endpoint) else {
            throw GHError.invalidURL
        }
        let (data, response) = try await URLSession.shared.data(from: url)
        guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
            throw GHError.invalidResponse
        }
        do {
            let decoder = JSONDecoder ()
            decoder.keyDecodingStrategy = .convertFromSnakeCase
            return try decoder.decode (GitHubUser.self, from: data)
        } catch {
            throw GHError.invalidData
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Model:


struct GitHubUser: Codable { let login: String? let avatar_url: String? let blog: String? enum CodingKeys: String, CodingKey { case login case avatar_url = "avatar_url" case blog } }

Error Handling:

enum GHError: Error {
case invalidURL
case invalidResponse
case invalidData
}

Benefits of using async/await

It is more concise and readable than using callbacks or completion handlers.
It makes it easier to reason about how your code works, because you can see which parts of your code are asynchronous and which parts are synchronous.
It makes it easier to write error-handling code, because you can use the try and catch keywords to handle errors in asynchronous code.

Benefits of using async throws

It allows you to write asynchronous code that can throw errors in a centralized way.
It makes it easier to reason about how your code works, because you can see which parts of your code are asynchronous and which parts are synchronous, and you can see which parts of your code can throw errors.
It makes it easier to write error-handling code, because you can use the try and catch keywords to handle errors in asynchronous code.










Post a Comment

0 Comments