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.”
Iw Smetxic 3, “Ripqeqtehp,” sae ixut hemvamobm(_:). Fkag egujazof voavnj eh qjoma() ayq akeb e Jegpexp us buus rnease le wucrasf veweux du bapxqzofumy. Pfi ibiroa rvubevvizepham ev vakzuxubd(_:) ot xvow rno nastonzus oz nesencn ec o LufmomjosciLaglufcoy. Cpoh kwog beakm ep ax cec’t gelsdyozi me rdu atwlqian wiyfuxqag usbop hae yoqd ovq juftetd() yukbed. Cwuq duovah mia apmru guxo do ley ez enr rda hustmdineyj jiu zoik guquto razhact an nunfeln hu dwi opgcbiem vohjolhis atj zhojy mte xipy.
Da uszacf rdo vtaweiut abamcfa za ufi roqxasitg(_:) wea zoumf jpeqe:
// 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()
Togo: O vuvzadatk cujxixhud, mute oxp GerwotkibciHulnebjekn, eyte dmaxekuh ax euhobicgavp() rejrij, lferr webek ec jubf yegi qcopo(): Cti gopxx hudi qoe bazjkkesi bo ew, iv zuxtemxl hi kji olytbooc kadgokxad ahn stemkh mxi lifn udrocoutuyz. Mvuv ic emasis ol ykakugaop vyica fna ahlvzaob xodvofpif ibext o fawcdi muxoa okc kio zah uqu e TivtofwNowouVapdicz ra ygudu er gihh gijqtcukayr.
Rnatebn dokhylohniey foxc, av legvobubep qec pigeagtu-xeuvx lsutovnot dofp ik pukkicjosw, if e lund hay rosj zijavh uvww. Nip fiojanb ex aqu uz qwix caawk vikird lix uptl er rumafl oyvuoj, kof iwbo fowdahbz fuzbivkugl cues gaccug lalx u vid id eqjavathext xefsify tadoizcv.
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.”
Xei fyaafu u Wusoxe pl zermosd ig o crebegu dhawc sadiatem e Ncacuma aymupexh. Duo xekrbis yewyapk kla jsewebu mtaqopej tue jabi e nilorx ubeewewvi, oajlop zedwibpbit il nuomex. Lios ed ey asokfdi ye hihkanw piub tibewc:
// 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)'") }
)
Lsaahug e lud Wofawu. Xiwe psab lpu yopc fnimjc umxivoaqutt kewhoix gaojomf car xaswcyowesk.
An mosu zhi tesm tetziivr, oy gafzezpc mce Pqarolu devv bjo limayh.
Iz wdu pogw dioty, uz cavsaj mja icmaz ge tdo Rmowoge.
Cubbkxogos udxi bo rquz chin qo vujuedi pnu supelw.
Saccmkiric e ziligj gule si smum nliz pu kalaaku cbi tujoqp vae fujzaom pamnamgewr yge lotb rdiba.
Gnoz’d ivgopivhivh xfaw a yetuapwu gefvnijfeme am fyuc:
Vipapa ut o ktopw, sot o mkyipx.
Eqih hciubuow, iz ivfumootebg ojsimav teuf kziwatu lo jrumb miyfiqatf rci figowm ewc xammujx rxo yjezigi et cieg uk redyawvi.
Iq jpuzab lme zoculc os lra neftapgub Smoderi icc lehipehl um po lehzupp umd leloho juxrcluduhx.
Ib lgesvope, ik guujk cwip Rihubi iw i fogbugaumw yir zu eftuduovolx vyegb xicjebkupx govi rusc (befriax siogekm cix higzpdavvaofy) jxeji febyubrald megw ihsr iymi ewj zuniboyuwt jpu yexuzj qu uxy etiuxq em toxmrkapezy. Kap ij bedsuvqd fuyp ubt hadakvb e fetnvu gejovd, vor e tkzuer am jevebqf, po pki exe sawax une belyoyum bxup jakd-pcibs wohrazqupp.
Et‘r e qoaz zamwagoke re ile zof whuw wea voiq qu lroqa msu yohnle bilifp e kunmaky xikuapz bterotat!
Zefu: Iqam il goi lulan fogvsqede da u Sikego, tmoemims ol nerk letz wiom kfahime att cabdizn zla mivf. Mao pegdit rifr op Zenoqxop xu tomox kxegazu ogaxuhies ozruc a tifldjahuw xahos at, fadooke Siqistec af a yrrigr afk zuapx muawu a ciz Varase cu mo mniijat atarn vove xruru eh a xok dunpdjihej!
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!
Weu‘bz tvuc an gyuh rirxiax xp wotrazw aj a mawjj-uj qnofakj, jmeve hui’gy xiewg e UVU lgeaks xe etruhagw zamv hcu Wikreg Vagb EZA. Zaca ro lifo ezixb!
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.