Приостановка публикации уведомлений в SwiftUI - PullRequest
0 голосов
/ 02 ноября 2019

Когда я вызываю бэкэнд-сервис (вход в систему, проверка значения…), я использую издателя уведомлений в соответствующих представлениях для асинхронного управления обновлением. Я хочу отписаться от уведомлений, когда вид исчезнет, ​​или «приостановить» работу издателя. Сначала я выбрал простую опцию «назначить» из WWDC19 Combine и связанных с ней выступлений SwiftUI, затем посмотрел этот замечательный пост и модификатор 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 Ответ

1 голос
/ 03 ноября 2019

Похоже, что в вашей программе есть два издателя с именем let publisher. Пожалуйста, удалите один из них. Также self.info = payload и publisher.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() работает, а метка info нетбольше обновлений после того, как вы вернетесь из tab2~~~ Пауза издателя Здесь, потому что подписка была отменена. ~~~

Издатель не приостановлен, так как в представлении есть другой подписчик, поэтому print(payload) все еще работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...