我的 PasteNow 用了 Apple 自带的 Combine 库来实现一些响应式的设计,因为之前有 RxSwift 的使用经验,因此上手 Combine 比较快,加上是系统自带的,用起来也非常顺手。于是我最近还在做另外一件事情:迁移其他旧项目的 RxSwift 代码至 Combine。因为 Combine 作为 iOS 13/macOS 10.15 就引入的系统库,现在已经到了可以普遍采用的程度了,是时候和 RxSwift 说再见了。
毕竟 RxSwift 发展了那么多年,生态还是比 Combine 要丰富一些。于是在从 RxSwift 迁移至 Combine 的过程中,我遇到了不少 RxSwift 可以非常方便做到的事情,但在 Combine 默认不太方便。好在我们可以去扩展 Combine,就让我用一个 UIButton 设置 title 的例子来说明这个吧。
比如 RxSwift 里可以非常方便地将一个值绑定到 UIButton 上去:
.bind(to: button.rx.title(for: .normal))
在 Combine 里面,就只能:
.sink(receiveValue: { title in
button.setTitle(title, for: .normal)
})
如果这个 button 还是 self 上的,那么还得麻烦来一个 weak self,不然会强引用。
.sink(receiveValue: { [weak self] title in
self?.button.setTitle(title, for: .normal)
})
真的是非常麻烦呢。但只需要写这样的一个扩展,就能轻松解决这个问题:
import UIKit
import Combine
extension Publisher where Self.Output == String, Self.Failure == Never {
public func setTitle(on button: UIButton, state: UIControl.State) -> AnyCancellable {
sink { title in
button.setTitle(title, for: state)
}
}
}
然后就可以快乐地这样调用了:
.setTitle(on: button, state: .normal)
希望这则小技巧可以帮助到你,欢迎交流讨论。