Search code examples
swiftuser-interfaceswiftuibackgroundmessage-queue

Show messages in open form from background task in SwiftUI


I have a background task which needs to show values to the open dialog. I read a lot of stuff about this tasks and adapted the tutorials to my needs. But regardless of my attempts, there is no update until the task is finished and the control is back in the from control.

So what I did in the Content-View, I defined it like:

struct ContentView: View {
    @StateObject var infoText    = InfoTextObject()

with type defined like:

class InfoTextObject: ObservableObject {
    @Published var text = ""
}

For defining this in SwiftUI, I defined that struct, because classes are not allowed:

// small view with text message area
public struct InfoView: View {
    @ObservedObject var it: InfoTextObject
    
    public var body: some View {
        TextField("Infos ...", text: $it.text)
            .frame(alignment: .topTrailing)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .fixedSize(horizontal: false, vertical: false)
            .disabled(true)
    }
}

The body definition for the form looks like this

var body: some View {
        VStack(alignment: .leading)
        {
...
            InfoView(it: infoText)
        }

And finally the function which is called to update the screen with a new message looks like this:

// function to display any text in the view
func DisplayMessage(infoTextObject: InfoTextObject, _ text: String = "", debugMode : Bool = false) {
    DispatchQueue.main.async {
        infoTextObject.text = text
    }
}

To me this looks very complex for such a simple and usual task. But even than it doesn't update. :-(

Any hints?


Solution

  • It's hard to tell what is going on, since the code you are showing us is not the code you are using. Here is an example of the code that updates txt in a simulated background process.

    struct ContentView: View {
        @State var infoText = ""  
        
        var body: some View {
            VStack {
                InfoView(txt: $infoText)
            }
        }
    }
    
    struct InfoView: View {
        @Binding var txt: String 
        
        var body: some View {
            TextField("Infos ...", text: $txt)
                .frame(alignment: .topTrailing)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .fixedSize(horizontal: false, vertical: false)
                .disabled(true)
            Button(action: { DisplayMessage(infoText: $txt, "new message to display") }){
                Text("Click for background task")
            }
        }
    
    }
    
    // simulating reporting updates during a long background process
    func DisplayMessage(infoText: Binding<String>, _ text: String = "", debugMode : Bool = false) {
        infoText.wrappedValue = text
        DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 2) {
            DispatchQueue.main.async {
                infoText.wrappedValue = "processing"
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                infoText.wrappedValue = "getting there"
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
                infoText.wrappedValue = "close to finishing"
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 6) {
                infoText.wrappedValue = "all done"
            }
        }
    }