スレッドについて
.receive(on: DispatchQueue.main)
を用いるとメインスレッドで受けれるので良さそうである。
.receive(on: RunLoop.main)
は、スクロール中に止まるらしい(Timerがスクロール中に止まるのと同じく)
UIControlをCombineで購読したい
デフォルトで用意されているものはない。
CombineCocoaを利用すればできる模様。
UIKitでもCombineしたい!を叶えるCombineCocoaを試してみる
もしUIKitでUIを作りCombineで非同期処理を行うなら絶対にCombineCocoaの導入を検討するべきではないだろうか
CombineCommunity/CombineCocoa
自分でPublisher, Subscriptionを作っても良い。
zafarivaev/UIControl+Combine.swift
Observe UIButton Events Using Combine in Swift 5
Subscription
Publisher
RxSwiftとCombineの対応
非常に便利なチートシートがあった。
RxSwift to Combine Cheatsheet
Timerについて
上記チートシートにも少し書いてあるが、Combineでは以下のように実装する。
- 以下、引用
func startCounting() {
isTimerRunning = true
cancellable = Timer.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
.sink { _ in
self.count += 1
}
}
上記を軽く解説すると、Timer.publish
はConnectablePublisher
(connectメソッドを呼ばれて初めてイベントを発生させる)というものだが、特にそのような機能を利用しないので、.autoconnect()
を呼んで普通のpublisherのような振る舞いに戻しているようである。
setFailureType()について
- 以下、引用
extension Int {
enum NumberConvertError: Error {
case convert
}
func toAnyPublisher(string: String) -> AnyPublisher<Int, Error> {
guard let number = Int(string) else {
return Fail(error: NumberConvertError.convert)
.eraseToAnyPublisher()
}
return Just(number)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
}
}
PublisherのFailureがNeverの場合のみに使用でき
指定したError Protocolに適合した型を出力するPublisherに変換します。
上記の例ですとJustからJustに変換しています
逆にErrorを含むパブリッシャーをErrorを返さないパブリッシャーに変えたい時は、mapError(_:)が使える。
自分独自のオペレータを作りたい
extensionを利用して作ることができる。以下は一例。
import Foundation
import Combine
extension Publisher {
func throttleForTap() -> AnyPublisher<Output, Failure> {
return self.throttle(
for: RunLoop.SchedulerTimeType.Stride(Const.tapThrottleDuration),
scheduler: RunLoop.main,
latest: false
)
.eraseToAnyPublisher()
}
}