Concurrency in modern Swift with Async & Await in SwiftUI — Part 3

DevTechie Inc
Jul 19, 2023

Concurrency in modern Swift with Async & Await in SwiftUI — Part 3

We will continue our exploration of modern concurrency with task structure & task modifier and async await version of URLSession in this article.

Task Modifier

Apple introduced the task modifier with the release of iOS 15. Task modifier adds an asynchronous task to perform before the view appears.

We can use this modifier to perform any asynchronous task with the benefit that if the view’s life ends before the task is finished executing, SwiftUI will cancel the task. Unlike onAppear modifier which will keep on executing the task until its finished.

Let’s update our code to render a random image from our service, when the view appears.

struct DevTechieAsyncAwaitExample: View {
    
    @State private var counter = 0
    @State private var response: Image?
    
    var body: some View {
        NavigationStack {
            VStack {
                if let image = response {
                    image
                        .resizable()
                        .frame(width: 400, height: 400)
                }
            }
            .task {
                response = try? await WebService().getDataFromServer()
            }
            .navigationTitle("DevTechie")
        }
    }
}

Async URLSession

Since the introduction of async and await, URLSession has also added a variation so we don’t have to deal with completion blocks or trailing closures anymore.

Before async and await, URLSession had the following interface.

URLSession
    .shared
    .dataTask(with: req) { data, response, error in
         // execute something big
    }

With async and await, the API has been changed to be more readable.

let (data, _) = try await URLSession.shared.data(from: url)

Lets change our image downloading service to use URLSession instead of using overload from Data class.

class WebService {
    func getDataFromServer() async throws -> Image? {
        let (imageData, _) = try await URLSession
                        .shared
                        .data(from: URL(string: "https://picsum.photos/4000")!)
        return Image(uiImage: UIImage(data: imageData)!)
    }
}

No changes in the view, but here is the code for convenience.

struct DevTechieAsyncAwaitExample: View {
    
    @State private var counter = 0
    @State private var response: Image?
    
    var body: some View {
        NavigationStack {
            VStack {
                if let image = response {
                    image
                        .resizable()
                        .frame(width: 400, height: 400)
                }
            }
            .task {
                response = try? await WebService().getDataFromServer()
            }
            .navigationTitle("DevTechie")
        }
    }
}

Build and run