Объединение: переход из центра уведомлений addObserver с селектором к издателю уведомлений - PullRequest
0 голосов
/ 25 октября 2019

Я видел, как перейти к Combine с использованием Publisher из некоторого NotificationCenter кода, но не видел, как это сделать для чего-то вроде:

        NotificationCenter.default.addObserver(
        self,
        selector: #selector(notCombine),
        name: NSNotification.Name(rawValue: "notCombine"),
        object: nil
    )

Я видел, что этодоступен как издатель, но у меня нет selector, и я не уверен, что для него сделать:

        NotificationCenter.default.publisher(
        for: Notification.Name(rawValue: "notCombine")
    )

Кто-нибудь знает? Спасибо!

Ответы [ 2 ]

2 голосов
/ 28 октября 2019

Вариант использования не совсем понятен, но здесь приведен пример базовой игровой площадки:

import Combine
import Foundation

class CombineNotificationSender {

    var message : String

    init(_ messageToSend: String) {
        message = messageToSend
    }

    static let combineNotification = Notification.Name("CombineNotification")
}

class CombineNotificationReceiver {
    var cancelSet: Set<AnyCancellable> = []

    init() {
        NotificationCenter.default.publisher(for: CombineNotificationSender.combineNotification)
            .compactMap{$0.object as? CombineNotificationSender}
            .map{$0.message}
            .sink() {
                [weak self] message in
                self?.handleNotification(message)
            }
            .store(in: &cancelSet)
    }

    func handleNotification(_ message: String) {
        print(message)
    }
}

let receiver = CombineNotificationReceiver()
let sender = CombineNotificationSender("Message from sender")

NotificationCenter.default.post(name: CombineNotificationSender.combineNotification, object: sender)
sender.message = "Another message from sender"
NotificationCenter.default.post(name: CombineNotificationSender.combineNotification, object: sender)

В некоторых случаях вы можете также сделать это решение только для комбайна без использования уведомлений

import Combine
import Foundation

class CombineMessageSender {
    @Published var message : String?
}

class CombineMessageReceiver {
    private var cancelSet: Set<AnyCancellable> = []

    init(_ publisher: AnyPublisher<String?, Never>) {
        publisher
            .compactMap{$0}
            .sink() {
                self.handleNotification($0)
            }
            .store(in: &cancelSet)
    }

    func handleNotification(_ message: String) {
        print(message)
    }
}

let sender = CombineMessageSender()
let receiver = CombineMessageReceiver(sender.$message.eraseToAnyPublisher())
sender.message = "Message from sender"
sender.message = "Another message from sender"
1 голос
/ 28 октября 2019

Вы правы, когда говорите: «У меня нет селектора», потому что это уже половина дела. Вы можете получать уведомления из центра уведомлений без селектора, использующего Combine.

Другая половина заключается в том, что вы можете подтолкнуть свою логику для обработки уведомлений в конвейер объединения,так что правильный результат просто выскакивает из конца конвейера, если он вообще вас достигает.

Старомодный способ

Допустим, у меня есть представление карты, которое издает виртуальный визг, когдаоно прослушивается путем публикации уведомления:

static let tapped = Notification.Name("tapped")
@objc func tapped() {
    NotificationCenter.default.post(name: Self.tapped, object: self)
}

Теперь, скажем, для целей примера, что игра заинтересована, когда она получает одно из этих уведомлений, является строковым значением nameСвойство Карты, на которой размещено уведомление. Если мы сделаем это старомодным способом, то получение этой информации будет двухэтапным процессом. Во-первых, нам нужно зарегистрироваться, чтобы получать уведомления вообще:

NotificationCenter.default.addObserver(self, 
    selector: #selector(cardTapped), name: Card.tapped, object: nil)

Затем, когда мы получаем уведомление, мы должны посмотреть, что его object действительно является картой, и если это так,извлеките его свойство name и сделайте с ним что-нибудь:

@objc func cardTapped(_ n:Notification) {
    if let card = n.object as? Card {
        let name = card.name
        print(name) // or something
    }
}

Способ объединения

Теперь давайте сделаем то же самое с помощью инфраструктуры объединения. Мы получаем издателя из центра уведомлений, вызывая его метод publisher. Но мы не останавливаемся там. Мы не хотим получать уведомление, если object не является картой, поэтому мы используем оператор compactMap, чтобы безопасно перевести его на карту (и если это не карта, конвейер просто останавливается, как если быничего не случилось). Нам нужна только карта name, поэтому мы используем оператор map для ее получения. Вот результат:

let cardTappedCardNamePublisher = 
    NotificationCenter.default.publisher(for: Card.tapped)
        .compactMap {$0.object as? Card}
        .map {$0.name}

Допустим, что cardTappedCardNamePublisher является свойством экземпляра нашего контроллера представления. Тогда у нас теперь есть свойство экземпляра, которое публикует строку, если карта отправляет уведомление tapped, а в противном случае ничего не делает.

Вы понимаете, что я имею в виду, когда говорю, что логика выдвигается вконвейер?

Итак, как бы мы организовали получение того, что выходит из конца конвейера? Мы могли бы использовать приемник:

let sink = self.cardTappedCardNamePublisher.sink {
    print($0) // the string name of a card
}

Если вы попробуете это, вы увидите, что теперь у нас возникает ситуация, когда каждый раз, когда пользователь нажимает на карту, печатается название карты. Это объединенный эквивалент нашего более раннего подхода «регистр-наблюдатель-с-селектором».

...