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.

When working with background threads in your application, it’s important to note that publishing changes directly from these threads is not allowed. Instead, you should ensure that values are published from the main thread using operators like `receive(on:)` on model updates.

Let’s consider an example to understand this in more detail. Suppose you have a SwiftUI application that fetches data from an API in the background thread using a URLSession. Once the data is fetched, you want to update the model and reflect those changes in your view. However, updating the view directly from the background thread is not permissible.


        struct ContentView: View {
            @StateObject private var viewModel = ViewModel()
            
            var body: some View {
                Text(viewModel.data)
                    .onAppear {
                        viewModel.fetchData()
                    }
            }
        }
        
        class ViewModel: ObservableObject {
            @Published var data: String = ""
            
            func fetchData() {
                DispatchQueue.global().async {
                    // Fetch data from API
                    let result = fetchDataFromAPI()
                    
                    // Update the model on the main thread
                    DispatchQueue.main.async {
                        self.data = result
                    }
                }
            }
            
            func fetchDataFromAPI() -> String {
                // Simulate API request delay
                sleep(2)
                
                return "New Data"
            }
        }
    

Now, in the above example, we have a SwiftUI `View` called `ContentView` which displays the `viewModel`’s `data`. When the view appears, it triggers the `fetchData()` function in the `viewModel` which fetches data from an API in the background thread using `DispatchQueue.global().async`.

Once the data is obtained, it updates the `data` property of the `viewModel`. However, instead of updating it directly from the background thread, we use `DispatchQueue.main.async` to perform the update on the main thread. This is achieved by wrapping the update code inside `DispatchQueue.main.async` block.

By doing this, we ensure that the updates to the model (`data` property) are published from the main thread. The `@Published` property wrapper on `data` will then notify the view to update itself accordingly.

This approach adheres to the recommended practice of manipulating UI-related state and publishing changes from the main thread.

Related Post

Leave a comment