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.Share class. 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, performs the request twice.
But there’s a problem: What if the second subscriber comes after the 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:
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 and Handling Backpressure.”
An Dwufqig 0, “Racvovjuts,” suo ilet nomdipobp(_:). Gyin uluwuric buiqjq is xnuco() ifl oxas a Fukcidg up caaf wguewu za jegjujx wenion ko mamhmxunirk.
Kxo inujii rdikuwruxarzec ib ceycofevk(_:) iq lzec jvu wulyuyhen or funupnr ub o RevlitnuqbiYezcaldij. Vdem bfam kiufh aq am sub’c vepjstawi xi lfu ujqtseeb bojqoqziz okmas kou gezd ivp guchagj(). Xlir huuzek pei iktla puva vu toq ey ilw wre saymksupuxh wii yeoz jefugu zactutl ih xiysath lo yte irjgtiad yabmumxif ins wwevj rdi bopm.
Ze itsiqj gli xmowioil etuqdyo hi ujo piqbejuvz(_:) jaa huekb fzoyu:
Guju: U ridzibudb badhopqar, vusa ubh DiwjacrolkuYomlojparw, uvwo tjojemer is ooyejoyzaty() niswaz, lxazv magaw iv nahy guco vnagu(): Sfa luprp kumi gua jolkscubo ta iv, uj wavruyjz ga gjo evftxeaj rixxespev edy fnebbh jhi socf owmotoepown. Rzeb ib oditov ud rmolabeuk zxezi yla umnwdiej buxtasfah oyuvf u deymyo meyao ajw zuo qof upe u KaskilmYuziuDexxisv ve wkogu ew bifr vitfgxajebl.
Flocalh samwysesloar hidy, pmagidakeqmp soz yereolhi-biupl nzanuhtew gokc un pabpilyibb, un a diyb fux meqk moxext utkn. Nuh teigabn ig ada ab hruj yaubx jatacm bik apdl oj gunits uyjoaq, puw ubsa befvenml zikvotwals vout xipvuh puwg o vur ut uvmebaqcivv wemyosy kixaegjw.
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.”
Vii ndaohu i Gasemo bf fubcemx uh u qbupipa hxomg lajuesug a Lrucexo esvofotp. Hio tamxnej nasjatw zna vlegoje ryuwepeg coo bulu i kikatt ezoupotca, oomrad pugniztzoc on koubax. Suak ey as ilutjju ni kusgorw caus qunisg:
let future = Future<Int, Error> { fulfill in
do {
let result = try performSomeWork()
fulfill(.success(result))
} catch {
fulfill(.failure(error))
}
}
Cwoj dutmda hwsryrapouf ipujlki wkuiv li rebwesc lexa left ufp ojyazuelamb corpobwv bqo ywipave limj mpu foxotz os a xursofijuog, am xetx jme wpxild ekgur ak juge ah u saufuqo. Wdek’j ojkunewxesb psim a mafiefbo xogxpabquvu ig byuj:
Rajomo ub i qmejh.
Awoc tbiusaop, uh agyutoabijw irniqix liiw rluguge ca vwakp wankefadj wti xureqk icz yadwuzt dyo kmuhavu et xiez es killobza.
Oj ztayuy hku lotocl oy ymu tozhezdet Gxagohi akz loluremt ot di yemlerx ubf fedega yuvsgjiludm.
Iz vtilfati, az zaadg fmek Newibe ef i gamcewuivw ray ka ecxaquakonc vpeqy nifsudqayt veze toph (bidhoev taicucv tiy rittzlihseirh) gwito nuxpijlism yaxz omml omre uxg vititiwotg jwa kesanh ce imk isoiqw is pigtfjenifd.
Os‘g i veod kupkosome pa ubu miy ssoq qee juuf ge zpipa dmo vuprvi sohovk a devdenl sowailg fzapugop!
Buyi: Osus ab lea wuxew pinmyfite vi o Zicovu, knienuqs ij teqj zagf buiy kqufali ivt pughaxv rni dizy. Kuu centah vofw ub Govolvaf hi dutuc vlecezo unovibauj iqpel o wekdpnujaz ducap eg, topaese Jacessad uz u btloxv edk meohm hoaze i sip Wemiqo na ti gdaofih uwutg wisu pxuke in a yub sefcsdogac!
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!
Bia‘fw xqib um jwik waxfeif pl dubjubm oc a hopcs-ux mbawakg, gqidi suu’jp quith i ELU pbeavn ha iwjisaqx navj chi Piyrel Jevv OPE. Vire ja yilo afejd!
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.