是否可以使用 init 中的异步序列(异步等待)来观察通知中心?

hri*_*hri 5 ios async-await swift swiftui

我想观察.EKEventStoreChanged来自异步等待 API 的通知NotificationCenter,这意味着处理异步序列。

该代码有效,但我不确定在 中创建任务(从而观察更改)是否是init()一个好主意,或者是否有更好的选择。我有点担心这是对 the 的误用init(),因为现在,如果我没看错的话,init()必须运行 the 的整个生命周期,ObservableObject但 inits 的目的只是在对象创建期间运行?

class CalendarState: ObservableObject {
    // ...
    
    init() {
       // ...
        
        Task{
            await observeCalendar()
        }
    }

    func observeCalendar() async {
        for await _ in NotificationCenter.default.notifications(named: .EKEventStoreChanged) {
            await loadEvents(date: date)
    }
}
Run Code Online (Sandbox Code Playgroud)

欢迎任何帮助、提示或经验 - 谢谢:D

Rob*_*Rob 11

在 SwiftUI 中,.task当视图被移除时,修饰符会自动取消。因此,由于结构化并发,AsyncSequence通知也将为您取消:

\n
struct DetailsView: View {\n    var body: some View {\n        Button("Dismiss") {\n            ...\n        }\n        .task {\n            await addNotificationsHandler()\n        }\n    }\n\n    func addNotificationsHandler() async {\n        for await notification in NotificationCenter.default.notifications(named: .foo) {\n            foo(with: notification)\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

在 UIKit 中,您必须更加小心,因为异步序列不会自动取消。因此,当视图消失时,您将跟踪 for TaskthatAsyncSequence和it:cancel

\n
private var notificationsTask: Task<(), Never>?\n\noverride func viewDidAppear(_ animated: Bool) {\n    super.viewDidAppear(animated)\n\n    notificationsTask = Task {\n        await addNotificationsHandler()\n    }\n}\n\noverride func viewDidDisappear(_ animated: Bool) {\n    super.viewDidDisappear(animated)\n\n    notificationsTask?.cancel()\n}\n\nfunc addNotificationsHandler() async {\n    for await notification in NotificationCenter.default.notifications(named: .foo) {\n        foo(with: notification)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

最重要的是,在上面的 UIKit 示例中,您必须手动取消Task正在循环的AsyncSequence. 在 SwiftUI 中,.task修改器已为您取消,因此AsyncSequence将自动取消。

\n

最重要的是,如果您开始循环遍历AsyncSequencea 中的an Task,请确保在完成后执行cancel此操作。Task根据定义,Task选择退出结构化并发,因此您必须自己处理取消。

\n
\n

如果您想要观察多个通知,可以使用任务组。例如,在 SwiftUI 中,下面是一个观察应用程序何时退出活动状态以及何时变为活动状态的示例:

\n
struct SampleView: View {\n    @StateObject var viewModel = SampleViewModel()\n\n    var body: some View {\n        VStack { \xe2\x80\xa6 }\n        .task {\n            await viewModel.addNotificationsHandlers()\n        }\n    }\n}\n\n@MainActor\nclass SampleViewModel: ObservableObject {\n    func addNotificationsHandlers() async {\n        await withTaskGroup(of: Void.self) { group in\n            group.addTask { await self.addBecomeActiveHandler() }\n            group.addTask { await self.addResignActiveHandler() }\n        }\n    }\n\n    func addBecomeActiveHandler() async {\n        for await notification in NotificationCenter.default.notifications(named: UIApplication.didBecomeActiveNotification) {\n            handleBecomeActive(for: notification)\n        }\n    }\n\n    func addResignActiveHandler() async {\n        for await notification in NotificationCenter.default.notifications(named: UIApplication.willResignActiveNotification) {\n            handleResignActive(for: notification)\n        }\n    }\n\n    func handleBecomeActive(for notification: Notification) { \xe2\x80\xa6 }\n\n    func handleResignActive(for notification: Notification) { \xe2\x80\xa6 }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

或者在 UIKit 中:

\n
private var notificationsTask: Task<(), Never>?\n\noverride func viewDidAppear(_ animated: Bool) {\n    super.viewDidAppear(animated)\n\n    addNotificationsHandlers()\n}\n\noverride func viewDidDisappear(_ animated: Bool) {\n    super.viewDidDisappear(animated)\n\n    removeNotificationsHandlers()\n}\n\nfunc addNotificationsHandlers() {\n    notificationsTask = Task {\n        await withTaskGroup(of: Void.self) { group in\n            group.addTask { await self.addBecomeActiveHandler() }\n            group.addTask { await self.addResignActiveHandler() }\n        }\n    }\n}\n\nfunc removeNotificationsHandlers() {\n    notificationsTask?.cancel()\n}\n\nfunc addBecomeActiveHandler() async {\n    for await notification in NotificationCenter.default.notifications(named: UIApplication.didBecomeActiveNotification) {\n        handleBecomeActive(for: notification)\n    }\n}\n\nfunc addResignActiveHandler() async {\n    for await notification in NotificationCenter.default.notifications(named: UIApplication.willResignActiveNotification) {\n        handleResignActive(for: notification)\n    }\n}\n\nfunc handleBecomeActive(for notification: Notification) { \xe2\x80\xa6 }\n\nfunc handleResignActive(for notification: Notification) { \xe2\x80\xa6 }\n
Run Code Online (Sandbox Code Playgroud)\n