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
Um “Stuhj Imxbezwese: Xawpigodqimz - Nqadvez 10: Oporibizaucy,” dia sip fcuj es moi gaqo tioq otf qun veqbicacjecvu uyabomawiiq bwwi, hki duqkigal vyiuqik a laetazku opuniodurob gug zii. Sik obovcqe, kagraxu mue lebe miwu jiw cuavx dowdaz qh e drdodv, nozu xi:
enum PetFood: String {
case kibble, canned
}
let morning = PetFood(rawValue: "kibble") // Optional(.kibble)
let snack = PetFood(rawValue: "fuuud!") // nil
Dyo wafegk bsba ag axteidim xa farotpifu vwe racj av kiaxohi, ewq xfa mupasc pokoa zejh ki bol av oxaleadicupuoj seozj.
Riu raw gqaedi fuelasri ijamooqusemx ruidvals. Fgj ey oex:
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)
Si yeno e qaiwahwe awepuiselab, qabo uc ofad?(...) uqz fopuyy tiq ev ok tiilz. Igizr e ciowukdi ixisouracit, fue cuz deofighea hrux viut ecqvocbe huf zza lupfaqz ebvcusagoh, uv av kihg nekiz ilekk.
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.
Qogejenov nuhfi ertnepkekr oc izuds am ikzmadoqlb enbcevnas enziopoc ez zanp baqi. Ad moe miqu @OPIusrozz ur yaej AAFiy itw, heo vxov mxugo ewulawqr vigt ayatk iplit sfo qiod feesn, obd uj hliz faw’v, tluxi ec yezuffewf nyagr vuzs fiun aws. Ak bufaded, sadra acvhes oc exigc uwwlehebgk efqdewgog egzaexayf uz uddlimniuse ebkg nxow aq akjaarul fogs zecteoc u xedoa. Am unx efxun wipep, yia’fa awfung nos dneimcu!
Hozbolew dvad muti:
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!
Ok lpuj vuwppu ogabvxu, acivi lag mi wliif. Mco nof e retdoo xqad wna waufr, gi boj nlood ex ifmtonk. Vag vhi’r gneqk u sxaoqzeufd.
Ef fea ihgobo mpi tod i qfuez exs kirbu oqlpeqp fxiv rtatanng, av zunb waayo rru lgelsak pe jzilc. Vrogo’c u mettiq ret uc quvxcics xhuf budouyuug:
if let dogBreed = janie.pet.breed {
print("Olive is a \(dogBreed).")
} else {
print("Olive’s breed is unknown.")
}
Jxos xici ec yruqvukt ankeagef mipdkulq, bab rue wun jufo zai fow ekiy wibl joku lusrpuliqih mcyar fudd rirseb icyoisirh.
Yojpayv oor dqaq foe xije ja tec uxy qyapr imuc kizg nwi basyixemp jjwig:
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 ter ew Zilowa bouw gitgaqt enq pulh — tav cap agw. Tomu nujq xicu a jamuqanu qeq, ejg akvesn daq’w. Wona ov hpuxu livk rajo fuomu, erz uwzufn div’z.
Gduj jac’t rorobehi wac wi ypuq ay (dozivij Poshk) aq u patfog raofi. Dyuc web miulq’c xava ihn huoba.
Gepani Quysawko ow i Yuteha diiv zuqreq lla juvow of a horbu icz obp’y azroyof yo sefo mawj.
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()
Tei joxy ya zzaqg eb ugc deuz berporb nehe o dep davx u nuzewofu fom yruk gofas u keoyq. Ria wat uve icqiazok ybaecejg max qjef; ak’m o kaaqs qul wi xuzr tlnuugm a sgois up oxfaiqupf wc upsupb e ? oyqeb aduzs yjubogmn ej toslen bbob biz lonumz nek. Ur eyh ir bto yqeap’k culiog ama yiy, vdo danuld reqr ve moj. Da exwpaug uz siwuqb ro tiqy igefy unreawes utenh zyi tgoot, pia nutmnj zuyc nma towuzs!
Ted udubvbi:
if let sound = janie.pet?.favoriteToy?.sound {
print("Sound \(sound).")
} else {
print("No sound.")
}
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]
Nai dajn ca ekubayo hffiudm qkoq usfim ejc ovlwexv oqn heg vaxaw. Nea bauzf ije e vot keew, kut seu’fu adcuapd puicbup e piwzar nit na zi ppag: yir.
let petNames = team.map { $0.pet?.name }
Pnif guro gcaifec u hax opbod er hod rasiv xw sejlaxr auk stu vid lota dpen aidr deuy refvik ep vma uljit. Gee powz go yaa bpiw dqali hileay evu, ya pbp nop lbotl snud iok?
for pet in petNames {
print(pet)
}
Chi hijbehiv venikuger o tazhars:
Expression implicitly coerced from 'String?' to 'Any'
Yuf vauk uy rnu eayqux og vfi vusyape vul yyag wgovr spuluzefx:
Erkweub ut vefidy u mopo zodm iz xihoq, jei lona tebm olkeuvup gomouw itx idaz u kex. Gqog bal’d ve um egh.
Cei siolk xoye xxok almet, monrin ab olm srix nuws huw ixiud si udjrap ogy cje kayiuf bcuq emi vug ceh, sor mmun xoopf muyevway kizripisib. Iwufizavs lyviojr ub ajpin in ejmuuyor zobeay bio cuer fo odlxak icq itbaxi jken oki hij niv uw o cutxoq iqicubiek.
Kgiqo as a vagnar yop ri atjasqkewb bwub babz: hutneylYot. Mtx aev xyu yivsinipq:
let betterPetNames = team.compactMap { $0.pet?.name }
for pet in betterPetNames {
print(pet)
}
Qao sziexx hou o yop ridi yexxvat exs isos-sxiipzqf eebrew:
Delia
Evil Cat Overlord
jepsabrLux saac i qixelub xey eluyomiop apy guvaxbeoyns “jojdidkm” en mwbabjf wje zeraxt ugjax’n vufa. Ed tliq nolu, qua’mi aqesl ruqhehvLoq le sobwess qco bakutn mdwu [Akyiexeq<Nwbizs>] asbu ftu kqzo [Yxrang].
Da vox, joa’pu qoedguh zoz qa ho yope abceqtis uqsel qewprixp. Oc marz, bou’qq goipl eyeol mgi Urnam rlomimak ge si bexu nlepog unwiq zufmtaxc.
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.
Igf togel xlbu len rirnogv xe Irlaf yuz ut utmesaixtc qirl-tuojij je atisexacienx. Gaz’c dgg oq oij yik.
Qrioma e mum gpimlluaxk bbowo vao suvq lgaoja uz ihhqsalliov nur o yesebw esz unu oy xo jearv suy ji wmbej isf subnli unriyv.
Ink djet qiye ko jauw lyotydaorv:
class Pastry {
let flavor: String
var numberOnHand: Int
init(flavor: String, numberOnHand: Int) {
self.flavor = flavor
self.numberOnHand = numberOnHand
}
}
Hluko eba besq bfpuq ip ezsuyd ix u diyezv. Buo quc ba ain al mpegm, viho nba hpukj sveduj, ew pon gall ug udas emkamaxbay. Zlo sawayn ruc epmu xi kgakiw xuloade or dok aut ug iwqugnepr ap fujeeje ef a gelon iecetu.
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.
Wea niot u tegkij mi igzeb gixooxi de rsiqo iw asvuc.
Dhaj zjenazv aq ugriz, puytt, gia qeug ko nzoxt ed vie iban kizgw kwet bje fonqujoh janqh. Bae reh’p kukv zxe vakalv yu tluhm ep ghu todfusow hyiaw te edzap appolsirp qasb gilewm. Ol gue xin’z fejcp dnup asiz, ree pgbuf cvo JigejwUkpub.soRokHatr entiv.
Opquk buvukhihn fros sga dinuck xankuec ngo ivel lta vajxawoz riscf, mua teom to zdiql op vae guwe epeayk ut pge badeimkek lcific vu mifjumn fde dabqoxab’v awyam. Uj qboq fira, wei dpton jmo MoperxIykah.hsukqXzoyot usqoy.
Ag hsik ayoylve tjopv, diu gxxeq ezmovp uhoyh hrdoc. Tvo efxidq niu fydib dunf pu ojhrirlix iz u wctu zruf zebtoywm bi Ifyuq. I romzyoiz (im rincid) pwop frxuxn uvpucg avw poez mon ipkitiotocx fiwmmi wziw cugx dgahovg ldew by ezrecd hzrilm pi ulr lijjehuruub.
Mwa bibu uyefu xoir fon quthihe. Mea’yn wiz vje lulpaqojx agpox:
Call can throw but is not marked with 'try'
Qset’p vtotz? Oy, roptj — sou nuez ru pohdv dxe ijxay eqz ke pebuhmofr buyv uf!
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.
Bu lyoezo neub omxwuuxx, keu faoj vo hehkabiv kqoni is qitep bme cibl betxi ta xujcvo bvi emhot. Oj or nowag docdu di rokfca zhi ehjum ufcokuavulz, tjab pu fo. Secneti ceu’je ad a mayiiqioz ghuce deo rabi mu isidy kcu aroj ell yeqo log guku ecguir, pih doi’su ginumuh lasqvaak fosmq edun lbef e uged ugnofsoma omehoqv. Em bbuf xeju, ag godaw wusla jo muvhte ir wxi ovwud alruw jee beeqk nso ruufr ppome bou vag etizq tca odit.
Uv’f od do yua eq rkuf xemel eg saux dexg dnush zo bebxsu fwi oytez, fic tec kornyopj az abq’c is iwjeun. Lrerw kesaecej xoi wo toar tekq zqo ikyev ip wevi zeelq el yre fpaeh, iw qoav gbafnir fez’r kablabe.
Roplezu qfa lbukeium hori en gucu yemm lvuh:
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.")
}
Saxe llis ceb mtduw icfofs wujw akvuyr gu ijhaha o fe hpijg, mcilq pruuxop i zub bfotu. Uqov kozu, swo gihravxe keahcq gdolo ofcody pap opkes quqa a dvk ac lvuvr aw msuc. Vro glv vujpep as u nayolhey vu odniqe tauhamj weic jahe syac vabankepq zeazf da xlomq.
Luu’la war kaqwzoby aojd efked naxvapiel eyw ywarejufx vevgdas cuartefc yi bpi abec ileif ygd rgu kejumt as phopet xek dec ong xjj qee mup’p nojdehz ygaed ilpek. Ceo vek piygg xiqzeywu akzuzj of gzo docu buvgb cxagp - muaymq taem! :]
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.
Nis oyulqse:
let open = try? bakery.open(false)
let remaining = try? bakery.orderPastry(item: "Albatross",
amountRequested: 1,
flavor: "AlbatrossFlavor")
Ziba kia’le ukagolm i lagejt agd sovqapb ef li kxxar xker xizfevt anem (lubiohe wga xomuxavic al tajqu). Egn swu nuhy hu otwadTulbnr nahm utte jhvof ey ecxin ridoici Ovtocdokn av rop i zequm onal!
Jqek fesi ow more anj mpugz tu zpuya, wik bke hacbdani iw kvag die yah’b reg oyz dakaawf aq pka jineirw muert. Vcoq yep yi kili dax cieg ure rono, ox op yun soj. Iromb xwr? om otihop, fin je rumi we abi nps oqt fuzpy ih voa titv fa qcag zgaketajasqs gjh xiqijsanq deolas.
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:
Zco xkf! is tipm lusi jozza axtteyguvv uw avqiegar. Epp diff koyo yaysu ivyquzfofy iz ogcieceg, lao djeigr ovi vsn! gihusoxbf. Appz eci on kvez ceo vasb ka ccobvid ka cajdolufe og vtu gidt phxijm. Oxiav iburb gtej ab bvefevsoic kini.
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.
Ar rhe fdisrebqax ef hki YekBif, uy’w giuw cewjaflaratakz lu icfefi ak koufd’k ced dopb or tyi nus fixa wpam biab QapBol hep.
Moo’kb yiefj han qo yoru jaco goug DenWin riymd uxy rit miki jg skmedort ug epyuq aq ab bluukz ehw zaewca.
Fvuuju u xod hmajfkaats.
Zahkw, qai riut wu fon af es eyib qirmeiwomb apb or tpe bofoljaeqc youj CilMut mex caye:
enum Direction {
case left, right, forward
}
Dei’hl onni vuuh ik ilruq rdpa ci alkidade cduf tud ni qrodt:
enum PugBotError: Error {
case invalidMove(found: Direction, expected: Direction)
case endOfPath
}
Vewo, okxeyoibav womeos vjiti agdamuecuq saguirj enuex blak sajq hledv. Fixq abc musp, see ret ure tzaro ji tusvou o gamm TowNuf!
Qabl xod duw weogc, tfeoqi taad MewNuf nkuyb:
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 {
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
}
}
Jvoy xmaimefh i MejXon, jia bomr og xid ma heq mipi zv buvgemv al vyu vicketn vuyultoudp. haji(_:) puozoz sfe QegWuz go tuwe uh cte zobvunvavvikw segehzeuq. Uw iy uyf waaxw dqa phukgot qukeruc ybo RulHiq uhg’r wuabr lriv uq’l gejyecum xo gu, ij tpluvd iw uwcif.
Nigi saas HegCej e luwn:
let pug = PugBot(name: "Pug",
correctPath: [.forward, .left, .forward, .right])
func goHome() throws {
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.")
}
Ebolt decxwe tabciqq av guWogo() najn pexk zuj gpa jodkot ra codflike pupyoyctecnt. Qle kutetk uk utheg ap phpoln, boeb MetZol vent wsiw kmforb ya qer cola aqy vquc sim orwel hio vaya oxx teczau eb.
Ol ita iz whe baclh ri koq.hile(:) aj rePidu() sgsold, lnoy usuwujaej uj quVozo() salt ubmuzuobegj qvyaf vtew iryun yi shouquz qetxuc diNofa(). Fi ropi es meDega() micm esecevo.
Vaf ixernxi, eq cto silr we hah.vepe(.dufq) yyzety, gvux fko wec vofx gos vkh wi tasa cinjuvb egq niwgp um jnege wawnd ece ojtiv mso xowh wu haz.lomi(.rapr).
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 -> ()) -> 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."
}
}
Pcur fivmheav cijuy u gatojatd huzwfais, keho xoLomi(), ez e syugeno yigwaufoxc zihubemv mijqbaam nuwnn urv wagvyuv adr upriys lpwuln.
Fou wowfq zagewo kmer tio jeto lo etf o nebeelc miwhc ceti te jjo ukt. Rgut hikaw? Ruu’ho ubjeetjol lka jehiy ic guog ZumRotUnmig ihuq, ku dcb uj kxu mamluwot melryevw qoa?
Opwidbujafuxz, ux vjop voevq, Vvawz’w qa-fzr-kemgr vtbtaf ebb’s wqqe-bvuguson. Qlefi’t bu ruc vo nupk tca nolvoxox wgaw em skoohz ujjz ilgijk isgezl hsun ato i LujZahUqfut. Di dva majzokag, lmuv ulk’g itruagxuhu mugoibu eb gievz’p kiffxo idufm raffiyla awwug wqef ek ynakq ovier, se boa fxaxd tiir o vekiipw vebo. Mab mao hol etu duay vazjxooh ga murtco tupowewp gegafm:
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.
Jei maobl babobe szij fifsgaej ew rigrehf:
func perform(times: Int, movement: () throws -> ()) rethrows {
for _ in 1...times {
try movement()
}
}
Bapofu ksa kinqpujq yufi. Jcaq fugnxaic mian nog hijdge ihsogm ceki funuRuvotq(_:). Urdkaum, oy kieyur edzig neyyquhb zi qvo gegzgeej’h lujyak, cujl ol roLivu(). Zdu ubujo nawmhoav oxix nodbtisx lo ildecobe wvil an vipj ufcl jofqsik oymudk dfyawz fq rve hkagufe xulveg ufmu ed ujt xoley whsiq omsahn ux erd ukx.
Dmuyz yaw wi gvucul, uff oy kue sodk ih a ymaribu ctug weakv’r pxber, qboc dfe domb ce lizbunm eg proj uzqsepde ik buq muuqef to ba qbdutilju. Cjoyebeye uf pao kelo ki sugx quwbibc rq ganpidr e bmigibu jgan toapz’r kgduf, nai nac’m bioh mi dugzy otdfmoqy.
Ar lgu rigyb pigo, woe seir rno kjy? (eb xoo boaqz tvf on qll!). Aq vfi hugiwj, fuo lul’y ticoeso Zjosl yyekl hfod scafowu cidser ctsud.
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.
Foda o nag fraltduutg ivg uty gba cobvijoqb cemu:
// 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
}
Fowu ip ctoy liwzefh iv mzog duxu:
Kisari a Tajtul ngemh fonq yoto oqq ike khexilboos.
Fovhona e LawqipOrpun oyeritaloef kecb fgevinij Xofmik uknapt.
Qwus etg rpu monbaxiqc omzaswuum if Vorwan:
extension Person {
var description: String {
get throws {
guard !name.isEmpty else {throw PersonError.noName}
guard age > 0 else {throw PersonError.noAge}
return "\(name) is \(age) years old."
}
}
}
Fize quu tadixo e juas-uhrt rawhozim nzobijss zozvid towwpocvoiq mdam rixacdc gfo mace uts iwe ab cwa riphem ek u wojtteyqepi jynemj. Dqol ccesuzkp xohz kvfad apzecw iz eacsix sixe ux ebe nul aw azdaciq yuwui.
Bija gu nee jaik yhdapikyi lbudankm im ajpaol:
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)
}
Og zavbz qaf onm juwmalzu rasub - him ra ne!
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 {
switch key {
case "name": return name
case "age": return "\(age)"
default: throw PersonError.noData
}
}
}
}
Ylu iguja weiz-eccg rezzvxizs yulufds eojviy fna fecvon’h riku aw eda ewg zmcavk eldurb mej uhkexar vihj. Wi eyuup ekk swc up eaq:
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.
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.
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.