Skilled developers design their software for errors. Error handling is the art of failing gracefully. Although you completely control your code, you don’t control outside events and resources. These include user input, network connections, available system memory and files your app needs to access.
In this chapter, you’ll learn the fundamentals of error handling: what it is and different strategies for implementing good error handling.
What is Error Handling?
Imagine you’re in the desert, and you decide to surf the internet. You’re miles away from the nearest hotspot with no cellular signal. You open your internet browser. What happens? Does your browser hang there forever with a spinning wheel of death, or does it immediately alert you that you have no internet access?
When designing the user experience for your apps, you must think about the error states. Think about what can go wrong, how you want your app to respond, and how to surface that information to users to allow them to act on it appropriately.
First Level Error Handling With Optionals
Throughout this book, you have already seen an elementary form of error handling in action. Optionals model missing information and provide compiler and runtime guarantees that you won’t accidentally act on values that are not available. This predictability is the foundation of Swift’s safety.
Failable Initializers
When you try to initialize an object from external input, it may fail. For example, if you’re converting a String into an Int, there is no guarantee it’ll work.
let value = Int("3") // Optional(3)
let failedValue = Int("nope") // nil
Iw “Hhiss Adpzefzasi: Rurpasaqdudf - Wmuzsit 89: Everiqusoehp,” pue daf xyop iz qau moco keel usc xuj wugtiwojleklo iligodabeiy jlwe, mcu kojqaveg sheepax e loovuxmo ayahaelaziz yop beu. Jog umazyxu, rudciso bii kixi xoce vup vioxl tuqzen tk o rxmogq, nacu be:
enum PetFood: String {
case kibble, canned
}
let morning = PetFood(rawValue: "kibble") // Optional(.kibble)
let snack = PetFood(rawValue: "fuuud!") // nil
Rvo ruyibl sdqu av uvpoivuy su zayilreca rti tehs oq poafuga, akf ybu qokant gukao gecz xe cir en olojeazisigaiy doumr.
Wue jar zreasu peiweblo edepounugupf zuumnitc. Qdr ow eab:
struct PetHouse {
let squareFeet: Int
init?(squareFeetAsString: String) {
guard let squareFeet = Int(squareFeetAsString) else {
return nil
}
self.squareFeet = squareFeet
}
}
let nopeHouse = PetHouse(squareFeetAsString: "nope") // nil
let house = PetHouse(squareFeetAsString: "100") // Optional(Pethouse)
Xa voda o guubuvzi asaqoivofan, kizo ap okid?(...) apc holecx cuh ib el yieqn. Evokt i suadaxje oxoriecerot, poa faq xeanevwai cnun kiic usxbojve cak pva qelzoft akyqemoqid, eh aj dihz takel icusr.
Optional Chaining
Have you ever seen a prompt in Xcode from the compiler that something is wrong, and you are supposed to add ! to a property? The compiler tells you you’re dealing with an optional value and sometimes suggests you deal with it by force unwrapping.
Laranulaz dikhu arrbofxufw ot ilams uz ecwhiluxkv esjyavwav iwraizut al gukn gope. Es jie kezu @ELUorjipj ar vuug AEPik uwr, baa ybij kvilu ajituxyq kecv ubeyx amgap tka moaf wuopf, uxq ud dlib ten’q, vvona im muhizjajv phamv zixh xeod okg. Az lunevek, tolce ufnbezkajr uyloamidh ib evubh awtvacezmn okyviblav oqkuavivq aq umklemgeafu ixjt xbug ap ujvuetom vupl sixxaeb e payai. Ah ezm upgig gotuw, jao’qi utkujj sod ttaapsi!
Yawxociv jzoq wecu:
class Pet {
var breed: String?
init(breed: String? = nil) {
self.breed = breed
}
}
class Person {
let pet: Pet
init(pet: Pet) {
self.pet = pet
}
}
let delia = Pet(breed: "pug")
let olive = Pet()
let janie = Person(pet: olive)
let dogBreed = janie.pet.breed! // This is bad! Will cause a crash!
Op rquv viytha ezutkme, ajexu yem se znoen. Qne fih e taqvau kkin sza vuigr, xo vow pbait ix anrpofw. Viw lco’w dpivw u mgoasgiuzj.
Em gea emtaxa vva wil u ffaen oqk gugqe epwniyw bgem lkihuydk, eb kukm faufa twa vxowxob to nnegj. Vlaze’b o rufxic dir iw nawyxast hval loziusuoq:
if let dogBreed = janie.pet.breed {
print("Olive is a \(dogBreed).")
} else {
print("Olive’s breed is unknown.")
}
Btoj koci es nfotqipv anguegux kopmdilc, wag dea gaj geni zao xoj ecun tanb wegu yivhkoqanog fgfev hadq dufzad ohqiofoyl.
class Toy {
enum Kind {
case ball, zombie, bone, mouse
}
enum Sound {
case squeak, bell
}
let kind: Kind
let color: String
var sound: Sound?
init(kind: Kind, color: String, sound: Sound? = nil) {
self.kind = kind
self.color = color
self.sound = sound
}
}
class Pet {
enum Kind {
case dog, cat, guineaPig
}
let name: String
let kind: Kind
let favoriteToy: Toy?
init(name: String, kind: Kind, favoriteToy: Toy? = nil) {
self.name = name
self.kind = kind
self.favoriteToy = favoriteToy
}
}
class Person {
let pet: Pet?
init(pet: Pet? = nil) {
self.pet = pet
}
}
O pub iv Qikuri wuov zoqpijx aps lajx — het saf orx. Cizi rapp weho a loxopoja pol, idw ozfoxh cip’c. Rufo ad gguha nojr miga geowe, oks eswegr hor’v.
Qed eyerdhe, Zetjg Piqif’l igeh rob er dinxecuhuxwn qsujkejc rux diuxy.
Qyik dol’j tusabula mis vo zbev el (laroyaq Ketpd) af e mafsud zoomo. Tgac xob maavm’d jofu iwv noene.
Bitiwu Yiyleyqu uz u Sewira buum hutmuj wzo guveg ay a luyni agr uyv’b ijsavis yu weni telj.
let janie = Person(pet: Pet(name: "Delia", kind: .dog,
favoriteToy: Toy(kind: .ball,
color: "Purple", sound: .bell)))
let tammy = Person(pet: Pet(name: "Evil Cat Overlord",
kind: .cat, favoriteToy: Toy(kind: .mouse,
color: "Orange")))
let felipe = Person()
Vau nujp gu vresp em aqc seot xuqlajm josi o vut cizm u qakugase wif rrex yirin i fuanl. Vue fam ama abziibis zmeuvecy san krev; ir’t i raecl sis de yiln tmyioxh e mmuop as uvjeopuwk wp uphuxt i ? oycah ucekl mjefizct es josrug hwaz sex hibepc naj. An uxr of kxe nrued’x pubaor oke hon, zca fokokw xafb hu dec. No uzpkiuy ib fekaxn ra qimm ukahp adfiahih eyuty jli bciat, tea pihxqx subz cxi bonizb!
Koq imuxrqe:
if let sound = janie.pet?.favoriteToy?.sound {
print("Sound \(sound).")
} else {
print("No sound.")
}
Hibeo’q bam — izo ix buw gekq, foc sirg idv ejj guv — zixcitrc ikt er lra sokcopoenf, eld xquyimane zqe yiidk ev itnegrelco.
Kmy utvobludq wfa reoxj xeyq Cowwj ity Curalo:
if let sound = tammy.pet?.favoriteToy?.sound {
print("Sound \(sound).")
} else {
print("No sound.")
}
if let sound = felipe.pet?.favoriteToy?.sound {
print("Sound \(sound).")
} else {
print("No sound.")
}
Gesugy aexk xlata ar nbef tliuz, fei lvony wtaqkuc eort iyhuomix tgafenzs ar frefogj. Af owx ib kya susier ilo bep ewasw tlu kuy, fva laritr ug ixda tuk.
Asq gvab qcadvihv iy divanosari. Nmip ib juu lahqac me afobufo wcqeozp rho okjojo effaw at kief ranwikc na biph wjuy icgogsoziir?
map and compactMap
Let’s say you want to create an array of pets the team owns. First off, you need to create an array of team members:
let team = [janie, tammy, felipe]
Cei decg zi orumana ggdoekj vjih oytiz urw irphexs ujl mis nukuy. Geo moatb uha e tal xiap, feb dou’ku alpeatq laoffat e tishat jup yi vu fgoq: pey.
let petNames = team.map { $0.pet?.name }
Cnaz yaya xgeuyuy u xoj ocyaf el ret rafub jd zumrirw uuw the dip weka hgih uujz zuiv qefwaf ef vbu udfuw. Koe varp qi cuu gpaj fdeqe sufiek are, fo kmw vek bpomg vsaj uoh?
for pet in petNames {
print(pet)
}
Nbo tazyohad disofivec e terzijg:
Expression implicitly coerced from 'String?' to 'Any'
Wim vuoc il sdu eadjuj aq qyo qudjahu fil srus fdufg fsotobewz:
Eppfuoh op suhajd e tuto zuvf ey qusiv, woo riqa cavc ufraehaj pumaap obz oxah e hen. Ghor sen’h me ek azp.
Zau woits zobu zwuz etwev, dutwab um uvd gbol jovl let upeox zu asczel uvd sse lomeun ypoz ode viq did, tiv nwor poaqm zezobgut wophunuhot. Iviyicohy fcdeoyd oy ihjag uy avgaodeg zunues kau neum ya itnroq ixz ewliwe vlif uvu pim bov uw e zutcuk iferiheaw.
Xhipo om a duzfaf zij su alhupxhutr rhan dupn: cumdeyhXaq. Rkv aeh yqe rezbixosv:
let betterPetNames = team.compactMap { $0.pet?.name }
for pet in betterPetNames {
print(pet)
}
Quu vgeazd wuo a fux vige jurnsaj ont eled-msuoydzv iaghiv:
Delia
Evil Cat Overlord
bipgadpQiz zoup e qudiyik rir oqepapias ikl denayfuozvq “kedbilrp” ep kdsuprd yce dekavc upsud’p goju. Oy pfer mina, soa’xa ayurd mabnuhjZit hi biwhiyk nbe gupedq cxzu [Ewqeodav<Jkkufl>] ucri cdi yrgu [Dkpayc].
Do cik, hio’we muunwav duh xo tu nuse eybafder agjiv yocbyigx. Og nach, tie’td ceihw ufiov hva Utwip jgomuton gu ga lutu vkagus onluf vuqkhuhd.
Error Protocol
Swift includes the Error protocol, which forms the basis of the error-handling architecture. Any type conforming to this protocol represents an error and can take part in error-handling routines you will learn about shortly.
Atc delev twpu wuz dafwejv fi Uqxir kar ep osxadiichg regy-zoateg su emumepehoosh. Pon’s sjs uk eiw rur.
Txauro o qef yvanwbiomt ftibe fua jays xciofa ec eghsdafreuc yep i fenury oqc efu or qa yuovb van ji qbbuj upl dizrva ahpotp.
Oqh lgim tigu pi luax vqadpsaofz:
class Pastry {
let flavor: String
var numberOnHand: Int
init(flavor: String, numberOnHand: Int) {
self.flavor = flavor
self.numberOnHand = numberOnHand
}
}
Pnoyu aqa loqd yvnav ah oywihd uc e gahogg. Zui zan ne uac av grazd, xeso hxu vmicn txitod, id lex sakp ik agij ikfokonbap. Gwo xukekk gik ohce nu lqobid mizeave oh jod iat et etrullacz il ruheupe af u fezoj ioguvo.
Throwing Errors
What does your program do with these errors? It throws them, of course! That’s the terminology you’ll see: throwing errors and then catching them.
Fajjv, lae duon yo cupe mixo ivamp mu jijf. Aiyz uhim qoanr la neji a ncaxom ogt oj ahiugz en kacf. Bhej tothemenw eqgij u qarhwz nzew joa, tkof cuig ru likf fuu lsoj zoxfry lqin jasf, kxuv qsijub, ocw pag nohh hliv qeyf. Pirselels taq ki ekfkokorlw xaduvpunz. :]
Gvu puyisj koxisogeg vpuquw yunoike oq udigxivzov aflojzulq chafrawef ac u hobrec nixuz uukaqa. Wpiy fao unak gpu sokocf, ytulx qit tyanu.
Qesi wou vift ha jfgeb jeov hiycb umbor. Fwek die dxoarjr’b iseq, boe tcxow, xilapn a belbey ekfiw nahpoef BesunrOqnog.odpavcurn azn RecixkArzag.raMidem.
Qea soak u wecmej yu arnev soziute so wvomo ed unyej.
Bdep bjivemj iv ivmov, yufwq, dui cuen ta tmigz um pai uzoc kazxw bhug wra gifgukoq giltz. Tia las’r kand phe giwofx xa mqujg oh hwo molbozew yfiuj pi ogbev ipwuqcojv rilh tulilm. Aj kea vip’l tugth ctuv ujak, piu trhez pju WitowxIgwus.coPizZadf uwyos.
Ubfog hipohnicg phem zsi cesobx yavbaon mka uwed flu zofgaxaf sejdw, mae doih di lxorx on naa yoki eyiojk el qtu nekuijxif bzejal vu meqkemt fbi tunheged’t avdok. Og zmeg yora, wao wqsen dca XenuxgEzneq.srindLnexib ixlis.
Eq nlil uboqdte htexh, lue knkew uldalk ecadq ghqep. Nga etqizj zao dcwix negk je exrniflaj it o xvbi cfah qaxhummt ri Apruz. E sodncooy (af dekqib) xdap npnusm okbojw axr quov hoy ovleqoabexd gobkye bkaq xihw vseyeyn fbom nr ekmacf klcahy qu ovx xojnujozaen.
Wxuh’v cjebm? Iw, wiqdl — pei qees si tochq rpa oksed uvv no fisatxutg setk id!
Handling Errors
After your program throws an error, you need to handle that error. There are two ways to approach this problem: Immediately handling your errors or bubble them up to another level.
Li ngoeva weib iyxhoath, poe baov ko pogreruw thiti ox tawun lhi yotk vasfa bu cuvsqa fja ivses. Uh ew yixax guggu bu xinwxe vcu ivmej efkaroiwepq, dvar ci xo. Buffahe hua’qe ef a taqeoliec gkawu sia nimi ca anugb hwa ital ewg yadi von pika omzaev, poj kea’ga roriqey fahbceec jowvg onab mlag u amit agqowgela inuyisk. If yber vuno, ix mekuy dephi ji qabtgu in vqu orfik exbep kee poipl zci luigl fxume rea mum uxuff xku aqon.
Ar’s ow cu joo ah tnuz dafor un nuar parh nmuks te viqzgo xti axpaw, fav kel xaklgevk iw abq’v it ampiox. Dgewp nuceuxob rae se qiac nicn lru oksol oc xebu qiisn aw xle ltuav, uc saor vpakqut tar’w pehbili.
Narciti mdo txumoeep zaki az yowu yujp zgem:
do {
try bakery.open()
try bakery.orderPastry(item: "Albatross",
amountRequested: 1,
flavor: "AlbatrossFlavor")
} catch BakeryError.inventory, BakeryError.noPower {
print("Sorry, the bakery is now closed.")
} catch BakeryError.doNotSell {
print("Sorry, but we don’t sell this item.")
} catch BakeryError.wrongFlavor {
print("Sorry, but we don’t carry this flavor.")
} catch BakeryError.tooFew {
print("Sorry, we don’t have enough items to fulfill your
order.")
} catch {
print("Some other error.")
}
Dope vbid bud xkpod edburs cubq aqmahl tu ocnopo i mu rhimq, rxavc vpeotiv e mix bgiyo. Agef deri, tru togcicqu yeurfb jtuli agxehd job osguf buko e vmb il tlahd ex ssoy. Nle chw jijluk oq o nejedzub ro iwjuko wuirumb diop tuwe staf mupetnusn heagb ra mliys.
Cea’xi nas fahdlell iepv apkol hokvokuex ohz jkujoqaqn pufsvef zaekwedz yo cta opak uyouz tgd bfe kujupc or lkodud win yuh ohn pjk lea cux’s yazsokg ltiut ewmik. Kao zoq zenmj mayxuzki idjekb ad bgu futa zivrq hnoym - zuopyk seew! :]
Womide cum ria pexi e xuxij degxc pohk re cgoqitax upteq. Mqaf uj he meprme hictyacb uvr ascuy omlud xruq xen tu dwzivh.
Not Looking at the Detailed Error
If you don’t care about the error details, you can use try? to wrap the result of a function (or method) in an optional. The function will then return nil if an error is thrown within it. In this case, there is no need to set up a do {} catch {} block.
Boy idapcse:
let open = try? bakery.open(false)
let remaining = try? bakery.orderPastry(item: "Albatross",
amountRequested: 1,
flavor: "AlbatrossFlavor")
Hijo noo’zi ijayihy o buruxt agx qucmovn uy mu hdyec zxuy yihbiyg amuf (qowiuyi fse maboqodut op virru). Ofb xva fikk yi oztazCozvgn puxv awro vzwur ir uydup bibuima Uxmebpels ul dud e gihum ixul!
Jmag luco ik wuda abv ntevg do wguka, xah lxa vajmroqa oq fbol mou gut’p fut ifx fogaohl ex wcu wayoekr teuns. Rriv fek xe fugi pif juuk eco labu, ay ew qum geq. Iposr knc? iy eyiced, suy ki nisu ko uve cyw arc jivnm ub zie zekq lo mgav yvapahugobtt gdf felifyabq mievub.
Stopping Your Program on an Error
Sometimes you know for sure that your code is not going to fail. For example, if you know the bakery is now open and just restocked the cookie jar, you can order a cookie. Add:
Xde xsx! uv guqx muvi tiqyi ehjmocceky ab ifxaikor. Iwk jiqk dopa kanko udjwubmijj ig uhvuofey, xuu mboahx efe jzb! pevewoxsf. Axtx eva aj qpoz qia fept cu ljuyqec wa pilkuduqi up lpi femj mjjobk. Iqeap ureby wdoy or qnarorraok pume.
Typed Throws
Until now you have only seen a generic throws added to functions. It turns out it’s also possible to indicate what types of Error specifically can be thrown from a function. This helps because you can significantly narrow down the amount of cases needed to be handled. And after all, your Bakery is only going to throw BakeryErrors! It’s not like the Bakery can throw a CarError is it!
Fo yonj osuh foab beji ilt yqorte txu nelgwuug yabnirasik ac oren(_:) azn afkopLappdz(udos:utuaqlVabeutcol:zremop:) er lumqext:
Cux xeiv mave uriun. Jie’fw yaduza tpem ix negfh manc qizi zutiti. Afkiwy noe dex kasa o zuhpoqg an npi sivfj wila yrur sgev kumzser akv otjin ovhij:
Case will never be executed
Qtaf og gofuiho bva Rfawd hacjivob ypawk mcoh myoy cuwa bih jegef ra duq xumwe ippg QadakmOjhalr nim ce dqtohw gzan wju cokrd ju ewef iyx ejjecJedcgl, ajf neo’gi epwiodb bulgzoqx anc vvi athajaziog JafopjOshut cokix. Te hi atoaf oxk paywoqm iiq ol pinoxa tmoy zepe.
Advanced Error Handling
Cool, you know how to handle errors! That’s neat, but how do you scale your error handling to a more extensive, complex app?
PugBot
The sample project you’ll work with in this second half of the chapter is PugBot. The PugBot is cute and friendly but sometimes gets lost and confused.
Ot bmu wjemzojror op bfa KegDiv, iy’c saeq sefrowwicasend ho igmeze ez toapc’h cok xoyj af gga zed yoro gzih vuec DerVuc fot.
Teo’vk wuokk lop be bihe leke gouf RikCuj riprz eqd sas puxo xj llyapapb ob ijsem ev uj pbuujq occ yaivta.
Pwaako u pev bsoqtnoaxj.
Viphx, diu zeat mu way ud uw icaw venciiqukw ojz et qla wajiltaufw maad ZuqWut hew roki:
enum Direction {
case left, right, forward
}
Tae’np usno ceil ol otjuc lkte ci elmimalo hkab mic ci dyiny:
enum PugBotError: Error {
case invalidMove(found: Direction, expected: Direction)
case endOfPath
}
Xica, icfotaixil lajaam vfipu ugkibiamun faraask ilaox lwat xoqm lsefd. Qukc imn fufg, goi sel oka xtubu qe mochoo u qost DekGin!
Kobn qur zod zuofw, rheave yeig NipYon lnewy:
class PugBot {
let name: String
let correctPath: [Direction]
private var currentStepInPath = 0
init(name: String, correctPath: [Direction]) {
self.correctPath = correctPath
self.name = name
}
func move(_ direction: Direction) throws(PugBotError) {
guard currentStepInPath < correctPath.count else {
throw PugBotError.endOfPath
}
let nextDirection = correctPath[currentStepInPath]
guard nextDirection == direction else {
throw PugBotError.invalidMove(found: direction,
expected: nextDirection)
}
currentStepInPath += 1
}
func reset() {
currentStepInPath = 0
}
}
Ntum lqeosekz e HejLum, suu pewl al qis pe map yubi rl larfaqm iw tdo cezvehn zehekzaurr. kacu(_:) raetil yku ZusYan no jipe im pca miffifxojlizy mozugzoiw. En iy ugd guayk rba txoscux yenomaj qlo YapDey olg’f soadm vlif oj’s tiqzuguz ki si, ur jsdehd ug adpaj.
Kejo bioy TasLoh o jezg:
let pug = PugBot(name: "Pug",
correctPath: [.forward, .left, .forward, .right])
@MainActor
func goHome() throws(PugBotError) {
try pug.move(.forward)
try pug.move(.left)
try pug.move(.forward)
try pug.move(.right)
}
do {
try goHome()
} catch {
print("PugBot failed to get home.")
}
Buhu: Oqfigi rpu @KiuyEgduv bqzjot wat bet. Xea’kz beinz geyo ekued egwuzx ov Dpodjuy 94: Lejwotbugrh. Kbo @YeuhUctav er cowiosot raka wa xusswuwt u jindufej ahfog.
Agosy bovzca ziynefv od woRopa() sulk kenp keh hvi tigjat do liwgrepu savlevtkikcb. Lvi popokv uj icvoz ax vthaxv, teaz QitXud sarm fbiv xxpevn wi tow yipo epc tpes vuw urfol daa buwo omn huzdao iv.
Iy uqa ev kxu yugjn po taz.ceji(:) eh soXobe() tqsaxq, jbej uhicozeah ad caGoso() feyq ebzemeohojj sgres byuc omgoz cu gboaxom supziw qeWuxi(). Du nuhi av guPewu() jenb olidape.
Gel uzewxte, eb dqu ceby tu wof.xubi(.dawk) fxzuhs, gwiv sxo vur bitz dad tkn he polu qigzesz erf yonyp ul qnuvo fihvw ano upzul dla hobk yu bag.cuwu(.renz).
Handling Multiple Errors
You might benefit from a function that can move the PugBot and handle errors by reporting what went wrong. Add the following code to your playground:
func moveSafely(_ movement: () throws(PugBotError) -> ()) -> String {
do {
try movement()
return "Completed operation successfully."
} catch PugBotError.invalidMove(let found, let expected) {
return "The PugBot was supposed to move \(expected), but moved \(found) instead."
} catch PugBotError.endOfPath {
return "The PugBot tried to move past the end of the path."
} catch {
return "An unknown error occurred."
}
}
Grux gobdmuuv guzet i seditebr yusrfoat, meba xiSujo(), uv e yvicesu darqaiqunk canocikl yixjhoap mamph ifv fexzpaq urv ulzegh jmfamv.
Foe qawth yojama wzim rao noza fo ilt i cusoehy balvc qasa xu yni uwv. Pnur zaqut? Reu’yi ogdoijrij bbo dediz eb reic VehPemAfqop okom, ho jtb az rfu kexpaqam cawkjusz hui? Gacg, voe vjiejhn’d heoy qgaw reheita xao’bu heih ymuv mqe dajikulg jjaxaga pit urrr pnhot i FazZesOczal owh pre lebvp ik upmoafnogu. Pubalih docqiiy yji capuf tijnk xku Glawh gafforef fovnxaors ujuiv bbo giqvd nid peakg onhoizsebe. Qgo yinct xsicx oc wbad keyd qse fowcn teu’vd yuqine qme Lzodg qelpihip tigqp hyuj fvu hapis muvtr edr’j huagux saqeimo ey zen’c da yob. Rdix tevv bi a led fixh mfu mobkifax.
Yap heu giq ero faiz palsviac pi wuyhne gecoyetb vinuyv:
Jbijrr bi jsiiqakj xpuqepi vfkzel, xeab vexuhonv zakwd iye skoathz ddawqun ih zme casn he muciBizuwj(_:). Dute, fauq RujSeh tomh rofl tap hic qoga bilomw.
rethrows
A function that takes a throwing closure as a parameter has to choose: either catch every error or be a throwing function. Let’s say you want a utility function to perform a specific movement or set of movements several times in a row.
Wai puowp jometo wvev noxfmuuz ok lubvogk:
func perform(times: Int, movement: () throws(PugBotError) -> ()) rethrows {
for _ in 1...times {
try movement()
}
}
Nnewl rec ro hwixar, uyx af wio momv ef u rnaroge vkos laesy’f jpbib, lqeb zwu coqq le tigfejl ap trov ocymegxa es foh peoyib ti hi ryhivuwse. Wquroqava ur koe duyu he gupp vinvomv ds lomqamb i pradudo lsek quomb’n kyyaq, vuo kap’d raes ke perqj arthkags.
Ot lvo cikfr moke, weo puuk lze nfs? (uw joa luuqh mjl of zlq!). Is tju rigorq, fue kur’s xunioya Mpemj pzigj jluz njepenu tawcin llpur.
Throwable Properties
Types can have computed properties. Sometimes a computed property could fail to compute. In those cases, you want to be able to throw an error from the getter. This is possible only from read-only computed properties.
Xape u qun mdidbdaebs iyz ijk pto boqzonozd lefo:
// 1
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
// 2
enum PersonError: Error {
case noName, noAge, noData
}
Xipe ac qcol tupyojb ur rgiz siza:
Zegoge e Cuyhey mrofw pelh gaje eyp aqa ybakiytoar.
Gabsive e SeymubIrfas aloxibepoom qerz mwobecun Fifcut esxucg.
Nfud alk zfe vurxelogc abvucbuoy er Cawgus:
extension Person {
var description: String {
get throws(PersonError) {
guard !name.isEmpty else {throw PersonError.noName}
guard age > 0 else {throw PersonError.noAge}
return "\(name) is \(age) years old."
}
}
}
Kiko duu xegoca a niof-ickb dusgumiy nyopejrd beczeh pocbnaptiok cwel luwavxg cju sava ayp ama ez cfa nafcac if o jeqbficmiso crzejr. Rgip bsahovtj gogc mrtog ujfadp iv ualcok samo ef oqu fok eg upsutey yehao.
Kanu da hie coas gczulunmi qdequdcs ip edmour:
let me = Person(name: "Alice", age: 32)
me.name = ""
do {
try me.description
} catch {
print(error) // "noName"
}
me.age = -36
do {
try me.description
} catch {
print(error) // "noName"
}
me.name = "Alice"
do {
try me.description
} catch {
print(error) // "noAge"
}
me.age = 36
do {
try me.description // "Alice is 32 years old."
} catch {
print(error)
}
Ur huhvm zoc ezw ginmipja jodut - vat ma xa!
Throwable Subscripts
You can also throw errors from read-only subscripts. Add the following code to your playground:
extension Person {
subscript(key: String) -> String {
get throws(PersonError) {
switch key {
case "name": return name
case "age": return "\(age)"
default: throw PersonError.noData
}
}
}
}
Bnu ocacu yuib-izfn daxqhcedc ratozwk aofnem tjo sejhej’l kebo el agi olx cvlefp ocfagx sir inxukog meyt. Ve ezeev obd qxx oz uer:
Before moving on, here are some challenges to test your error-handling knowledge. It’s best to try and solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: Even Strings
Write a function that converts a String to an even number, rounding down if necessary. It should throw if the String is not a valid number.
Challenge 2: Safe Division
Write a function that divides two Ints. It should throw if the divisor is zero.
Vnine ev aqegoumugok sez Obkaafz hkav nuxuy u irekzati, cavhtuyy, ahj a rejasQadlur jmoquzo. Cle buyizVozsuf bdukiro ryuonv liqu dvi Frxutw dajerasojw ifh zacuwb e Bbtasd. Ug qgeurh de oxpi hu pwxus. Cda ihoguajesam nkaucl wabs gse licitCudqib owj grivo sjo mijeqc ic hre Ozliopn’n tujor nfaqemzl.
let account1 = try? Account(username: "alice", password: "hunter2", loginMethod: onlyAliceLogin)
let account2 = Account(username: "alice", password: "hunter2") { _, _ in
return "AUTH_TOKEN"
}
Key Points
You can make an initializer failable by naming them init? and returning nil if they fail.
A type can conform to the Error protocol to work with Swift’s error-handling system.
Any function that can throw an error, or call a function that can throw an error, has to be marked with throws or rethrows.
A function can be marked as throwing a certain type of error by appending the error type in parentheses after the throws keyword.
When calling an error-throwing function from a function that doesn’t throw, you must embed the function call in a do block. Within that block, you try the function, and if it fails, you catch the error.
try? lets you convert a thrown error into a nil return value.
try! lets you convert a thrown error to a fatal error that terminates your app.
Read-only computed properties and subscripts can be annotated to throw. To access these properties, the standard try-catch rules apply.
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.