在篇目一中,咱们简略讲述了如何运用combine,但是其实在的运用场景相对灵活多变,因此针对不同运用场景,采取不同的运用办法,就需求咱们把握更多的运用办法,以下是几个combine的常用办法,十分好用,主张收藏。

组合多个Publisher的成果

Publisher最常用的办法就是获取值后进行告诉回调,然后进行核算操作。单个的回调,咱们在篇目一中讲解过,这里就不多做介绍了。有些场景下,咱们需求等候两个变量都获取值后,再进行操作,比如我个人开发的满满财表,需求等候汇率跟现金都获取到再汇总核算。

针对于这个问题,一般有两种解决办法:

zip

以下是zip的运用办法:

let publisher1 = URLSession.shared.dataTaskPublisher(for: url1)
let publisher2 = URLSession.shared.dataTaskPublisher(for: url2)
​
Publishers.Zip(publisher1, publisher2)
  .sink { completion in
    // 处理完结事件
  } receiveValue: { data1, data2 in
    // 处理两个Publisher的成果
  }
​
CombineLatest

以下是CombineLatest的运用办法:

let publisher1 = URLSession.shared.dataTaskPublisher(for: url1)
let publisher2 = URLSession.shared.dataTaskPublisher(for: url2)
​
Publishers.CombineLatest(publisher1, publisher2)
  .sink { completion in
    // 处理完结事件
  } receiveValue: { data1, data2 in
    // 处理两个Publisher的成果
  }
差异

迷糊没??没错,这两个办法在运用上,一摸一样,那么该如何挑选呢?

CombineLatest

  • 当任何一个源发布者宣布新值时,将获取一切源发布者的最新值并进行组合。
  • 生成的新发布者将按照最近接纳到的值来更新。
  • 发布者之间的值能够不完全对应,只要至少有一个发布者有值,就能够进行组合。

Zip

  • 等候一切源发布者都宣布一个新值后,才进行组合。
  • 组合的值将以元组的方式呈现,包括一切源发布者的最新值。
  • 发布者之间的值必须严厉一一对应,否则组合将等候一切发布者都具有对应的值。

也就是说,如果咱们其中一个数据源改动就需求进行回调操作,那么咱们就应该挑选运用CombineLatest,如果咱们的运用场景是两个数据源的数据必须全部修正才进行回调,那么咱们就应该挑选运用Zip

定时操作和超时处理

有些场景下,数据源的数据并非需求及时回调,这个时候咱们就需求选用延时处理,办法如下:

let publisher = ...
​
let timeoutInterval: TimeInterval = 5.0
​
publisher
  .timeout(timeoutInterval, scheduler: DispatchQueue.main, options: nil) {
    // 在超不时履行的闭包
  }
  .sink { completion in
    // 处理完结事件
  } receiveValue: { value in
    // 处理成果值
  }
​

在上述示例中,运用timeout操作符设置一个超不时刻,如果在指定时刻内没有收到新的值,将履行供给的闭包。这能够用于处理网络恳求等需求设定超时的场景。

条件切换

import Combine
​
enum Condition {
  case trueCondition
  case falseCondition
}
​
let conditionPublisher = CurrentValueSubject<Condition, Never>(.trueCondition)
​
let truePublisher = Just("True Publisher")
let falsePublisher = Just("False Publisher")
​
conditionPublisher
  .flatMap { condition -> AnyPublisher<String, Never> in
    switch condition {
    case .trueCondition:
      return truePublisher.eraseToAnyPublisher()
    case .falseCondition:
      return falsePublisher.eraseToAnyPublisher()
    }
  }
  .sink { value in
    print("Received value: (value)")
  }
​

在上述示例中,咱们运用CurrentValueSubject作为条件发布者,并初始化为.trueCondition。咱们界说了两个简略的Just发布者,别离表明条件为truefalse时的成果。

然后,咱们运用flatMap操作符将conditionPublisher的条件值映射到相应的发布者,并运用eraseToAnyPublisher将成果转化为AnyPublisher类型。这样,咱们就能够将两个不同的发布者进行组合。

