【SwiftUI 3.0】.task プロパティの使い方

SwiftUI 3.0 で追加された .task プロパティを試してみました。

基本的には onAppear の代替になるもののようです。

動作確認環境

macOS Monterey 12.1

Xcode 13.2.1

iOS 15.2(iPhone SE 2nd generation)

【SwiftUI 3.0】.task プロパティの使い方

.task の定義を確認

SwiftUI のライブラリには以下のように定義されています。

@inlinable public func task(
    priority: TaskPriority = .userInitiated,
    _ action: @escaping @Sendable () async -> Void
) -> some View

第二引数のクロージャ action:非同期メソッド(asyncとして定義されています。第一引数の priority: TaskPriority でその非同期の動作の実行優先度を指定することになります。

特に指定しなければ .userInitiated が適用されます。

ここでは詳しく触れませんが、

ユーザー操作を起点とした非同期処理はデフォルトの .userInitiated としておき、バックグランドスレッドで行うユーザーを意識する必要のない非同期処理.background を指定すると良いかもしれません。

サンプルコード

実験として、Qiita記事の一覧を取得するAPI.task のアクションで実行してみました。

データ構造体

データは ID とタイトルのみとしました。

struct QiitaArticle: Decodable, Identifiable {
    let id: String
    let title: String
}
Qiita記事取得APIを投げる関数

URLSessionJSONDecoder を使ってシンプルなAPIを叩きます。これを .task でコールします。

func fetchQiitaArticles() async {
    do {
        guard let url = URL(string: "https://qiita.com/api/v2/items?page=1") else { return }
        let (data, _) = try await URLSession.shared.data(from: url)
        articles = try JSONDecoder().decode([QiitaArticle].self, from: data)
    } catch {
        print("Error")
        articles = []
    }
}
.task で API を実行

.task の action: クロージャで非同期でメソッドを呼んでいます。

@State private var articles = [QiitaArticle]()

var body: some View {
    NavigationView {
        List(articles) { article in
            VStack(alignment: .leading) {
                Text(article.title)
                    .font(.headline)
            }
        }
        .navigationTitle("Qiita記事一覧")
    }
    .task {
        await fetchQiitaArticles()
    }
}

APIの結果が返却され articles にセットされると以下のように List にデータが表示されました。

onAppear との違いは何か…

公式ドキュメントには以下のような説明があります。

A closure that SwiftUI calls as an asynchronous task when the view appears. SwiftUI automatically cancels the task if the view disappears before the action completes.

画面が表示された時に実行されるのは onAppear と同じようですが、黄色線にある通り、

画面が非表示となった時に task で実行されている処理が自動的にキャンセルされるようです。

そのため、画面が再表示されたときに処理結果が重複することを避けることができます

自分で onDisappear でコントロールする必要がないため便利ですね。