Balbas Code

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を追加して、更新をラップしました。