Publishing changes from background threads is not allowed〜の対処法
公開日: 2023-10-10 21:58:02
更新日: 2023-10-23 00:45:22
Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
Listの表示の際に無限スクロールを実装しようとしている際に、こちらのようなエラーが出た。
このエラーは、SwiftUIのViewが更新を受け取るときに、メインスレッド以外からの更新を試みたときに表示されます。通常、このエラーは非同期の操作を行い、その結果をメインスレッド以外でObservableObjectに反映しようとしたときなどに発生します。
対処方法としては、receive(on: DispatchQueue.main)を使用して、メインスレッドでの更新を保証します。
修正前のコード
import SwiftUI
import Combine
class SampleViewModel: ObservableObject {
@Published var text: String = "Hello"
func fetchData() {
DispatchQueue.global().async {
// バックグラウンドスレッドでデータをフェッチすると仮定
self.text = "Updated Text"
}
}
}
struct SampleView: View {
@StateObject var viewModel = SampleViewModel()
var body: some View {
VStack {
Text(viewModel.text)
Button("Fetch Data") {
viewModel.fetchData()
}
}
}
}
修正後のコード
import SwiftUI
import Combine
class SampleViewModel: ObservableObject {
@Published var text: String = "Hello"
func fetchData() {
DispatchQueue.global().async {
// バックグラウンドスレッドでデータをフェッチすると仮定
let updatedText = "Updated Text"
// メインスレッドで値を更新
DispatchQueue.main.async {
self.text = updatedText
}
}
}
}
struct SampleView: View {
@StateObject var viewModel = SampleViewModel()
var body: some View {
VStack {
Text(viewModel.text)
Button("Fetch Data") {
viewModel.fetchData()
}
}
}
}
具体的な修正点
修正前のコードでは、非同期タスク(ここではDispatchQueue.global().async内のタスク)の中で直接@Publishedな変数(この例ではtext)を更新していました。
DispatchQueue.global().async {
self.text = "Updated Text"
}
修正後のコードでは、非同期タスクの中でデータを取得した後、そのデータをメインスレッド上で@Publishedな変数に反映するように変更しました。
DispatchQueue.global().async {
let updatedText = "Updated Text"
DispatchQueue.main.async {
self.text = updatedText
}
}
この修正により、@Published変数の更新はメインスレッドでのみ行われるようになり、エラーメッセージが表示されなくなります。
要するに、データの更新を行う前に、それがメインスレッドで行われることを保証するためのDispatchQueue.main.asyncを追加して、更新をラップしました。