I have some code that has a long-running task inside of a .perform{} block. I'd like to be able to show the status of this long-running code in a Swift UI view by passing data through a @Published var. But .perform{} seems to prevent @Published from actually propagating to the view.
Is there a way to get the view to update in the middle of this perform{} block?
I've managed to boil my problem down to a playground that illustrates the issue . See the ????? comment.
import SwiftUI
import PlaygroundSupport
import CoreData
struct ContentView: View {
@StateObject var contentModel = ContentModel()
var body: some View {
VStack {
HStack {
Text("Status:")
Text(contentModel.status)
}
Spacer()
Button("Do stuff.") {
contentModel.updateStatus()
}
}
Spacer()
}
}
class ContentModel: ObservableObject {
@Published var status = "foo"
func updateStatus() {
let managedObjectContext = PersistenceController.preview.container.viewContext
// this perform is needed in the Real App™ to manage data fetch + output
managedObjectContext.perform {
// it doesn't matter that this is async
DispatchQueue.main.async {
let oldStatus = self.status
// this status does not propagate into the @Published
// until after the perform{} block finishes
// (read: we don't see it show "X" in the UI)
self.status = "X"
// ????? how can I make this propagate the the view right away?
// simulate a long-running (blocking) operation
sleep(3)
// cheap toggle
if oldStatus == "foo" {
self.status = "bar"
} else {
self.status = "foo"
}
}
}
}
}
class PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
return result
}()
init(inMemory: Bool = false) {
let model = NSManagedObjectModel()
container = NSPersistentContainer(name: "Example", managedObjectModel: model)
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
}
}
PlaygroundPage.current.setLiveView(ContentView())