在SwiftUI中暂停通知发布者

时间:2019-11-02 17:18:27

标签: ios swift swiftui

当我调用后端服务(登录,值检查...)时,我在相关视图上使用通知发布器来异步管理更新。 我想在视图消失时取消订阅通知,或“暂停”发布者。 我首先使用了WWDC19 Combine中的简单«assign»选项以及相关的SwiftUI讨论,然后研究了这个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
    }   
}

1 个答案:

答案 0 :(得分: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)仍然有效。