Last active
October 20, 2021 02:21
-
-
Save creaaa/f2cc875bf1dcd55be821133f0f04285a to your computer and use it in GitHub Desktop.
share、makeConnectable、connect
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
share() を消すとどうなるか? | |
=> 購読が1本ではなく、2本発生するので、1も2も両方 valueとfinished を無事受け取れる。 | |
Received data 1: 1256 bytes. | |
Received completion 1: finished. | |
Received data 2: 1256 bytes. | |
Received completion 2: finished. | |
これだとPublisherの生成コストがエコではないから、share()を使ってPublisherを struct => class化し、 | |
自身を参照型とすることで、購読を1つにすることができる。 | |
ただし、1つにすることで発生する注意点もある。その例が、1つのPubに対し、複数のSubがくっつく時だ。 | |
「1つめのSubにくっついた時点で」Pubは値の放出を開始するので、 | |
2つめのSubがくっつくのが遅く、PubがdataTask() でダウンロードを既に完了してしまっている場合(finishを流している場合)、 | |
2つめのSubはValueを受け取れない(finishだけは受け取れる。) | |
Received data 1: 1256 bytes. | |
Received completion 1: finished. | |
Received completion 2: finished. | |
そこで makeConnectable() で PubをConnectablePublisher化する。 | |
ConnectablePublisherにはconnect() が生えており、これを明示的に呼ばれるまで、 | |
Subの購読が開始されていたとしても値の放出を行わない。 | |
2つのSubへの接続完了がしっかり保証されたうえで、安心してconnect()を呼ぼう。 | |
<注> | |
connect() は Cancellable を返す。 | |
1. これに対し cancel() を呼ぶ | |
2. Cancellableをdeinitする | |
のどちらかで、値の発行を止めることができる。 | |
(dataTaskPublisherで「通信がキャンセルされました」が出るのは、値が返ってくる前に、let _ 、もしくは返り値を無視することによって、 | |
購読が破棄されていたから、なのだな...) | |
let url = URL(string: "https://example.com/")! | |
let connectable = URLSession.shared | |
.dataTaskPublisher(for: url) | |
.map() { $0.data } | |
.catch() { _ in Just(Data() )} | |
.share() | |
.makeConnectable() | |
connectable | |
.sink(receiveCompletion: { print("Received completion 1: \($0).") }, | |
receiveValue: { print("Received data 1: \($0.count) bytes.") }) | |
.store(in: &cancellables) | |
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { | |
connectable | |
.sink(receiveCompletion: { print("Received completion 2: \($0).") }, | |
receiveValue: { print("Received data 2: \($0.count) bytes.") }) | |
.store(in: &self.cancellables) | |
} | |
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { | |
// connect() は、MakeConnectableというConnectablePublisherに生えている | |
connectable.connect() | |
.store(in: &self.cancellables) | |
} | |
いくつかのPubは、既にConnectablePublisherとして実装されている。 | |
Multicast とか、TimerPublisher とか。 | |
これらのSubに対し複数のSubがくっつかないのであれば、 | |
こららのSubに 明示的な connect() のコールはわずらわしい。 | |
そこで autoConnect()。 | |
これをしておけば、Subが接続された時点で(connectを呼ばなくても)、値の発行が開始される。 | |
Timer.publish(every: 1, on: .main, in: .default) | |
.autoconnect() | |
.sink() { date in | |
print ("Date now: \(date)") | |
} | |
.store(in: &cancellables) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://forums.swift.org/t/combine-what-are-those-multicast-functions-for/26677/31