Section 6/171 menit
6. Best Practice: Penggunaan MainActor
6. Best Practice: Penggunaan MainActor
BP 6.1: Annotate di Level Type, Bukan Method per Method
Kalau seluruh class adalah UI, tandai @MainActor di class. Lebih bersih, dan caller tidak perlu cek per method.
swift
// ❌ Per method
final class HomeViewModel {
@MainActor var items: [Item] = []
@MainActor func reload() async { ... }
@MainActor func toggle(_ id: UUID) { ... }
}
// ✓ Per class
@MainActor
final class HomeViewModel {
var items: [Item] = []
func reload() async { ... }
func toggle(_ id: UUID) { ... }
}
BP 6.2: Jangan Letakkan CPU-Heavy di MainActor
Parsing, encryption, image processing, sorting list besar — bukan di MainActor. Pindahkan ke actor lain atau function nonisolated.
swift
// ❌ Block main thread
@MainActor
final class SearchViewModel {
func search(_ q: String) {
let results = heavyFuzzyMatch(q, in: bigCorpus) // CPU-heavy di main
self.results = results
}
}
// ✓ Off-main untuk CPU
@MainActor
final class SearchViewModel {
var results: [Item] = []
func search(_ q: String) async {
let found = await Task.detached(priority: .userInitiated) {
heavyFuzzyMatch(q, in: bigCorpus)
}.value
self.results = found // kembali ke main
}
}
BP 6.3: Pakai assumeIsolated di Callback Lama yang Sudah di Main
Banyak API UIKit/AppKit memanggil callback di main thread tapi belum di-annotate @MainActor. Pakai MainActor.assumeIsolated untuk menghindari hop yang tidak perlu.
swift
// ✓ Tanpa hop
button.addTarget(self, action: #selector(handleTap), for: .touchUpInside)
@objc private func handleTap() {
MainActor.assumeIsolated {
viewModel.didTapButton() // tidak ada await, tidak ada hop
}
}
BP 6.4: Update UI dalam Batch
Setiap await MainActor.run { } adalah hop. Hop berlebihan = lag visual.
swift
// ❌ Per item hop
for change in changes {
await MainActor.run { table.apply(change) }
}
// ✓ Satu hop
await MainActor.run {
for change in changes { table.apply(change) }
}