Publishing changes from async model

I'm attempting to figure out how to display a message to my users when some asynchronous code takes some time to run. So far I've used a sample I found online to create a popup banner and tied the message together using an ObservedObject of the async method on my view and Publishing the values.

My sample code project is on a public GitHub repository here. https://github.com/SimplyKyra/CodeAttempts

Right now I have an issue as when I set the variables in the async method I'm getting the following error: 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. Solutions online seem to fix this issue by updating the value on the @mainActor thread but I want these methods to run asynchronously AND update the user on what's happening. What's the best way to update my variables from this location?

Accepted Reply

In case anyone is looking into this in the future. I asked on StackOverflow (https://stackoverflow.com/questions/74159031/swiftui-publishing-changes-from-async-model) and was told to put it on the main thread with ‘@MainActor’ or use DispatchQueue.main.async. I enclosed each of my assignments with the DispatchQueue.main.async and it now works without the warnings.

For example my async method now looks like:

class myAsyncViewModel:  ObservableObject  {
    @Published var imageName: String = "questionmark"
    @Published var title: String = "title"
    @Published var subTitle: String = "subtitle"

    func thisMethodTakesTime() async -> String? {
        print("In method: \(imageName), \(title), \(subTitle)")
        DispatchQueue.main.async {
            self.title = "MY METHOD"
            self.subTitle = "Starting out!"
        }
        try? await Task.sleep(nanoseconds: 1_000_000_000)
        DispatchQueue.main.async {
            self.subTitle = "Between"
        }
        try? await Task.sleep(nanoseconds: 1_000_000_000)
        DispatchQueue.main.async {
            self.subTitle = "About to return. Success!"
        }
        print("In method: \(imageName), \(title), \(subTitle)")
        return "RETURN RESULT"
    }
}

Replies

In case anyone is looking into this in the future. I asked on StackOverflow (https://stackoverflow.com/questions/74159031/swiftui-publishing-changes-from-async-model) and was told to put it on the main thread with ‘@MainActor’ or use DispatchQueue.main.async. I enclosed each of my assignments with the DispatchQueue.main.async and it now works without the warnings.

For example my async method now looks like:

class myAsyncViewModel:  ObservableObject  {
    @Published var imageName: String = "questionmark"
    @Published var title: String = "title"
    @Published var subTitle: String = "subtitle"

    func thisMethodTakesTime() async -> String? {
        print("In method: \(imageName), \(title), \(subTitle)")
        DispatchQueue.main.async {
            self.title = "MY METHOD"
            self.subTitle = "Starting out!"
        }
        try? await Task.sleep(nanoseconds: 1_000_000_000)
        DispatchQueue.main.async {
            self.subTitle = "Between"
        }
        try? await Task.sleep(nanoseconds: 1_000_000_000)
        DispatchQueue.main.async {
            self.subTitle = "About to return. Success!"
        }
        print("In method: \(imageName), \(title), \(subTitle)")
        return "RETURN RESULT"
    }
}

Indeed was looking. Thanks for the tip!