【SwiftUI】 .onAppear と .task
SwiftUIではViewが表示されるタイミングで1度だけ呼ばれるコールバックメソッドとして.onAppear
、 .task
があります。
.onAppear
定義
func onAppear(perform action: (() -> Void)? = nil) -> some View
Viewが表示されるタイミングで1度だけ呼ばれます。
そのタイミングで行いたい処理をクロージャーで指定してあげます。
使用例
struct ContentView: View {
var body: some View {
Text("Sample")
.onAppear {
print("onAppear")
}
}
}
.task
定義
func task(
priority: TaskPriority = .userInitiated,
_ action: @escaping () async -> Void
) -> some View
第一引数の priority:
で第二引数の async(非同期)
で定義された動作の優先度を指定します。 指定しなければ .userInitiated
が適用されます。
また、Viewが非表示になったタイミングで自動でタスクをキャンセルします。
そのため、画面が再表示されたときに処理結果が重複するのを回避することができます。
基本的に.onAppearの代わりとして使うことができます。
.onAppearと同様にViewが表示されるタイミングで1度だけ呼ばれます。
使い方も同様で、処理をクロージャーで指定してあげます。
使用例
struct ContentView: View {
let url = URL(string: "https://example.com")!
@State private var message = "Loading..."
var body: some View {
Text(message)
.task {
do {
var receivedLines = [String]()
for try await line in url.lines {
receivedLines.append(line)
message = "Received \(receivedLines.count) lines"
}
} catch {
message = "Failed to load"
}
}
}
}
上記はApple Developerで紹介されている使用例です。
.onAppearのように await
を使わずに書くこともできます。
ですが、このように使用するなら.onAppearでいいとは思います。
struct ContentView: View {
var body: some View {
Text("Sample")
.task {
print(".task")
}
}
}
.onAppearと.taskはどちらを使えばいいのか
そもそも何が違うのか
今まで .onAppear のなかで Task
を使って非同期を行なっていたのが、.task を使うと Taskを使わずに済むというのと、
.taskは、Viewが非表示になるとタスクを自動でキャンセルします。
.task を使うと .onDisappear で制御をしなくていいので便利ですね。
また、 .onAppear の方が早く呼ばれると聞いたのですが、「早く呼ばれているのか」、「処理が早いのか」はわかりませんでした。
どちらを使えばいいのか
結局どちらがいいのという問題ですが、基本的に.onAppearでいいと思います。
ですが、処理を中断して欲しい場合がある時は、上記したように.onDisappearで制御しなくていい(自動でタスクをキャンセルしてくれる)ので.taskを使用するといいと思います。
画面遷移時の挙動
SwiftUIには
Sheet
を使ったモーダル遷移と
Navigation
を使ったスタック遷移があります。
Navigation を使った遷移では、子Viewから親Viewに戻ってくるタイミングで .onAppear、 .task ともに呼ばれますが、Sheetを使った遷移では呼ばれません。
また、当然ですが NavigationStack、NavigationView に .onAppear、.task を使用すると戻ってきたタイミングでは呼ばれません。
struct ContentView: View {
var body: some View {
NavigationStack {
VStack {
NavigationLink {
SecondView()
} label: {
Text("Navigation")
}
}
}
.onAppear {
print(".onAppear")
}
.task {
print(".task")
}
}
}
上記のコードでそのまま試しています。
Discussion