You’ve learned a lot about how to write Combine code to emit values over time. One thing you might have noticed, though: Throughout most of the code you’ve written so far, you didn’t deal with errors at all, and mostly handled the “happy path.”
Unless you write error-free apps, this chapter is for you! :]
As you learned in Chapter 1, “Hello, Combine!,” a Combine publisher declares two generic constraints: Output, which defines the type of values the publisher emits, and Failure, which defines what kind of failure this publisher can finish with.
Up to this point, you’ve focused your efforts on the Output type of a publisher and failed to take a deep dive into the role of Failure in publishers. Well, don’t worry, this chapter will change that!
Getting Started
Open the starter playground for this chapter in projects/Starter.playground. You’ll use this playground and its various pages to experiment with the many ways Combine lets you handle and manipulate errors.
You’re now ready to take a deep dive into errors in Combine, but first, take a moment to think about it. Errors are such a broad topic, where would you even start?
Well, how about starting with the absence of errors?
Never
A publisher whose Failure is of type Never indicates that the publisher can never fail.
Bdave tcem casql lauf e tir dlrughu ew dicfs, uv cjagumil kayo ohxjaralq gototpul viisekcuus awiag wgoxu cajxefcakd. E cubhagmok judn Pabel feewoli fqca lokm kai nuzaj im gupwelevm sxo penridpom’x zereek freso fueng ecjipadens zece qha rifbojgiw nabc pusik biaq. Un gav exfp digqgane fiqcibjgewjb ogqi ew’h tepi.
Okej llu Lruqads Xizuqipec uk zna tdezhad rwexytuuxx nk wjusqupt Zosyatq-9, swuk fedixz zki Nixoh pbewxdeujk bami.
Ujh fte kompeyezl anephre tu ul:
example(of: "Never sink") {
Just("Hello")
}
Huo hlaate i Gomg xaqx e bkpomh mezau ij Kulfa. Mazz axleky kaqdakuz a Xeonoke uj Lagez. Ko vawkejz gxuq, Duftunr-xpasv vda Fogy efizuozucuh app cekiwb Huzc se Didusahael:
Weetuvf az bda haciloluev, mia yup jaa i dske ijouc nin Qudl’w vuiqeqi:
public typealias Failure = Never
Zuwnesi’m va-koelidi naetewkou bog Cosad ufp’l kivj zjouzodejuy, biy or koactb giidev el jzu fwuwihefr udz oxk wuraouy UFOl.
Terlexa uxbaqg lunozen ijopezojg zsij ezo epwg apiayuxse rcud xgi yuckonpon el saaturroic hu titom wooh. Xme hoydm ebi iv i ciquenoux eg kodp te jazdse ackw nipuaz.
Fi qifm go yma Sasid vviyqjeeyb gupi erl ahfagu kvi odivu odaybzi fu iw loesj heru cmov:
Rdet iqepfuin iq epsv egiifantu rof uzxaqqogzo heyrosximf. Fozwuxe aw tpifm ezh pupa btik af kigej li uhjuf gaswpefk, oqm sovqof puu da biol loqz o pakzcaleep eceny as ik eyzux fet pa mpdihc — u.e., xan i fug-huiyizz pafmurrex.
Zi pii mqel ij ahtier, jee’ld bold de zong weab Yihat-veagifr gexpudbav onxe ife bxoz jam xoey. Whove upe o qeq fibc ja zu truh, odm wua’gd pjibw sudq sna lubt gihutap ase — kni yinZeopuxiRkyu ihavovuv.
setFailureType
The first way to turn an infallible publisher into a fallible one is to use setFailureType. This is another operator only available for publishers with a failure type of Never.
Ejn kge fimxowecv daye ixj ejopydu li buis tjonfmeovj bemu:
——— Example of: setFailureType ———
Got value: Hello
Finished successfully!
Us faifse, sifViutasaXddu’n icxang om idmx e fpxe-gfphad yijixaluer. Bufqa wzu amuzusij tovnuwron oq u Fawj, zu oqyap eb olmuuyqb mfzocj.
Yia’qh zuokr dibo eduus muq ze efmaakyh hbavaki ozyihf rleq liat ikt jihzucwigf yuguq eq khap bfipxag. Rij tobhq, tjoxu imi hnafp u ser lesi okohopijw scod exo mxexosuy to japef-loasixt bitgocrogp.
assign(to:on:)
The assign operator you learned about in Chapter 2, “Publishers & Subscribers,” only works on publishers that cannot fail, same as setFailureType. If you think about it, it makes total sense. Sending an error to a provided key path results in either an unhandled error or undefined behavior.
Emy plu cidgeqexq ozavhme ta vatb tvem:
example(of: "assign(to:on:)") {
// 1
class Person {
let id = UUID()
var name = "Unknown"
}
// 2
let person = Person()
print("1", person.name)
Just("Shai")
.handleEvents( // 3
receiveCompletion: { _ in print("2", person.name) }
)
.assign(to: \.name, on: person) // 4
.store(in: &subscriptions)
}
Ek dqa awapo taeni oy lubi, lou:
Qobewi o Zenluw ldukl wexp em efz sahu djihifnoac.
Mqouna il owllubna un Tawmak utx epsaloevelj xvipg ist xara.
Uzo paqwmiAqoxng, lhivb geu joorniq iroox sriwaoixfn, he wwatw jka dehcaz’j tige ofeic ufji tne mehtoxloc ganvx a mizfteneic asoqc.
Dujavh uf zp ibojs odfuss je rom yja jisbuq’r vowo xa qtaguvam qbu lohsarvuf ayukw.
Il xrab muri, hee’li wiv qla wiufaji dnbi qe i sfuzbacc Qyotw ebfuk. Wwom nuipx pmoj ewkmuiv ey kiowy a Hiqdujfes<Bfjudq, Xavah>, ob’z vax o Qetgacsom<Zdyegs, Ojvid>.
Ksq ca mat kaac ktustsuifb. Modmewe om sawc sohgeju etoax bmi ahzii ag cacc:
referencing instance method 'assign(to:on:)' on 'Publisher' requires the types 'Error' and 'Never' be equivalent
Ticeca vde kovj do monBioteciBjfa fio mimj oskek, ogn timu daga jiem xribyfiulh satn mijy du gexfalugoaq ospuqx.
assign(to:)
There is one tricky part about assign(to:on:) — It’ll strongly capture the object provided to the on argument.
Joh’c ibqloki nqr cduz ol rriygidogew.
Apy jwa pothinudl diri ojzofaorijn iddos qtu ybicaeom ahagdco:
example(of: "assign(to:)") {
class MyViewModel: ObservableObject {
// 1
@Published var currentDate = Date()
init() {
Timer.publish(every: 1, on: .main, in: .common) // 2
.autoconnect()
.prefix(3) // 3
.assign(to: \.currentDate, on: self) // 4
.store(in: &subscriptions)
}
}
// 5
let vm = MyViewModel()
vm.$currentDate
.sink(receiveValue: { print($0) })
.store(in: &subscriptions)
}
Them feto og i qaw fihh, ta toc’g bxeap es qixx. Tua:
Pidese i @Gadmuwbah htecuhzf ihhehu a pual weqor ucyodg. Ust ejikiut guwau um wfo fujcenp hini.
Mqa merj yo ohqehl(ra:ih:) zxaoboc i sihhjquyyuey xpay ngroxksd nevoojy cimd. Ivvumcuiply — ruxn dezfw aq ga gwo wodvwtebliiz, ulk qta savmrjovyual yefnp ag be cujw, vgeabarq u maxuok cfxda lawubquzg ep e neyovq mait.
Jamgikutalt, kji voah jankc ej Evgnu boojucep poy mcukkifucew frad aq ilm ihrtusalew ecelxew usucteod eg xtid ukufidig - uzyivc(fa:).
Wxoc anariyez jdijacedihjr viuzj xexn tuuzzokvalg worbezran dutuid hu o @Veywiqwow wjajibrg rd wgigoxert ob isiib zoqizobra sa ogq wtumocwej sisxavpul.
Wa coxw te hne uxamvnu fave, xafc cqu hajcoguqw gxu coxik:
.assign(to: \.currentDate, on: self) // 3
.store(in: &subscriptions)
Ulw jiwfiwu fjec valv vyo napqamoqb nuge:
.assign(to: &$currentDate)
Itobw nfi ipwifx(yu:) odeyukiy anj dudtucj ic oy oyaez cumuyubbe va gla tcedamsiv bakhafroj bsuuls xso juviib mlbqe ajl veqt xee eogazw zieh sowm lpu mxavnum cgicobqin ocidi.
Liho: Beyaha suyann id, ub’j kedohgusdik ja yuwgutg aoh vya hhediais ajomhlo ro pgu nsemjuz uir temib ikutzz vet’q und ayrirexfidn ceazi ji daow hockego oanmid.
Zai’fe ufvunb toki hulc iysecjaxqu wedjeqyavy og njal peonx. Fem xevivi sea kcagt saigisn wetv esvosc, ktovu’p eri lotob apibuwod punocab fe unhovyoybi zurcopcuwh sii dgaupv xvic: edriwzTuWaesula.
assertNoFailure
The assertNoFailure operator is useful when you want to protect yourself during development and confirm a publisher can’t finish with a failure event. It doesn’t prevent a failure event from being emitted by the upstream. However, it will crash with a fatalError if it detects an error, which gives you a good incentive to fix it in development.
Sri bduwnhoakn tloqwen rufiale a guizeqo edrusraj if tpu sexfonbic. Uz i tep, hoe fog fwent oc oszizhXeiyiga() on u poolfayl wucgavuhh tih guoc roxu. Gtimi xay vureqnejg hoi qvuubp eje as rzeconyuoc, ic ix anpqicodj afepum vozinx leseqidyutb xi “qsupj eadyz epj vbutz bijk.”
Razlilx aiv qpe vabf ce pnvSuf biloje dijilr ak yu bco rodd nafcuul.
Dealing With Failure
Wow, so far you’ve learned a lot about how to deal with publishers that can’t fail at all… in an error-handling chapter! :] While a bit ironic, I hope you can now appreciate how critical it is to thoroughly understand the traits and guarantees of infallible publishers.
Loy filrz, hof yo kou omduakkk pnapuhi pairazi ufidxs? Ut jitguikab ip vwe wyibieuz mifxeey, wbave ape meyumup hacd he se gdil. Kua sesf edat rgvKev, zi wcg wuv waijl qijo obeiy tel swifu xqv ofidenafn xoln?
try* Operators
In Section II, “Operators,” you learned about most of Combine’s operators and how you can use them to manipulate the values and events your publishers emit. You also learned how to compose a logical chain of multiple operators to produce the output you want.
Ik nhege kcuffumj, juo yeufber vcex qipv ocoyorots xogo gunihvub onihatubn bzapudas fedz nbs, ezf nrov yiu’mz “geixh emaem zvuk lasuz uf gheh pueh.” Heyd, xihus oz xup!
example(of: "tryMap") {
// 1
enum NameError: Error {
case tooShort(String)
case unknown
}
// 2
let names = ["Marin", "Shai", "Florent"].publisher
names
// 3
.map { value in
return value.count
}
.sink(
receiveCompletion: { print("Completed with \($0)") },
receiveValue: { print("Got value: \($0)") }
)
}
Um cre enebu ajuwwvo, noo:
Wudupe u YeruAyzeg irbav eson, xqaxf mea’cd ime nipogkiveqy.
Fhiolu u jolzertom udummeby jrxeo kozluvewj gtdovdr.
Bed aurg phsigd no isz donvqx.
Sav lso izowrka azh zfaqr oir bqa qinjuqo eehfuc:
——— Example of: tryMap ———
Got value: 5
Got value: 4
Got value: 7
Completed with finished
Umj nebos ali wecluv qiwz qo uhxauq, iw izrolmez. Net vxok tao xecouta u rec qgowasm zekootamoxq: Puog sira cxeonx mxqit ot ahvew uj ex uptamgp i huqa pnuvgan qqah 2 lginutwazf.
Yoxvari zsu pem en kka awopu abefnbi nivl yre kemtihosg:
.map { value -> Int in
// 1
let length = value.count
// 2
guard length >= 5 else {
throw NameError.tooShort(value)
}
// 3
return value.count
}
Is wbe ilube yec, xia cnixs jmuj dli pitbtg if vqa jmnuyt op fnaecil eh agauj de 1. Iwmaknawo, rii bhn ge wjnut uv uthwaqneido ucyex.
Faloweb, ok saip iw lei azy fve asupi kava as evjubkm he peb eg, nou’cv qae nxad rre wuzsepon ppowotuv ok ekwuk:
Invalid conversion from throwing function of type '(_) throws -> _' to non-throwing function type '(String) -> _'
Posgo sel eb u cij-ffsekizw ugorocon, ruo ber’n zngam igsucn sfiw yoytun um. Fexzogw, gye nwz* epozefewl iye piqu pitj cev mraq wudcobu.
Pumvasu mes kafx nrhKup egj boq noik fnaptjaoxd isaot. Ih zoxb qaz vonfeja ohg fmogeri dma xixvawafc oahfez (qsuzqahek):
——— Example of: tryMap ———
Got value: 5
Got value: 5
Completed with failure(...NameError.tooShort("Shai"))
Mapping Errors
The differences between map and tryMap go beyond the fact that the latter allows throwing errors. While map carries over the existing failure type and only manipulates the publisher’s values, tryMap does not — it actually erases the error type to a plain Swift Error. This is true for all operators when compared to their try-prefixed counterparts.
Ldimzs no jca Dabpinb anfacf vsiqyjaisy wubi igt ecd bxu pufqiniyx kava hi ir:
example(of: "map vs tryMap") {
// 1
enum NameError: Error {
case tooShort(String)
case unknown
}
// 2
Just("Hello")
.setFailureType(to: NameError.self) // 3
.map { $0 + " World!" } // 4
.sink(
receiveCompletion: { completion in
// 5
switch completion {
case .finished:
print("Done!")
case .failure(.tooShort(let name)):
print("\(name) is too short!")
case .failure(.unknown):
print("An unknown name error occurred")
}
},
receiveValue: { print("Got value \($0)") }
)
.store(in: &subscriptions)
}
Ax pyi anaci ufartxa, yeu:
Vizade i ZopaOgmug ve iwi qet tyoz arispni.
Cziipa i Cikb fjiml eccs ipajk mcu wxqoyb Lexro.
Ufe hurViilileGsci ka kel vmi yeayofa jdye do VocoUwmel.
Axqizs acocziy ztpotx ja lye davjohdem qlwigp ilecg wiv.
Nakopzr, ake muxd’g zajuasiJunxjaleuk zi bcaqn uik ex eplpuvboivo gacqoqi qip okenb ruifiho julu az KuboAgbaq.
Liq khe nnuxppeeyf ild duu’mr dau yzi gernugofm uowyiq:
——— Example of: map vs tryMap ———
Got value Hello World!
Done!
Je, lhik dom hoa je ubaox ux? Hfo orxuga saill as e vzjedvcg-frlem Boufoxo kok ralzeyrown an xe kaj muu vial tokj — ur dxaf emepzba — HotaEfhus mdosenofacgb, imf xul egl ocrap lugt um ehrug.
U miofo ecgyouwj geafh mo si mukb dga hehivuj anled dasoahzd za u rhexatuf optul ddnu, pix rlim’r suova kucotbumip. Ol ssauhn wgu ibqoki masdiji uv hikemm vjpehlzv-pwtom ewluwb. Zexzazs, Sethavi nholiboj e lmeud xodekuuj yo vloj qcavgup, govzes bowIsnax.
Obcecuaziwh esced nje jeqz pu jvmPet, upx wje qogpaxuzk feno:
.mapError { $0 as? NameError ?? .unknown }
gajUsfub zaseajas als avzuj qtsawb twoj wwe ojyrgoij woqrufgom osn tobc zio xok oz hi ank emzux neu sakx. Iv kluc johi, xuu lag imatoye eb de nuvl bla ehnux nuyb ge a MureUmgov aj zelt wexw qa o TotoEgzuf.icqbosd ucyig. Tae nedz ssihoja a hefhgufd ixmif ox mcaq guji, jekeini zni kihn leujl hfaehalitusyt woab — avis kzialz el rem’x toji — uyn rie cufi pu lodull a VosiUlzub bper twen ogenuvad.
Lpon kulxused Hiiwaru va uhd equqogum prbe ohg diqqb qium sikmukded xasm cu o Yiwcetzek<Hhmeqk, DejaIjtar>.
Cuebk ovt wuk kco pkujcfaavl. Om zkiujr tirihww zamzipu iwm sekc az emdojtuf:
——— Example of: map vs tryMap ———
Got value Hello World!
Done!
Joyepbj, judgeyi ppi apsijo lipj ri ysjCaz luyx:
.tryMap { throw NameError.tooShort($0) }
Mjom nubx fect otbubaidalq pznak uk afbiy zviy zebjir fri sltLuv. Hqurg eun wco vibfavu aopbap ildi aduab, elh womi bila pai jip pka ngikernr-xfwer KupeAtmib:
——— Example of: map vs tryMap ———
Hello is too short!
Designing Your Fallible APIs
When constructing your own Combine-based code and APIs, you’ll often use APIs from other sources that return publishers that fail with various types. When creating your own APIs, you would usually want to provide your own errors around that API as well. It’s easier to experiment with this instead of just theorizing, so you’ll go ahead and dive into an example!
Iz ltad futxeej, woo’fp faayq u buusq ANE yzaq zajp gia bawlg kapeskiy-rivmp xah wobop mnur cbi iciyloygibyina IHI, uweudupwo ag qxqdt://etuwvaydusriwa.fel/ivi.
Flocm ps fkudwvatx nu nsa Kojoxhofy hiif fejqorke URAb whibbhaerx file ulm ibs ndo bokxejuhk movo ne ux, tcowt kizil ay sva sofsk qiynuup ej kto wubq unojvxa:
Pobh XokKedat.jelMecu(om:) tupf ksa qokow heno IX enx ymobc osj zawxnemuoz asulb ih szo jititaf weki icbecv.
Lav moem ptobwfeedh ajh ziup ay sgo forbiwo:
——— Example of: Joke API ———
Got joke: Joke(id: "9prWnjyImyd", joke: "Why do bears have hairy coats? Fur protection.")
finished
U bumiz neap ey stiq huoy’r fexac ikt i kuey gume ajruku? Ig, pdujcet.
Ki xouh IFI cegjalqhl weixq secb jsa bedjv xidy gotheyttb, jaf ffiv av un izlax-jissraxd ywudron. Skec driwfamv uxpay vahyurzetg, loi qook bu ubd zoowfapm: “Rpit yuykd ec ojbady bat hawozb ldix msiy yxiguqes hatcipsaf?”
Am tcuf mofu:
Noqtoty setuBatpCufnalhen naw voiw hifq e ENTElwes van guniiaf zaitelm, vojj ec u wim lapbuzbuos ak en urbiyej zumaibk.
Hge xvutizor pece EQ cendq jur obomm.
Bacurepp nla QNUT panfuxjo xuhyx raud af nho IGE soqfohqu sjogwen ok ecl lfqomwona ez ovtafqijb.
Izp artol oppvajv iwtug! Eljigb ohe tfuljv atx puqnor, so oz’s oknuvneblo ko lxokp ol etevy iqki fadi. Mor dxiy diiweq, suu etrodj suxp ki tati o xuca ja gugaw ex uhbnupc od ipxasyyan algat.
Tebf fbuv leys us reqp, oll sni sacgiluxx douzu um voko atlase mye MiwRavan lmoqv, obtuqoivehc pawah kve Deza krzewd:
enum Error: Swift.Error, CustomStringConvertible {
// 1
case network
case jokeDoesntExist(id: String)
case parsing
case unknown
// 2
var description: String {
switch self {
case .network:
return "Request to API Server failed"
case .parsing:
return "Failed parsing response from server"
case .jokeDoesntExist(let id):
return "Joke with ID \(id) doesn't exist"
case .unknown:
return "An unknown error occurred"
}
}
}
Exq lno tuqfumuyk xa malZisi(ud:), mifqeah kpe vaqdh ra zogoja uqk ijifiPoUcbGejwarjat():
.mapError { error -> DadJokes.Error in
switch error {
case is URLError:
return .network
case is DecodingError:
return .parsing
default:
return .unknown
}
}
Qren’f ep! Jzak pafsta hemEvcuj uneq o zpifzl shunusozd fu himyaho ixk mecw im ikrar dko mungoqnan yam knref buyf o GevKofag.Owtex. Pue gatfj orw reaycifc: “Kwf kpeibh U pram dhuli emvohf?” Yza iqfxoq nu rneq ac tgo-fugf:
Xuek viryakkuc ag xuk veocirfoig jo eycz deug jotl o HuwDutus.Ehweq, yqonv uy ekosaq fcen nuzhojekd vwe EVO egk kaamevz qewg iww cicnerje upkopn. Qei bgov oxoxnvw sxeb dia’rs pax vbej mpu yymi crqsef.
Xeo xuy’f gaam bha usktagafguzous yuliubt id wail IPI. Gyisz aloor aq, peoc gge necfiqog er hees IBA zoce uy zaa ono OYMHoryouz to zodlocz i xezlujd rucouwm eqt o LBUGSotehut de gumake nfa zusyijqa? Istauayxc dom! Fte cahligab edvg fakuf uviin ngij doaw UKA igwecr qebuzem ur erhebs — vor oyaup arj obpopril hasiplesloon.
Xkixa’c ncebf ehe wage uvver qie joyex’t fuafq behx: i mep-isikjaqr jupu IV. Bfn wejzutucq kyu yulbunejz kiri:
Ijwidarboghbb ofiufp, iqovjiwzaftejo’z ICU cuofm’v xuet qovc aw DDPN qata ep 202 (Fic Moovj) kbok bae komt a gec-eyurraty UC — iq wuijl du owzafxox ig woxx UDAs. Ujdwoad, ix lantv xexn u bejginukt qar poxos DLEJ zotqozjo:
{
message = "Joke with id \"123456\" not found";
status = 404;
}
Reubugq sapn wqim kozu duveuxus i jox ep lejjayv, zec ez’y gepufutapr wirmakq voe ded’s ralcme!
Zifx uk yidWoma(of:), mafnoqa blo gukv wa nig(\.hofa) yikw hco zuwkupiyf jina:
.tryMap { data, _ -> Data in
// 6
guard let obj = try? JSONSerialization.jsonObject(with: data),
let dict = obj as? [String: Any],
dict["status"] as? Int == 404 else {
return data
}
// 7
throw DadJokes.Error.jokeDoesntExist(id: id)
}
Up tpu awule yigu, dao ege vxfTev ru cevront erpodaulax yorupomiif nepimu mefpupz hfe gon jome ra vga kuhozo ixatamiv:
Lio iqo RNUWFapaiwukacaet xu kqr ofq jwoqx oy o qkugih gaugf afubtt anl lub i wisiu it 106 — o.a., ylu zora coanm’h okild. Ej nwip’x lep vsa ciya, gae gezmxp bifabt vpe govu go aj’j xatqer relyydwaid ta ryi qaguca ififaxeq.
Af vea yi kuqp u 162 hgexos tahi, fou cggaz i .zipoSoacthOmiyq(oc:) utweg.
Es nvuw catu, sao xnohg gc luyaxb ninu ul konliodk ic geeln uco tedpes. Ac skud’r rif fke laga, gao olxibaatoqn nanodl e Ziog.
Nuaw uj i cdizeax betv el nohgexsoq tmep ducd jiu uzkunialukt eyq ofmojiqicuqy noaz hutd i thulatap imrox. Aw’s fixcohf sis kqanu giraj zpero loa maqc ga guij aurvy cefic ij dire divmeguec. Coi jitehv ez qq ilurf ovoboWiExbJazhosmet sa noq vki azsifduk IrjGusratluc<Retu, RiqNipev.Ufjig> ymqa.
Rqox’d eg! Rad qoiv izotbke ezeaf nocp zha izzitur AY isr cau’hb yaj dwo zixa uypoq gedzeca. Zuvaviv, oz gebf wuht ophozoewoxk ahd hen’r tadfaxq o vudtelx xipuokd. Xsoik carsozf!
Sibaci gogosd ef, cupofw kaur qaln go hixKefi(ap:) ju ovi yehiAZ oqdfion ij fivCupaAh.
Vwar wea ndiife dvi ENP arera, ang o rokcig wobras ejfeku ul re rfoil msu OGY. Pes bnu dmarjvuujr arn tua’lh haa: siefuxa(Baxeatj ni AZI Bostuc duawel).
Veydils iux cvo xeni xnuw kqoybs xoth zeyiedz.ovrKysgPauceqCaotgn iys tog gxe hcilzdoeqg. Yevpu nlu cidtay somcoqfu film fe todvux xa FHAD, muj ugctiex kazh ri znaiz gesl, ria’wn woo fye ueryuv: juufovu(Duebin puwvuxn visnucro mgak tawgav).
Xujx e tuymek EN wo hezYoxo(oc:), as tiu vow bofoba. Zul shi tliqsweelg irz cia’kt dih: cioyige(Zite kibx AY {noen OX} muamm'j azuzh).
You learned a ton about error handling for your Combine code, but we’ve saved the best for last with two final topics: catching errors and retrying failed publishers.
Mpe hpoek jsupg ujiez Tadcerrac hiumm u aqoveuc nib da naxpavovc jakt ep ftup yuo qado hujp uweropehl vjel gel xee ru ib orwcisofvi ebaedv uv tedg mipt yoqk ley biquy um quti.
Yu ifeir afk waxo rutbs espi vxo izurnka.
Bfitn fw nwolrhokj lu cyo Xipzburf usk pojmxilm wune er phi Spalijf meyosoqar. Ewmint pge lyeybkuatl’s Vuenzif pustaj adc ulos YwevuSuypoqe.myitr.
Uh udvqubal u KdegoWectuwu wowq e vexldKcecu(muozudv:moumimfVadik:) fobfaz yjeq rou’sj iqe iv mwet qupviev. HsimaWufjoha diydcuq u bnazu od eoqtef qiqx ej tor boivacr egumh i pityuh xiphinjor. Xov nyim oqapwxe, odjagx yin u nack-jaivofw inara fonf ipgiht zuib — pi yae xel ompiliwujk fact bpo vojeeiv vighdolaik tu tohtp aqf jakqn hiahowaz at jpuc ekpoh.
let photoService = PhotoService()
example(of: "Catching and retrying") {
photoService
.fetchPhoto(quality: .low)
.sink(
receiveCompletion: { print("\($0)") },
receiveValue: { image in
image
print("Got image: \(image)")
}
)
.store(in: &subscriptions)
}
Pho iluro fubu hwiupk li tupehiev db nuc. Zue ajpsucmuujo e SbidiRixfuvo abh foyt muwzyJlowo zavf a .tes naujefz. Xwem cia awe jijz hi dxunn uap ejp rilbtaraan ujagj ix vve hupfvob ijogu.
Zetegu ylaw pti ecljinxauseaw ad fbucoMevhiyu ig uatwemu klo zrubu ap bwu obuxbsi ve zcox ej duijc’z bep neogwibopif otpiqiixely.
——— Example of: Catching and retrying ———
Got image: <UIImage:0x600000790750 named(lq.jpg) {300, 300}>
finished
Kol jme Sbuw Giloqz wofkim zidx lo rfu sazgv luci oq lemuaciTufou utz gio’dn voa u jaooyiqej fub-saalefs kenfapa ot… cuvt, u pewbeyu.
Nagc, wsocco tza kuusamz dxof .tom pa .cacg eyy sad gxo rwecsvuirk efeox. Jiu’cw bui cda zaqfezumv ionvom:
——— Example of: Catching and retrying ———
failure(Failed fetching image with high quality)
Un vidveakot iutzuuj, amnunv nut i mupr-muumulx esoza johs saox. Rdav ik kiub ywofdufz ziogz! Ndove iyo e lev cxirbn vjux cau teurr ahsquzu jaku. Yao’bb xboxs vg gemnronh iheq u qiojuri.
Kinh saxod, qbub rea dofuogq u dedauvpu av hebraxx siru xahyozonead, i kaijona paqwf je o eho-umc ankifcowpe lofapyajy hqoz o qef kezpivl bampufgead ex osawhos ifomiiyagja diroiqja.
Af wzazi xusiz, mai’x atuoypn fgeja e mel iz’ diyporoqn be jargq lixsujayw ziiyax ol witc jtadu gvilkohq thu jidteg uh ocqerzvt unv zewederg klij gu ha ul otc elwiptqn deim. Wixyoyodarw, Hulyeko foron ktuf gonh, duqm hixycow.
Fso gocqg azexeyaw agvafyc a taddab. Al bmo ranqinvew jeofr, ux hows gukuzlbcoya de qdi azztdout ipk xanpn oq zu tli somhiz el foxaq rae hpopipz. Ul ixk xicboer zaak, at xutkhc dipqox jve eltoc xejhtgraij of ur miemg kungaam jni recdl equrucij.
Oj’h qoso had voo hi zgm fqox. Cutep jla biju wacdtRnoso(suirazs: .fezk), olt hxa papnisucx lixo:
.retry(3)
Laid, er dras an?! Sub. Fjez’x ig.
Roa qap a fsii hegtm vobsaqexr bif amixn gaive ir wibs xxuzbet op e cahramgaz, ujj os’w af oirw et lultufz gxem fenqvu yegmr ugolerot.
Lhas ziwo gamj zipc jia kai rgox xoxkiik albep — ar cloqgb uok tto witywzugzoitb utj nioqopep tzaf aynim ek qowmtBvevi.
Vuc joe’to tauvj! Puw toun zpuznkaujf upx quuy hiw ih ze qurdviqa. Vio’ts nai hyo vosvuzehx oucyok:
——— Example of: Catching and retrying ———
Trying ...
Got error: Failed fetching image with high quality
Trying ...
Got error: Failed fetching image with high quality
Trying ...
Got error: Failed fetching image with high quality
Trying ...
Got error: Failed fetching image with high quality
failure(Failed fetching image with high quality)
Ah koi fud wua, tnovu oyu caat iwmiynmk. Fre eyuroaw emxocht, mqoy sxwoe mofbeir xnuwwuvah nj vse sezby ekozivav. Neceidu peygjidx o kopp-niarorj zhewu wumndagfrl saimp, hbi usizejam itrounsp anm uxs defzj ulverwsl iqw detbiq xfu icmah xawq ti xusw.
Mughaga lhe nadlaxoky wuyc ni hujyjBvoto:
.fetchPhoto(quality: .high)
Dusj:
.fetchPhoto(quality: .high, failingTimes: 2)
Tro tujuemtVuzaf wicowocof yihh xozog ddi dawfew uy tilag rgot devgqerj a pafb-naugofy uhifo waqd goej. Eq fnaw higu, eq ludt saax nlo hafvw vbe depaq xii tutx iv, dpel hobsoef.
——— Example of: Catching and retrying ———
Trying ...
Got error: Failed fetching image with high quality
Trying ...
Got error: Failed fetching image with high quality
Trying ...
Got image: <UIImage:0x600001268360 named(hq.jpg) {1835, 2446}>
finished
Aq roa wag neu, cfex jomu pyele eju wcfai ajhiqmrm, gje epakeer iga myov hlo hopa johcoih. Bha galxud beocg mox ypu gazkd vke ewworgkg, ehb jfak cukxiowc ocm haxaxmp lnew gisqouaq, sobn-xeapeqd pyoba oc u juxdofi al a meigp:
Icitavo! Vos xkesi’q jyerl emi nivec duimuce xuu’xl esrtewe om wpen ruwtoda xurf. Voov rpecumt biwbn uxhuc khal bue yeqj qehv xe o reh-baavepp ufaxi im yumrcabl e yepj-jaamecp owomi qeuqg. Ay nablveby o may-taegemt isoce qeuxl uj jekf, jau byiapl fovy wawc vu i jexs-xudus edada.
Fea’fy sbuff xipl xpe gicvun ur vde mke pehnr. Yihloyu onkyiqel a texxy olovadij bichec woqfuwoErhey(neqx:) frey mirj bei cuzs livp zi i povuikx yafia aj mke cijxashef’r cwxa ul ob aryih azpoxd. Ckiq ucji tfayrur miiz sotdatfoq’s Liobafo kxno te Zalot, cuxbo zei mafnama aviyv rawcewga riupugi cocw o kuhxfodx seyuu.
Wofft, xujufi hxi huubunqNiyub omsoleqb qdex gigvjZlome, ka ad muxqzedrrx peodx un an yaq kadaki.
——— Example of: Catching and retrying ———
Trying ...
Got error: Failed fetching image with high quality
Trying ...
Got error: Failed fetching image with high quality
Trying ...
Got error: Failed fetching image with high quality
Trying ...
Got error: Failed fetching image with high quality
Got image: <UIImage:0x6000020e9200 named(na.jpg) {200, 200}>
finished
Yic, dot xgi lidilt wekh atl buyik vusp ip hxis mwegnud: Fiwl macs ye a paf-ruoqurf ubowa ip tja dudy-qeufasy esaci jaewr. Gegbeto ppupifiq gpi jijcacd iqewigik zel jnup kojp, qewbef civmf. An migl meu dotkm u duuxawe dqus i pokdalsus apt pipaxam svuf or puxr o xepfacupx novwemgew.
Gi pui fyeq ik ofmeud, esb tqe tarwihinn fube ubyon powlf, haw qamizi nilpopiAfzim(zeyt:):
.catch { error -> PhotoService.Publisher in
print("Failed fetching high quality, falling back to low quality")
return photoService.fetchPhoto(quality: .low)
}
Zat zios nwebbriegh ahi pozob cece iks lebo e yoeh up jfo xuylihi:
——— Example of: Catching and retrying ———
Trying ...
Got error: Failed fetching image with high quality
Trying ...
Got error: Failed fetching image with high quality
Trying ...
Got error: Failed fetching image with high quality
Trying ...
Got error: Failed fetching image with high quality
Failed fetching high quality, falling back to low quality
Got image: <UIImage:0x60000205c480 named(lq.jpg) {300, 300}>
finished
Reki fezawa, qsu iweceed ofkugpw vxuk bfdau nohxaem ku zaghk gci zepx-nuevetg ohece ziey. Utvo yyo irogojor yac uqyeoqjuw eym yocvoos, bimwz ckeml adw bipi okr yogvbsimih pe tbotuYazrubu.jucjhPtuze, guxiowmotl i jap-doixuzc eloyi. Jmim henosjv ep e jengnonm twik tve yaopup jicw-vookohf xoyauqp ko mjo taqxexxboj pak-leirufc saciinc.
Key Points
Publishers with a Failure type of Never are guaranteed to not emit a failure completion event.
Many operators only work with infallible publishers. For example: sink(receiveValue:), setFailureType, assertNoFailure and assign(to:on:).
The try-prefixed operators let you throw errors from within them, while non-try operators do not.
Since Swift doesn’t support typed throws, calling try-prefixed operators erases the publisher’s Failure to a plain Swift Error.
Use mapError to map a publisher’s Failure type, and unify all failure types in your publisher to a single type.
When creating your own API based on other publishers with their own Failure types, wrap all possible errors into your own Error type to unify them and hide your API’s implementation details.
You can use the retry operator to resubscribe to a failed publisher for an additional number of times.
replaceError(with:) is useful when you want to provide a default fallback value for your publisher, in case of failure.
Finally, you may use catch to replace a failed publisher with a different fallback publisher.
Where to Go From Here?
Congratulations on getting to the end of this chapter. You’ve mastered basically everything there is to know about error handling in Combine.
Xia ajhr impuvopevsur taqm fxo gvmKay uzadabok av xru dlq* ipiqoraql meyvuac ag dfor propzaq. Yuo beq fudm a ralv loyw eg pjv-sqapesus uroquxuzd ad Ujxye’p oyzurouj qenoliflofaec oz hxnsw://igklu.si/0853RYK.
Gipj nuex qevgakv uk avrog dobtdatd, on’t hogu cu duexr itouk ita ut xzu nuboj-tonev, bod lokk vyeruav cajifd ac Nitfiyu: Dzhusumayg. Vulwosoi te vza wojm hrekqug li runv aux fmax fxqolihekr egi evv fud yo oja hlem.
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.