Swiftでasync/awaitとTaskを使うときに最初に見ること
Swiftで非同期処理を書くなら、まず async/await と Task の関係を見ておくと整理しやすいです。
Swift公式のConcurrencyドキュメントでも、非同期関数には async を付け、呼び出し側では await する形が説明されています。
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/
async関数を書く
func fetchName() async -> String {
"Makonote"
}
async が付いた関数は、呼び出し側で await が必要です。
let name = await fetchName()
ただし、await は非同期コンテキストの中でしか使えません。
Taskから呼ぶ
同期的な場所から非同期処理を始める場合は Task を使います。
Task {
let name = await fetchName()
print(name)
}
SwiftUIのボタンやライフサイクルから非同期処理を起動するときに、よくこの形になります。
Button("Load") {
Task {
await load()
}
}
UI更新はMainActorで扱う
UIに反映する値を更新する場合は、MainActorを意識します。
@MainActor
final class ViewModel: ObservableObject {
@Published var name = ""
func load() async {
name = await fetchName()
}
}
@MainActor を付けると、その型の処理はメインアクター上で実行されます。
UIの状態を持つViewModelでは、この形が扱いやすいことが多いです。
Taskを増やしすぎない
とりあえず Task {} で囲むと動くことがあります。
ただ、あちこちにTaskを作ると、どこでキャンセルされるのか、どの順番で完了するのかが見えにくくなります。
非同期処理の入口だけ Task にして、中の処理は async 関数として分ける方が追いやすいです。
Button("Load") {
Task {
await viewModel.load()
}
}
まとめ
Swiftの非同期処理は、まずこの形で見ます。
- 非同期関数には
async - 呼び出し側は
await - 同期的な入口から始めるなら
Task - UI更新は
@MainActor Taskは入口に寄せる
細かいキャンセルや並列実行に進む前に、この基本形を作っておくと読みやすくなります。


