Пример скелета для Swift Combine Publisher-Subscriber - PullRequest
3 голосов
/ 12 июня 2019

Когда я портирую некоторый код Objective C на Swift, я пытаюсь лучше понять новую инфраструктуру Combine и то, как я могу использовать ее для воссоздания общего шаблона проектирования.

В этом случае шаблон проектирования - это отдельный объект (менеджер, служба и т. Д.), В котором любое количество «клиентов» может зарегистрироваться в качестве делегата для получения обратных вызовов. Это основной 1: Многие паттерны с использованием делегатов.

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

class Service {

  let tweets = PassthroughSubject<String, Never>()

  func start() {
    // Simulate the need send to send updates.
    DispatchQueue.global(qos: .utility).async {
      while true {
        self.sendTweet()
        usleep(100000)
      }
    }
  }

  func sendTweet() {
    tweets.send("Message \(Date().timeIntervalSince1970)")
  }
}

class Client : Subscriber {
  typealias Input = String
  typealias Failure = Never

  let service:Service
  var subscription:Subscription?

  init(service:Service) {
    self.service = service

   // Is this a retain cycle?
   // Is this thread-safe? 
    self.service.tweets.subscribe(self) 
  }

  func receive(subscription: Subscription) {
    print("Received subscription: \(subscription)")

    self.subscription = subscription
    self.subscription?.request(.unlimited)
  }

  func receive(_ input: String) -> Subscribers.Demand {
    print("Received tweet: \(input)")
    return .unlimited
  }

  func receive(completion: Subscribers.Completion<Never>) {
    print("Received completion")
  }
}

// Dependency injection is used a lot throughout the 
// application in a similar fashion to this:

let service = Service()
let client = Client(service:service)

// In the real world, the service is started when
// the application is launched and clients come-and-go.

service.start()

Вывод:

Received subscription: PassthroughSubject
Received tweet: Message 1560371698.300811
Received tweet: Message 1560371698.4087949
Received tweet: Message 1560371698.578027
...

Это хотя бы отдаленно близко к тому, как Combine предполагалось использовать?

Ответы [ 2 ]

2 голосов
/ 20 июня 2019

Ваш пример работает, но это бессмысленно.Поскольку два видео WWDC 2019 о платформе Combine изо всех сил пытаются продемонстрировать, реальная сила платформы Combine вступает в игру, когда операторы применяются к издателю, изменяя сообщение или сообщения в процессе передачи до конечного подписчика.

И даже в вашем простом случае более вероятно, что send будет отправлено субъекту автоматически, в ответ на какое-то другое событие (например, настройку свойства).Если вы напишите свой пример таким образом, вы, по крайней мере, продемонстрировали бы использование фреймворка Combine в качестве альтернативы KVO (который, в конце концов, является механизмом Objective-C Cocoa, тогда как Combine является нативным Swift).

0 голосов
/ 04 июля 2019

Абонент Custom Combine также должен соответствовать протоколу Cancellable, который предоставляет метод для пересылки отмены полученному объекту подписки от Publisher.Таким образом, вам не нужно выставлять свойство подписки.Согласно документу:

Если вы создаете пользовательского подписчика, издатель отправляет объект подписки при первой подписке на него.Сохраните эту подписку, а затем вызовите ее метод cancel (), если вы хотите отменить публикацию.Когда вы создаете настраиваемого подписчика, вы должны реализовать протокол Cancellable, и ваша реализация cancel () перенаправит вызов сохраненной подписке.https://developer.apple.com/documentation/combine/receiving_and_handling_events_with_combine

...