はじめに
SwiftUIでは、ある特定のViewが表示された場合に処理を実行させたい場合
.onAppear
もしくは.task
を使うのが良さそうだと思いましたが、2つの違いがよく分からなかったので調べてみました。
環境
Xcode 13.4.1
内容
.onAppear
Viewが表示される前に実行するアクションを追加
func onAppear(perform action: (() -> Void)? = nil) -> some View
SwiftUI がこのメソッドを呼び出す正確な瞬間は、適用する特定のビューの種類に依存するが、アクションクロージャーは最初のレンダリングフレームが表示される前に完了する
.task
Viewが表示される前に実行する非同期タスクを追加
func task(
priority: TaskPriority = .userInitiated,
_ action: @escaping () async -> Void
) -> some View
.task
でViewのライフタイムと一致する非同期タスクを実行できる。Viewを削除するか、Viewの ID が変更される前にタスクが終了しない場合、SwiftUI はタスクをキャンセルする
非同期呼び出しが完了するのを待つか、AsyncSequenceインスタンスの値で待つために、タスク内でawaitキーワードを使用する
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"
}
}
}
.onAppear
と.task
の違い
.onAppear
の中には非同期処理が書けない
※ 非同期関数型 '() async -> Void' から同期関数型 '() -> Void' への変換は無効です。
ただしTask
を追加することで.onAppear
でも非同期処理が使えますが、
.task
を使った場合は、例えばViewが非表示となった時に実行されている処理が自動的にキャンセルされるようなので、この違いは残るようですね
Text(message)
.onAppear {
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"
}
}
}
おわりに
.task
を使う場合で意図せずタスクがキャンセルされることはあるのだろうか。。。その場合は.onAppear
+Task
を使うメリットがあるのかも?🤔(このあたりはまだ理解できていない...)
Task
についても深堀しようと書きながら調べてみましたが、さらっとまとめられる内容ではなく諦めました。。。
参考