最终,咱们经过sink订阅了组合后的发布者,并在接纳到值时进行打印。

咱们能够依据需求修正条件和成果的类型,并依据详细的事务逻辑和场景界说自己的条件发布者和成果发布者。

自界说操作符

一般情况下,体系供给的combine操作符就能够满意大部分的操作需求,但是如果在实践开发中遇到了以下场景:

  1. 组合多个发布者的值:当您需求将多个发布者的值进行兼并、组合或转化时,能够运用自界说操作符。例如,将两个发布者的值进行兼并、依据条件过滤值、对值进行转化等。
  2. 封装常用的操作序列:如果您常常运用一系列的操作符来处理发布者的值,您能够将这些操作符封装为一个自界说操作符,以便在代码中重复运用。这样能够提高代码的可读性和可维护性。
  3. 创建特定功用的操作符:自界说操作符允许您依据自己的需求创建特定功用的操作符。例如,如果您需求履行一些特定的数据处理、过滤、聚合或转化操作,您能够界说一个自界说操作符来满意这些需求。
  4. 扩展Combine结构的功用:自界说操作符是扩展Combine结构功用的强壮工具。您能够运用自界说操作符来填补Combine结构中缺少的某些操作符或功用,以满意特定的需求。

自界说操作符是Combine结构的一个十分强壮的功用,它能够帮助您依据自己的需求扩展Combine结构的功用。自界说操作符是经过完成Publisher协议的扩展来完成的。

下面是一个简略的比如,展现了如何创建一个自界说操作符,将两个发布者的值进行兼并:

swiftCopy code
struct MergeOperator<Upstream1: Publisher, Upstream2: Publisher>: Publisher {
  typealias Output = Upstream1.Output
  typealias Failure = Upstream1.Failure
  
  let upstream1: Upstream1
  let upstream2: Upstream2
  
  init(upstream1: Upstream1, upstream2: Upstream2) {
    self.upstream1 = upstream1
    self.upstream2 = upstream2
  }
  
  func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input {
    let subscription = MergeOperatorSubscription(subscriber: subscriber)
    upstream1.subscribe(subscription)
    upstream2.subscribe(subscription)
  }
}
​
class MergeOperatorSubscription<SubscriberType: Subscriber>: Subscription where SubscriberType.Input == Int {
  private var subscriber: SubscriberType?
  
  init(subscriber: SubscriberType) {
    self.subscriber = subscriber
  }
  
  func request(_ demand: Subscribers.Demand) {
    // Do nothing
  }
  
  func cancel() {
    subscriber = nil
  }
  
  func receive(_ input: Int) {
    _ = subscriber?.receive(input)
  }
}
​
extension Publisher {
  func merge<Other: Publisher>(with other: Other) -> MergeOperator<Self, Other> {
    return MergeOperator(upstream1: self, upstream2: other)
  }
}
let publisher1 = Just(1)
let publisher2 = Just(2)
​
let mergedPublisher = publisher1.merge(with: publisher2)
​
let subscription = mergedPublisher
  .sink { value in
    print("Received merged value: (value)")
  }

在上面的比如中,咱们首要界说了一个MergeOperator类型,它完成了Publisher协议。这个类型包括两个泛型参数Upstream1Upstream2,别离表明要兼并的两个发布者类型。MergeOperator类型还界说了OutputFailure类型,它们别离表明兼并后的发布者即将发布的值类型和错误类型。

MergeOperator类型完成了receive(subscriber:)办法,该办法接纳一个订阅者,并将订阅者注册到要兼并的两个发布者中。此外,MergeOperator类型还完成了一个内部的MergeOperatorSubscription类,它完成了Subscription协议,它的作用是接纳来自要兼并的两个发布者的值,并将这些值转发给订阅者。

最终,咱们在Publisher协议的扩展中完成了自界说操作符merge(with:),该操作符接纳一个要兼并的发布者,并将它们传递给MergeOperator类型的结构函数。