Skip to main content

How to run an asynchronous task when a view is shown

About 3 minSwiftSwiftUIArticle(s)bloghackingwithswift.comcrashcourseswiftswiftuixcodeappstore

How to run an asynchronous task when a view is shown 관련

SwiftUI by Example

Back to Home

How to run an asynchronous task when a view is shown | SwiftUI by Example

How to run an asynchronous task when a view is shown

Updated for Xcode 15

New in iOS 15

SwiftUI's task() modifier is a more powerful version of onAppear(), allowing us to start asynchronous work as soon as the view is shown. Even better, the task will automatically be cancelled when the view is destroyed, if it has not already finished.

As the task is executed asynchronously, this is a great place to fetch some initial network data for your view. For example, if we wanted to fetch a list of messages from a server, decode it into an array of Message structs, then show it in a list, we might write something like this:

struct Message: Decodable, Identifiable {
    let id: Int
    let from: String
    let text: String
}

struct ContentView: View {
    @State private var messages = [Message]()

    var body: some View {
        NavigationStack {
            List(messages) { message in
                VStack(alignment: .leading) {
                    Text(message.from)
                        .font(.headline)
                    Text(message.text)
                }
            }
            .navigationTitle("Inbox")
        }
        .task {
            do {
                let url = URL(string: "https://hackingwithswift.com/samples/messages.json")!

                let (data, _) = try await URLSession.shared.data(from: url)
                messages = try JSONDecoder().decode([Message].self, from: data)
            } catch {
                messages = []
            }
        }
    }
}

Download this as an Xcode projectopen in new window

The word “Inbox” in bold above a list of messages.
The word “Inbox” in bold above a list of messages.

You can attach the task() modifier to any view in your hierarchy, even ones that are presented as a result of a navigation push – it will only actually start its work when the view is displayed.

To demonstrate this, we could create a simple website source code viewer where users had a choice of sites to check:

struct ContentView: View {
    let sites = ["Apple.com", "HackingWithSwift.com", "Swift.org"]

    var body: some View {
        NavigationStack {
            List(sites, id: \.self) { site in
                NavigationLink(site) {
                    SourceViewer(site: site)
                }
            }
            .navigationTitle("View Source")
        }
    }
}

struct SourceViewer: View {
    let site: String
    @State private var sourceCode = "Loading…"

    var body: some View {
        ScrollView {
            Text(sourceCode)
                .font(.system(.body, design: .monospaced))
        }
        .task {
            guard let url = URL(string: "https://\(site)") else {
                return
            }

            do {
                let (data, _) = try await URLSession.shared.data(from: url)
                sourceCode = String(decoding: data, as: UTF8.self).trimmingCharacters(in: .whitespacesAndNewlines)
            } catch {
                sourceCode = "Failed to fetch site."
            }
        }
    }
}

Download this as an Xcode projectopen in new window

Both task() and onAppear() are able to run synchronous functions when you view is shown, so there's no particular reason to choose one or the other beyond personal taste – there's a nice balance to having both onAppear() and onDisappear() together.

Tips

By default, Swift tasks created using the task() modifier will run at the highest available priority, but you can pass a custom priority into the modifier if you know the work is less important.

Similar solutions…
How to control which view is shown when your app launches | SwiftUI by Example

How to control which view is shown when your app launches
How to control which NavigationSplitView column is shown in compact layouts | SwiftUI by Example

How to control which NavigationSplitView column is shown in compact layouts
How to run some code when state changes using onChange() | SwiftUI by Example

How to run some code when state changes using onChange()
How to show progress on a task using ProgressView | SwiftUI by Example

How to show progress on a task using ProgressView
How to run code when your app launches | SwiftUI by Example

How to run code when your app launches

이찬희 (MarkiiimarK)
Never Stop Learning.