Ответ
Требование обновлять пользовательский интерфейс только на главном потоке (Main Thread) является фундаментальным правилом в iOS/macOS разработке. Оно распространяется и на SwiftUI, несмотря на его декларативную природу.
Причины:
- Небезопасность UIKit/AppKit: SwiftUI в текущей реализации все еще использует низкоуровневые механизмы UIKit (на iOS) и AppKit (на macOS). Эти фреймворки не являются потокобезопасными. Параллельный доступ к элементам UI из нескольких потоков приводит к состоянию гонки, неопределенному поведению и крашам.
- Согласованность представления: Главный поток действует как последовательная очередь для всех операций с UI. Это гарантирует, что обновления состояния представления (например, текста метки, видимости элемента) обрабатываются атомарно и в предсказуемом порядке, предотвращая визуальные артефакты.
Что происходит, если нарушить правило? Xcode выводит предупреждение в консоль: [SwiftUI] Publishing changes from background threads is not allowed..., и приложение может упасть.
Правильный паттерн работы:
import SwiftUI
struct ContentView: View {
@State private var data: String = "Loading..."
var body: some View {
Text(data)
.onAppear {
// Запускаем тяжелую задачу в фоне
DispatchQueue.global(qos: .userInitiated).async {
let fetchedData = self.loadDataFromNetwork() // Имитация фоновой работы
// Возвращаемся на главный поток для обновления @State
DispatchQueue.main.async {
self.data = fetchedData
}
}
}
}
func loadDataFromNetwork() -> String { "New Data" }
}
Исключение (автоматическая диспетчеризация): SwiftUI может автоматически планировать некоторые обновления, вызванные жестами или системными событиями, на главный поток. Однако для явных асинхронных операций (сетевые запросы, работа с БД) переход на DispatchQueue.main.async обязателен.