I just ran into this, and wanted to share a recipe that works for me. I have a SwiftUI View with the associated ViewModel that’s owned by the view as a StateObject. I want the viewModel to be able to react to a bunch of notifications with the modern async API and long-running AsyncSequences, and for everything to be deallocated correctly when the view ends. I don’t want to code anything by hand and want things to happen automatically with the highest-level code possible.
Here’s what I came up with, that seems to work.
class MyViewModel: ObservableObject {
deinit {
// just for observability that it’s released correctly
print("MyViewModel deinit")
}
}
struct MyView: View {
@StateObject private var myViewModel: MyViewModel()
var body: some View {
WhateverView()
.task {
for await _ in NotificationCenter.default.notifications(named: .notification1) {
myViewModel.doSomething1()
}
}
.task {
for await note in NotificationCenter.default.notifications(named: .notification2) {
if let receivedObject = note.object as? SomeObject {
await myViewModel.ingestObject(receivedObject)
}
}
}
}
}
This seems to work correctly. I can start as many long-running listener tasks as I want, and they are all torn down and the view model object is deallocated when the view is closed which automatically cancels the tasks.