SwiftUI 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.

21 min read

In SwiftUI, updating the UI from background threads is not allowed. Any changes to the published properties should be done on the main thread. To update model values from background threads, you can use the receive(on:) operator to switch to the main thread before publishing the changes.

Here's an example of how to use the receive(on:) operator to correctly publish values from background threads:

import SwiftUI
import Combine

class MyViewModel: ObservableObject {
    @Published var myProperty: Int = 0
    
    private var cancellables = Set<AnyCancellable>()
    
    func updatePropertyInBackground() {
        DispatchQueue.global().async {
            // Perform background work
            let newValue = 123
            
            DispatchQueue.main.async {
                // Switch to main thread before updating the property
                self.myProperty = newValue
            }
        }
    }
    
    func updatePropertyWithPublisher() {
        Just(456)
            .receive(on: DispatchQueue.main) // Switch to main thread
            .sink { newValue in
                self.myProperty = newValue
            }
            .store(in: &cancellables)
    }
}

In the updatePropertyInBackground() method, we perform the work on a background thread and then switch to the main thread using DispatchQueue.main.async before updating the myProperty property.

In the updatePropertyWithPublisher() method, we use the receive(on:) operator on a Combine publisher to switch to the main thread before updating the property in the sink closure.

By ensuring updates to published properties are always done on the main thread, you can avoid the "Publishing changes from background threads is not allowed" error in SwiftUI.