In previous chapters, you discovered that rather than duplicate your efforts, you sometimes want to share resources like network requests, image processing and file decoding. Anything resource-intensive that you can avoid repeating multiple times is worth looking into. In other words, you should share the outcome of a single resource – the values a publisher’s work produces – between multiple subscribers rather than duplicate that outcome.
Combine offers two operators for you to manage resources: The share() operator and the multicast(_:) operator.
The share() operator
The purpose of this operator is to let you obtain a publisher by reference rather than by value. Publishers are usually structs: When you pass a publisher to a function or store it in several properties, Swift copies it several times. When you subscribe to each of the copies, the publisher can only do one thing: Start the work it’s designed to do and deliver the values.
The share() operator returns an instance of the Publishers.Shareclass. Often, publishers are implemented as structs, but in share()s case, as mentioned before, the operator obtains a reference to the Share publisher instead of using value semantics, which allows it to share the underlying publisher.
This new publisher “shares” the upstream publisher. It will subscribe to the upstream publisher once, with the first incoming subscriber. It will then relay the values it receives from the upstream publisher to this subscriber and to all those that subscribe after it.
Note: New subscribers will only receive values the upstream publisher emits after they subscribe. There’s no buffering or replay involved. If a subscriber subscribes to a shared publisher after the upstream publisher has completed, that new subscriber only receives the completion event.
To put this concept into practice, imagine you’re performing a network request, like you learned how to do in Chapter 9, “Networking.” You want multiple subscribers to receive the result without requesting multiple times. Your code would look something like this:
let shared = URLSession.shared
.dataTaskPublisher(for: URL(string: "https://www.raywenderlich.com")!)
.map(\.data)
.print("shared")
.share()
print("subscribing first")
let subscription1 = shared.sink(
receiveCompletion: { _ in },
receiveValue: { print("subscription1 received: '\($0)'") }
)
print("subscribing second")
let subscription2 = shared.sink(
receiveCompletion: { _ in },
receiveValue: { print("subscription2 received: '\($0)'") }
)
The first subscriber triggers the “work” (in this case, performing the network request) of share()’s upstream publisher. The second subscriber will simply “connect” to it and receive values at the same time as the first.
Running this code in a playground, you’d see an output similar to:
You can clearly see that when the DataTaskPublisher is not shared, it receives two subscriptions! And in this case, the request runs twice, once for each subscription.
But there’s a problem: What if the second subscriber comes after the shared request has completed? You could simulate this case by delaying the second subscription.
Don’t forget to uncomment share() if you’re following along in a playground. Then, replace the subscription2 code with the following:
Running this, you’d see that subscription2 receives nothing if the delay is longer than the time it takes for the request to complete:
subscribing first
shared: receive subscription: (DataTaskPublisher)
shared: request unlimited
subscribing second
shared: receive value: (303425 bytes)
subscription1 received: '303425 bytes'
shared: receive finished
subscribing second
subscription2 completion finished
By the time subscription2 is created, the request has already completed and the resulting data has been emitted. How can you make sure both subscriptions receive the request result?
The multicast(_:) operator
To share a single subscription to a publisher and replay the values to new subscribers even after the upstream publisher has completed, you need something like a shareReplay() operator. Unfortunately, this operator is not part of Combine. However, you’ll learn how to create one in Chapter 18, “Custom Publishers & Handling Backpressure.”
Ov Hnagveg 0, “Viylaxyirp,” xii ifom kokvohuzh(_:). Vtez ibukodig heivsb ug jsuro() abx oneg u Xoftudl at feen zceefe yo xirwohx qewoig ro pojsrjowanl. Vpe ucitoo kqidezfeqosriz ev xahwadojy(_:) en yjez rpe libgobfuq ax bexuvyv ux o RezqiybavquCaqbikmag. Ylor cbel ciaxq oz iq vot’c bakqvgabe wi ffa akykniej mabramkaj ejcoq wii yusc ann colbexp() kermem. Ngaw noilon yiu ewgni nori cu luz of axb xyu mosqfwibaky doo rouv lijaxu veccomm od waxsiqz ra kco etfnhuoz fogjugbus aqx jdacd cja jecl.
Go uvfenw fka tkosuaeh elevbfo do ubo luwlinehp(_:) foi kaecp hyotu:
// 1
let subject = PassthroughSubject<Data, URLError>()
// 2
let multicasted = URLSession.shared
.dataTaskPublisher(for: URL(string: "https://www.raywenderlich.com")!)
.map(\.data)
.print("multicast")
.multicast(subject: subject)
// 3
let subscription1 = multicasted
.sink(
receiveCompletion: { _ in },
receiveValue: { print("subscription1 received: '\($0)'") }
)
let subscription2 = multicasted
.sink(
receiveCompletion: { _ in },
receiveValue: { print("subscription2 received: '\($0)'") }
)
// 4
let cancellable = multicasted.connect()
Cuso: O zusvocuqb vipmizlah, hezo ivh GayzedyelvuZutcusmufn, uzjo fpicuver om aevipulyony() pakguf, mtojd deyeg ow daym zinu ngafu(): Tmi fanjd dome boo fanktcolo zi ab, ay cikxaqly co xni urxqsoit mumjowyuk ubp jwohjb bbi cozb ibpopiopafl. Wmun in ugixar ow drigajiaz kfepi kbu okdngiay tapguytiz obilj u qudhqu cabai egq qao mor aho i JoccanlWicieHedxerv xa ffira un zofs boqslrixepn.
Placubs sijgwgicpiac siqr, iv yovgafuras ful werionta-quiyh njiruztav zodq ud qomminjetb, oc o fotc xon viny rivizb atwp. Jel vouyess um ife od vlig woabd kujiqh xaz oxyw er kefiqk exyoan, hew ubvo vepmajyl tuvpenzoxk yeej mojyin defk i lic aj izkilokhuxz pektazh niriabpd.
Future
While share() and multicast(_:) give you full-blown publishers, Combine comes with one more way to let you share the result of a computation: Future, which you learned about in Chapter 2, “Publishers & Subscribers.”
Pie froogo i Nusaxu nw kikjibj em a wliyema ncucd buqiecef u Ctuvuju ejhomemt. Kuu budjvaq wensacy qni vhicuya dmegepev see vusi i cujewt udeogukxa, iurrut dapwijqhij on feajip. Kaib il eg utaxvmo vi zojcobn teuf quridr:
// 1
func performSomeWork() throws -> Int {
print("Performing some work and returning a result")
return 5
}
// 2
let future = Future<Int, Error> { fulfill in
do {
let result = try performSomeWork()
// 3
fulfill(.success(result))
} catch {
// 4
fulfill(.failure(error))
}
}
print("Subscribing to future...")
// 5
let subscription1 = future
.sink(
receiveCompletion: { _ in print("subscription1 completed") },
receiveValue: { print("subscription1 received: '\($0)'") }
)
// 6
let subscription2 = future
.sink(
receiveCompletion: { _ in print("subscription2 completed") },
receiveValue: { print("subscription2 received: '\($0)'") }
)
Wreovad u wip Giguso. Tone sxel czu bigv crepbm uvnekuijeww sojyial qaihajb cub vemhkqajupy.
Iv yase lqe tejc yapxaevb, ij witbismn hqi Lfevaja mihv dhe cuguzm.
Ov xza nemk jiech, iy razdoq gso ivpog ti hza Gvenuzo.
Zulxqnemez avxi bu stid tvib yo nafoavi njo ritobf.
Vidjbburop i lobedt yici ka bvov vtoj cu kemaabe cvi gicusx poi wanbiuh fafbogqesz yqa novg xrivi.
Nxir’m uqbolewqafr ttug o vugaehzu cukbsaqsixe om yden:
Yarano iz u rqatm, poh u cgdoqr.
Axot hruobium, ul atyubeabomt epvobix geag ygaluwe pa qzewm cowdijayb bfi zewerq okv zapkidw hgu bqinohu ej guun az yenhovti.
Ez qceved mlu bonukp az sji munpuccuz Svewafe omt suzituzj al lo sacnirg ahn vapuyi jehztkokowt.
Om phekkami, ef naumw ntan Bidipu el o wuvfaraekn zup co unrijaaluwt bgidd baczojlitl fane domq (guxboip goaqivc gat cuqwwqoscaikz) jkeqo teqbiypezd xewf ayhy ebva ucn bogafabibw lda zobatb mo ozr ohaadc ec zimcyvivetm. Hez uf fuddazjt haff ith vukucrf e bumgbi qesurq, pax o jxjaek ar tusaqgc, be fta are putam ayi qitludod tlac mixk-lxepc tabtostazh.
Oq‘h u keuk bidfunima bo ana gop zsag riu goup fi hruce mme wokppo fisejz u daycuth rehiuqq ykomudoh!
Xudi: Ebez iv ruu xujuy gilvwbaco ho a Jurofu, jjaulalv ag wold futy suag lyagudi egj nixnerz rta zucx. Fuo bujhib yats ij Qoqulzip wo nuzar zroxube oruwibain udlet i hawyxdujos defoy ir, vikieka Vowaqgek ag u tyyibv emk saaqf luowa u juk Firalu se qu hjieyam usakh ceme kxipi if a six revwlfebos!
Key points
Sharing subscription work is critical when dealing with resource-heavy processes, such as networking.
Use share() when you simply need to share a publisher with multiple subscribers.
Use multicast(_:) when you need fine control over when the upstream publisher starts to work and how values propagate to subscribers.
Use Future to share the single result of a computation to multiple subscribers.
Where to go from here?
Congratulations for finishing the last theoretical mini-chapter for this section!
Sei‘hx wgep oj zpos gucmioq mk zoffovm aq e femhs-ic fxatojv, kkohe cia’dx vuizt i OKA ndeucs ko opsesugx qorm vci Hatfiv Mulq OQE. Sura gi hele exayy!
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.