Scenario
A simple SwiftUI App that consists of a TabView with two tabs. The App struct has a @StateObject property, which is being repeatedly and very quickly (30 times per second) updated by simulateFastStateUpdate.
In this example, simulateFastStateUpdate is not doing any useful work, but it closely resembles a real function that quickly updates the app's state. The function does some work on a background queue for a short interval of time and then schedules a state update on the main queue. For example, when using the camera API, the app might update the preview image as frequently as 30 times per second.
Question
When the app is running, the TabView does not respond to taps. It's permanently stuck on the first tab. Removing liveController.message = "Nice" line fixes the issue.
- Why is
TabViewstuck? - Why is updating
@StateObjectcausing this issue? - How to adapt this simple example, so that the
TabViewis not stuck?
import SwiftUI
class LiveController: ObservableObject {
@Published var message = "Hello"
}
@main
struct LiveApp: App {
@StateObject var liveController = LiveController()
var body: some Scene {
WindowGroup {
TabView() {
Text(liveController.message)
.tabItem {
Image(systemName: "1.circle")
}
Text("Tab 2")
.tabItem {
Image(systemName: "2.circle")
}
}
.onAppear {
DispatchQueue.global(qos: .userInitiated).async {
simulateFastStateUpdate()
}
}
}
}
func simulateFastStateUpdate() {
DispatchQueue.main.async {
liveController.message = "Nice"
}
// waits 33 ms ~ 30 updates per second
usleep(33 * 1000)
DispatchQueue.global(qos: .userInitiated).async {
simulateFastStateUpdate()
}
}
}