Skip to content

Instantly share code, notes, and snippets.

@BigZaphod
Created March 8, 2024 15:53
Show Gist options
  • Save BigZaphod/874a9458c3fc5f546ea1e2b186118cc3 to your computer and use it in GitHub Desktop.
Save BigZaphod/874a9458c3fc5f546ea1e2b186118cc3 to your computer and use it in GitHub Desktop.
final actor Worker: ObservableObject {
@MainActor @Published private(set) var lastWorkDoneAt: Date?
private var counter = 0
func doWork() {
counter += 1
DispatchQueue.main.async {
self.lastWorkDoneAt = .now
}
}
func fetchWorkCounter() -> Int {
return counter
}
}
struct MyTestView: View {
@StateObject private var worker = Worker()
@State private var queuedWorkCount: Int = 0
@State private var fetchedWorkCount: Int = 0
var body: some View {
VStack {
Text("Queued Work Count: \(queuedWorkCount)")
Text("Fetched Work Count: \(fetchedWorkCount)")
Divider()
Button {
Task {
for i in 1...100 {
queuedWorkCount += 1
await worker.doWork()
}
}
} label: {
Text("Enqueue 100 Work")
}
}
.task(id: worker.lastWorkDoneAt) {
fetchedWorkCount = await worker.fetchWorkCounter()
}
}
}
@kylebshr
Copy link

kylebshr commented Mar 8, 2024

Would an approach more like this work, where you publish the completed work and use that directly in the view?

final actor Worker: ObservableObject {
    @MainActor @Published private(set) var publishedCount: Int?

    @Published private(set) var counter = 0

    func doWork() async {
        counter += 1

        Task { @MainActor [counter] in
            self.publishedCount = counter
        }
    }

    func fetchWorkCounter() -> Int {
        return counter
    }
}
struct MyTestView: View {
    @StateObject private var worker = Worker()
    @State private var queuedWorkCount: Int = 0
    @State private var fetchedWorkCount: Int = 0
    
    var body: some View {
        VStack {
            Text("Queued Work Count: \(queuedWorkCount)")
            Text("Fetched Work Count: \(worker.publishedCount ?? 0)")
            Divider()
            Button {
                Task {
                    for i in 1...100 {
                        queuedWorkCount += 1
                        await worker.doWork()
                    }
                }
            } label: {
                Text("Enqueue 100 Work")
            }
        }
    }
}

@BigZaphod
Copy link
Author

That's not the same as what I am going for, I think. The real Worker actor is managing a database, so I was looking for a way to generate an event that basically just means, "some data has changed, you need to re-run your query and update your UI."

So somewhere else in the app, a record may be added to the worker actor with something like: await worker.save(record) - possibly even as a background process or something triggered by a timer somewhere. After the record is actually been saved, I wanted to trigger a signal so UI elsewhere in the app can re-query their view and see the changes. Of course it's not as perfectly efficient as noting exactly what changed and only updating exactly those objects and views or whatever, but I figured this would be close enough. I think the idea would work okay except I ran into this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment