【问题标题】:Pausing a Notification publisher in SwiftUI在 SwiftUI 中暂停通知发布者
【发布时间】:2020-02-28 14:07:50
【问题描述】:

当我调用后端服务(登录、值检查……)时,我在相关视图上使用通知发布者来异步管理更新。 我想在视图消失或“暂停”发布者时取消订阅通知。 我首先使用了 WWDC19 Combine 和相关 SwiftUI 演讲中的简单“assign”选项,然后查看了 great post 和 onReceive 修饰符。然而,即使视图不可见,视图也会随着发布的值不断更新。

我的问题是:

  1. 当视图不可见时,我可以“暂停”这个发布者吗?
  2. 我真的应该担心这一点,它会影响资源(后端更新可能会触发列表和图像显示的大刷新)还是应该让 SwiftUI 在后台管理?

示例代码: 选项 1:onReceive

struct ContentView: View {

    @State var info:String = "???"
    let provider = DataProvider() // Local for demo purpose, use another pattern

    let publisher = NotificationCenter.default.publisher(for: DataProvider.updated)
    .map { notification in
        return notification.userInfo?["data"] as! String
    }
    .receive(on: RunLoop.main)

    var body: some View {
        TabView {
            VStack {
                Text("Info: \(info)")
                Button(action: {
                    self.provider.startNotifications()
                }) {
                    Text("Start notifications")
                }
            }
           .onReceive(publisher) { (payload) in
                    self.info = payload
            }
            .tabItem {
                Image(systemName: "1.circle")
                Text("Notifications")
            }
            VStack {
                Text("AnotherView")
            }
            .tabItem {
                Image(systemName: "2.circle")
                Text("Nothing")
            }
        }
    }
}

选项 2:onAppear / onDisappear

struct ContentView: View {

    @State var info:String = "???"
    let provider = DataProvider() // Local for demo purpose, use another pattern

    @State var cancel: AnyCancellable? = nil

    var body: some View {
        TabView {
            VStack {
                Text("Info: \(info)")
                Button(action: {
                    self.provider.startNotifications()
                }) {
                    Text("Start notifications")
                }
            }
            .onAppear(perform: subscribeToNotifications)
            .onDisappear(perform: unsubscribeToNotifications)
            .tabItem {
                Image(systemName: "1.circle")
                Text("Notifications")
            }
            VStack {
                Text("AnotherView")
            }
            .tabItem {
                Image(systemName: "2.circle")
                Text("Nothing")
            }
        }
    }

    private func subscribeToNotifications() {
       // publisher to emit events when the default NotificationCenter broadcasts the notification
        let publisher = NotificationCenter.default.publisher(for: DataProvider.updated)
            .map { notification in
                return notification.userInfo?["data"] as! String
            }
            .receive(on: RunLoop.main)

        // keep reference to Cancellable, and assign String value to property
        cancel = publisher.assign(to: \.info, on: self)
    }

    private func unsubscribeToNotifications() {
       guard cancel != nil else {
            return
        }
        cancel?.cancel()
    }
}

对于这个测试,我使用了一个虚拟服务:

class DataProvider {   
    static let updated = Notification.Name("Updated")
    var payload = "nothing"    
    private var running = true

    func fetchSomeData() {
        payload = Date().description
        print("DEBUG new payload : \(payload)")
        let dictionary = ["data":payload] // key 'data' provides payload
        NotificationCenter.default.post(name: DataProvider.updated, object: self, userInfo: dictionary)
    }

    func startNotifications() {
        running = true
        runNotification()
    }

    private func runNotification() {
        if self.running {
            self.fetchSomeData()
            let soon = DispatchTime.now().advanced(by: DispatchTimeInterval.seconds(3))
            DispatchQueue.main.asyncAfter(deadline: soon) {
                self.runNotification()
            }
        } else {
            print("DEBUG runNotification will no longer run")
        }
    }

    func stopNotifications() {
        running = false
    }   
}

【问题讨论】:

    标签: ios swift swiftui


    【解决方案1】:

    您的程序中似乎有两个发布者名称为let publisher。请删除其中一组。 self.info = payloadpublisher.assign(to: \.info, on: self)} 也在重复。

                   }
            .onAppear(perform: subscribeToNotifications)
            .onDisappear(perform: unsubscribeToNotifications)
            .onReceive(publisher) { (payload) in
                  //  self.info = payload
                print(payload)
            }
            .tabItem {
    

    如下:

                      @State var cancel: AnyCancellable? = nil
    
                private func subscribeToNotifications() {
                   // publisher to emit events when the default NotificationCenter broadcasts the notification
            //        let publisher = NotificationCenter.default.publisher(for: DataProvider.updated)
            //            .map { notification in
            //                return notification.userInfo?["data"] as! String
            //            }
            //           .receive(on: RunLoop.main)
    
                    // keep reference to Cancellable, and assign String value to property
                    if cancel == nil{
                        cancel = publisher.assign(to: \.info, on: self)}
    
                }
    
                private func unsubscribeToNotifications() {
                   guard cancel != nil else {
                        return
                    }
                    cancel?.cancel()
                }
    

    现在您可以看到,cancel?.cancel() 确实有效,并且在您从 tab2 回来后,info 标签不再更新。 ~~~发布者在这里暂停,因为订阅已被取消。~~~

    发布者没有暂停,因为视图中有另一个订阅者,所以print(payload) 仍然有效。

    【讨论】:

    • 我没有同时使用这两个选项,但您的评论让我意识到我的帖子并不清楚:我澄清了这两个选项。
    • 所以你的问题是如果没有订阅者,发布者会pause吗?因为如果您取消订阅者,您将无法恢复订阅。但是发布者(如果你把它保存为实例变量)仍然存在,你可以再次订阅它
    • 啊,感谢您指出这一点:我也可以使用带有选项 1 的可取消订阅者!
    猜你喜欢
    • 2020-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多