Publisher
概要
code:swift
init(_ attemptToFulfill: @escaping (@escaping Future<Output, Failure>.Promise) -> Void)
Promise とは、Publisher が 将来 エラーもしくは値を publish する時に実行される (つまり、Publisher が値を publish した時に、publish した値を受け渡されることが約束 (promise) されている) closureであり、 A type that represents a closure to invoke in the future, when an element or error is available.
下記のようなシンプルな型に対するエイリアスとなっている。Future における、最終的には 正常終了/失敗 を送出する、というのは、Result 型で表現されていることがわかる。 code:swift
typealias Promise = (Result<Output, Failure>) -> Void
注意点としては、Future の処理の実行タイミングは、初めて subscribe されたタイミング ではなく、Futureインスタンスが生成されたタイミング になるという点。実行を初めて subscribe されたタイミングに遅延させたい場合は、Deferred を利用する。 使い方
completion handler を用いた、API 呼び出し時の典型的な非同期処理の記法を Future を用いて書き換えるサンプルが以下にある。 completion handler は、ある関数の引数に受け渡して実行すると、その関数の実行終了時に呼び出される closure。このパターンは下記のように記述できる。 code:swift
func performAsyncAction(completionHandler: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline:.now() + 2) {
completionHandler()
}
}
これを Future を使って書き直すと下記のようになる。promise が completion handler の役割をになっている。 code:swift
func performAsyncActionAsFuture() -> Future <Void, Never> {
return Future() { promise in
DispatchQueue.main.asyncAfter(deadline:.now() + 2) {
promise(Result.success(()))
}
}
}
code:swift
cancellable = performAsyncActionAsFuture()
.sink() { _ in print("Future succeeded.") }
概要
値を送出して終了する Publisher。エラーは送出しないし、nil ではなく常に値を送出する のが特徴。Publisher の operation のチェーンの中で、単一の値の Publisher に変換したい場合や、単純に publish の起点としてとある値を用いたい場合なんかに利用できる。 使い方
例えば、エラーハンドリング内で以下のように利用できる。
code:swift
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: myURL!)
// the dataTaskPublisher output combination is (data: Data, response: URLResponse)
.map({ (inputTuple) -> Data in
return inputTuple.data
})
.decode(type: IPInfo.self, decoder: JSONDecoder())
.catch { err in
return Publishers.Just(IPInfo(ip: "8.8.8.8"))
}
.eraseToAnyPublisher()
概要
使い方
インスタンス生成時に即時実行される
結果は1度しか送出されない
複数回 subscribe しても同じ値しか渡ってこない
そのため、subscribe する度に (したタイミングで) 内部の非同期処理を実行したい 場合には、Deferred で以下のように包んでやると良い。これで、Future 内の closure の実行は subscribe されたタイミングに遅延され、さらに subscribe の度に closure が実行されるようになる。 code:swift
func createFuture() -> AnyPublisher<Int, Never> {
return Deferred {
Future { promise in
print("Closure executed")
promise(.success(42))
}
}.eraseToAnyPublisher()
}
let future = createFuture() // nothing happens yet
let sub1 = future.sink(receiveValue: { value in
print("sub1: \(value)")
}) // the Future executes because it has a subscriber
let sub2 = future.sink(receiveValue: { value in
print("sub2: \(value)")
}) // the Future executes again because it received another subscriber