Skeleton Example for Swift Combine Publisher-Subscriber

ken*_*nyc 6 publish-subscribe swift swift5 combine

As I port some Objective-C code to Swift, I'm trying to better understand the new Combine framework and how I can use it to re-create a common design pattern.

In this case, the design pattern is a single object (Manager, Service, etc) that any number of "clients" can register with as a delegate to receive callbacks. It's a basic 1:Many pattern using delegates.

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()
Run Code Online (Sandbox Code Playgroud)

输出是:

Received subscription: PassthroughSubject
Received tweet: Message 1560371698.300811
Received tweet: Message 1560371698.4087949
Received tweet: Message 1560371698.578027
...
Run Code Online (Sandbox Code Playgroud)

这甚至与Combine预期的使用方式相去甚远吗?

小智 1

自定义组合订阅者还应该符合 Cancellable 协议,该协议提供了一种将取消转发到从发布者接收的订阅对象的方法。这样您就不必公开 Subscription 属性。根据文档:

如果您创建自定义订阅者,则发布者会在您首次订阅时发送订阅对象。存储此订阅,然后在要取消发布时调用其 cancel() 方法。当您创建自定义订阅者时,您应该实现 Cancellable 协议,并让 cancel() 实现将调用转发到存储的订阅。 https://developer.apple.com/documentation/combine/receiving_and_handling_events_with_combine