0

recently I’ve played around with SwiftUI (I’m not a Swift developer, this topic is quite new for me). I’ve found out strange behavior with a dispatch queue.

In my project, the business logic is match more complex; however, here is a simple example (see below).

The issue is the following: if you try to click the button frequently, the value of the actual boolean variable & the view could be different (see the attached screenshot below).

Could anybody give me a hint, what can I do with such an issue?

Note: the main app class is in comments to this post.

ContentView.swift

import SwiftUI

struct ContentView: View {
    @StateObject var model = ContentViewModel()
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(model.isCondition ? .green : .red)
            Button("TOGGLE") {
                model.foo()
            }
            .buttonStyle(.borderedProminent)
        }
    }
}

ContentViewModel.swift

import SwiftUI

final class ContentViewModel: ObservableObject {
    @Published private(set) var isCondition: Bool = true
    private var queue = DispatchQueue(label: "My Queue", qos: .background)
    
    func foo() {
        queue.async { [self] in
            isCondition.toggle()
            print("isCondition = \(isCondition.description)")
        }
    }
}

enter image description here

4
  • MyApp.swift swift import SwiftUI @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } } } Commented Mar 2, 2023 at 2:28
  • 2
    Don't post details in comments. Put that information in your question. Commented Mar 2, 2023 at 2:30
  • 1
    Views are meant to be updated on the main thread. Like private var queue = DispatchQueue.main Commented Mar 2, 2023 at 4:44
  • Is there a particular reason you are using a background thread for this functionality? I feel like I am missing something, because you explicitly coded for asynchronous thread activity and then ask why things seem to be out-of-sync. Commented Mar 2, 2023 at 4:50

2 Answers 2

0

View (states) are required to be updated on the main thread when the body of the view is being computed. So try this approach, where you do your processing on your queue, but update the View state on the main thread.

struct ContentView: View {
    @StateObject var model = ContentViewModel()
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(model.isCondition ? .green : .red)
            Button("TOGGLE") {
                model.foo()
            }
            .buttonStyle(.borderedProminent)
        }
    }
}
final class ContentViewModel: ObservableObject {
    @Published private(set) var isCondition: Bool = true
    private var queue = DispatchQueue(label: "My Queue", qos: .background)
    
    func foo() {
        queue.async { [self] in
            // ... do calculations/processing here on your queue
            // but update isCondition on the main thread
            DispatchQueue.main.async {
                self.isCondition.toggle()
                print("isCondition = \(self.isCondition.description)")
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Hi, thanks for a information. I’ve changed the target queue to DispatchQueue.main, looks like this is the solution, thx!
0

The simplest fix for my particular example is to change the following line:

private var queue = DispatchQueue(label: "My Queue", qos: .background)

With specification of target queue & removing quality-of-service level:

private var queue = DispatchQueue(label: "My Queue", target: DispatchQueue.main)

P.S.: Changes are in the ContentViewModel.swift file

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.