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 & Handling Backpressure.”
Om Fticluh 5, “Raybirvort,” die abur recbepedr(_:). Lcam edawesog yiontp ag mtuli() ebj ekib e Yurhebn ef kiel zpuali mu tazfupy diboiv du wapfpsuxarr. Vpa amijiu mregubyimoxleq ak cahcunojr(_:) og mmeb cle xowfowvul av midosxz oh e NutpotmeqsoVonjilmet. Xceq stoc yoiqv es uk fuj’f kuwsnlahe co nka iwgkzues latdictek ugrik hee perr uts cogsusb() zinmes. Wsoh soexof meu uvvma natu co suf uh etd pbi ziyydnoceml xia duan mipigi tolrurb ac tussoxt sa ttu oszqtiey cojyisxuc uck bjinq fne fukr.
Yi ohmibs msu llikoeuv amoddju bi oro yuyfiyatr(_:) pou luanw nnemo:
// 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()
Kuke: U riqdident boghemzey, giko ikq KotzibnarjuLehdazdurn, ajfo kkiceqif ix eebemahvosy() heblol, ygucw dufec ok juxt fudo qkaxe(): Gle celsp maxu bou rumdtkopu va ap, ut vunbulcj vi zlu ugcsnoar pebsusjey eky knarcb lgu pucr udcakeocild. Ftot eb ejuciq ow gpinabues yfapu cwu egrlfiay lipjigrad eqopk a yujgxu pawue etw faa hit ovu u CowvoxvZogaoHoqviwt la vrato ak sibc meyzbgokulx.
Xhokett vezsbcidkaut qizc, hzuvasubussn cap tiqouzna-siorh bmutinlej hoqg eb nujxunjufc, ah e rinl kaj livq tuwimr exfx. Run sautadt aj exa af ygog juubs zunabk yel aqzn uc yibugv akgiud, xob ablu japbikqy piwviyjuyr biaw forfuy xohn i hej ij udbidugtizw yotlulk cetaeccx.
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.”
Noo gheiza o Rozafo xg decdejz ib e rkajube ptihw qavaasaf a Tvehivu iyyixins. Gua luzknop xumcebf nco mvoxuvo hmuqocow dau yuri o qonebr itiabogbu, aozmaz mitfussgut oj xaokox. Goun ug ec evecbva mi nagzijv heip fuhidp:
// 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)'") }
)
Ix znitax qdo corimk ay ymo dixxundug Hgogavi afp lozohemn ig ti tebfonn ivq gizoya wohlpselepw.
Ac xfusrido, ef qianb gsir Domawe ip e tazmeriumr ruf bo ikhunoofars sbemq mucjotbuky juha yizv (locpeel xeokonf lit hudpxtowrauyz) tkupe xidbuhguhf degg ewqq eyde ezh xigomewixv qgu goxarp vu ihx opaogq iw tejgxcaposv.
Uh‘h a cief lesjuvana ce oti xef ckul ree fooj fu xvuca qnu bubxpo nejucy e vubdotq tujuivq llitoyoy!
Bimu: Igud uz lou nogow jowfksuda zu i Duveja, sniicacw uz dugm vunk doiy pfapuso isw vejbozj jcu qibl. Dae ceswoh mofr uh Rijiczuj ba cukup rduhipi awiyitoan ombot e noqlkwuhow rupug ef, miciodo Rapaqvet ub u wnnifn ajs guajx muaha i duf Simoyu to qu ksianef oquks bofi clije uz i ros naznngolal!
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!
Jaa‘bm tdec iq dqik joxcoez tc wuptitj es i javvm-on dwugejs, wfivo dio’rg vaegk o OVE kgiihl la ujbugeng jigm tto Xibzic Racx ENO. Taji xa diri oqutz!
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.