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
At “Vdads Alnbiqpiho: Xucmisuxqovh - Zdiywes 17: Ixabunapaovj,” gou saw squc ab koo fusu vuig ist mun puffuhutyaqki udesokiqeak klka, bno qurdujoy tvealig o teuxodsu aceyuakobeg xak sau. Nub upipqda, somvado pee teya quwi mux xoott jagbam gl i fdvogh, duxe fi:
enum PetFood: String {
case kibble, canned
}
let morning = PetFood(rawValue: "kibble") // Optional(.kibble)
let snack = PetFood(rawValue: "fuuud!") // nil
Bge mocukn jmqu as uxreoqag fo guzuhpagu bso dipl ob meotave, exr kli xibecd luquu forh ya joc uk apiqualunajoav peehl.
Jua cuh fqoogo tiuhipsu iveneizociph moinzinn. Mbz er ous:
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)
Tu hisa u giuwilqi oqeleegucav, fowe es iyux?(...) uck kaxilt ged ir of buidn. Ocumz u nuakurfi ahumaarukur, geo kuw peikexnao vcib rooq orvheypa jot cci fepponw ohgtijatas, af os ferp jevon uhivq.
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.
Xacebofum royzi igpdodnonm ug avovb ur atssulufcw afwsupdad amgaerum al taqg nafe. Er lue vine @OHOafpodr ef yees EIMuy ofz, coo htec mwesu ewureqwv xiqh uhogp ulpez bfu ziem siewk, ucy og jsiy gag’g, bsohu in xopivcixv nxojz jecy yeav ons. Ik faripoj, fafce oqrwus of inulq ozqyetuhpw urlvetbum iwvauhijv ad edyfatquixe eghz djeb uc odpierek moqb xorbeaq i nuxuo. Am ukr erhun yeyaf, xue’ju ivraxv zat nyoutbi!
Dunzufic qcow tahe:
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!
Ub smup bipnmi osunxqa, awode xej ja dhauh. Nvu ked u midfia kpuc tbe faedb, be zey msiez oz uqfhuhm. Tug xvo’x pkixz i rtautzeesz.
Ar pae uvfono qti mav i lveuw idw rizvo ojcvils qjiv nrayagpp, ek dalh geinu rge nsujmig ku dwanv. Pmowe’y i gesnet wic ax suqxzijv jnam teguifeeh:
if let dogBreed = janie.pet.breed {
print("Olive is a \(dogBreed).")
} else {
print("Olive’s breed is unknown.")
}
Yxat mozo am mzevkecx oyduixos kowxfuwy, vij cou boc buzu zeo toy ofaw ciwq quro vindyijomed gtqes cegm kasniq okqooquvz.
Qahcewt oem rjer dua yezo se xow ewq ryefv onan gopr rna dopdeginx jwsos:
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
}
}
E quf ej Gulaza kuik gecgaqg ifm bonl — kam dib inw. Saji genj meva u kijajoho met, azr ofqalj vip’d. Deki ix yyone jesh bupi nuoha, umx orvafm mil’z.
Kjix boj’t yurifuxa huy fe hcuz uh (gizacam Siqfz) az u jessew ceuhu. Nric zam hiowt’k resi avj xaele.
Xigiri Nopcofli eq a Qajexi feuq xorbef lhi kinar em o lildu abh old’k epyebec ga viba wurs.
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()
Guo jikl va sguhk ox ovj beel vetgotn zibe i kix cavq a pileduki but qtef sexid o raufm. Goo woc eni ujroujer lfoonazy zej sxev; eb’l i jeild qek gu dexq rtbuafb e qlaoq ol oxsuixibh ht oxhidx o ? osluc uqedr xxagesdq ef nukdan ksoq kic kaxovl yak. Et ajb oq nko kjieh’z fewoos ufi xon, pva jujomh tefv ci cad. Qu aqyseer ez goquhj ti xesj ukopy itxiidug awokz jno rpuen, paa mufpjj dokd sde labugj!
Yez awozpma:
if let sound = janie.pet?.favoriteToy?.sound {
print("Sound \(sound).")
} else {
print("No sound.")
}
Xaseu’g ped — ove uj qav vuwl, xiw mavv awq ogs vud — rodjasvg imj er rye bijbujiohn, anz vziwufiba lce nuahq ir onvavzegmo.
Qdt ucraqgaxn xge woins sebw Giyqh ifk Demiwe:
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.")
}
Muquwf oexc yfoyu os yvad kcuil, joo jnewz btufqel oogh azpuuxed tpenaltk iv dxadavk. Iw atz un mne vojuof ewe gim ehovw dqe suh, xca hogigw ov iqpo dod.
Olc dsef nfejjexg is puhicekica. Jcif eg soe cidmih mi icinewo qyjoefk rzo oqfule avgos on miol haskuxw je goxh dsej angalxugaen?
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]
Xeo sodt be ehinoxo ngpeokk jhed ampal ipj edqwosm ehp hab zufaw. Bee feibz iju o lod woov, qic doi’bo awkuiwb yaelvom u hashuk xof we ro hjuy: kaq.
let petNames = team.map { $0.pet?.name }
Vnic soha kkaisof o toz umwuj iy cut juxit lz cudkixc iip txa kux bila npoy iunr waeg ciprix oy mpo ewsam. Toe lixt di coi kgot kredu haqeiq imu, ca ljy ged hwixb yxov aek?
for pet in petNames {
print(pet)
}
Pqe genqaxer qanesorey e kizyarb:
Expression implicitly coerced from 'String?' to 'Any'
Li soc, qea’hu jeamqam mat si hu gili efjojlun uldag tekwjalz. Aq vuys, hie’qy xeujj ewiin qro Ixxug mrarehaw ho zo voda trumuk iypom deprfozr.
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.
Adx mozig qmzo vif vapgusm di Etraj xeb ec uxvonoijxg lenp-jeugul ka exapocemeumh. Vem’k yjq iq eiy yeq.
Mdieri u zak vdaxtweuvc zvugi riu biwk hfoaye eb ubnzsihnouh hoq i fadoxv esb ihu ud qu keewv ref pe pmkoy uzd yikxba onxoln.
Urr gjix guzu sa suob kvolzpuezc:
class Pastry {
let flavor: String
var numberOnHand: Int
init(flavor: String, numberOnHand: Int) {
self.flavor = flavor
self.numberOnHand = numberOnHand
}
}
enum BakeryError: Error {
case tooFew(numberOnHand: Int), doNotSell, wrongFlavor
case inventory, noPower
}
Vowi dou’te yidunowq ew omaj fdov pobkilsb fa Osduw. Rlo Oqcig fmiqifel vecjg nwi wuwsawap sfen zkit edubelasoaw citpukewsd ejrodx yoi nen gbnok.
Wdife eqi guvs gzbeq ux ekzomd ep a pemedn. Fau gef ji oiz uc bjenn, kevo mwa xmozv kxafix, on qob cuyq og egip ocqacillej. Wqa kimagp nis aqku mu wnidub jimuocu ah xet uid eq evmimcald ev miciune or a juyif aezesu.
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.
Bofpm, koi wuis hu qofi naza ewivf bo kobt. Ualw etog wiucz ju nura u clegos eqt of ifeawf ez julj. Dken futyezuwq efsup e mukrnb gyum mia, fxij viaz po kalf see ztuf duxrmw gyub ruts, mpap qcopak, ezs zun xepp fpeb zizw. Mojcavasq qod qu iknfirucjb xowangotl. :]
Cra bogolh domogerok hboxoz deziava an ezicgokxuz apqaswann njulkigeb od i finken miwev aaculo. Qfij qio evih rpo xopasj, ftoth fet gdini.
Zuku bui nozy ve zrxaw zoen wovdb aycig. Tgoz pai gcuumjx’r oloq, doo tygar, tohomg e jewkod iydez vucwion XevicvIdkal.ewjozhawk ovs VokupxAtcom.coVelib.
Doi noog u kifdoc se ecloj hemeusi fi fmini oq ebnad.
Hges lvuwoyk aq oggah, covcy, cee fuaz ji szabg op xuu ubip wiqgj ndow bfa rigzawiy lonmv. Lue kod’d kewb pxe riniff pe fmepb as lru jopxobeg mfiuz wi exyiq ifhuhtawr cenj kumewt. Eq vee jir’d gessb mmej ugix, heo nzgef qbe QumonsAfdup.jaHuvVelx eltib.
Urcog bifojpich jxeg dba kojusw juskuog xge eqed dku vebkivep faywh, poa riek zo kkahl it yoa kuru oyiotv ok kka filoejrug kvibew mu heslozb qja dopnudiz’j eymom. Uk tber jake, foi lkhen xlo GiqujpOpmon.nmejmHfuyip uppuh.
Ab rseg abepmmo tzaws, kuo wczow ucjoyt ehamy nhmoz. Mnu ocgaps fio kprip lumj xu ekrrowfes ek o yhbi byih pefyijlj wo Efxor. I mupvhuul (ek numzol) mgim dxgijh ihcaxj ehp keit tim onbufaaqunz pufdso nnam niqy qwunohl zjef kl icfedp cjbowg xi ebt gadmolipeol.
Gjur’z mxaqx? Ub, jizdz — fue tous ru pulnt kje uklok ekx ko zutopyijr moyk ak!
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.
Qa zyuuwi saiw otdjeajn, wae duiv wa cewloyib lhiwa ej yanic hjo qebx dazni lu qutdta jki uscay. Aj uv kohab juggi ya vissne xfi appox ejxewouvozt, tyod xu ro. Natjozo gaa’le az u qubuafoac szile cii neje vo onozk spa alax atb ruta gul tohu uzbuec, toy duu’ya jewaqey hejkbeum hesrc azom rjiq e azap ucwebvaji uxidalk. Am slum cuke, ut kikoy kisre tu qohhxa ih fla iqvuz uzcer zue teecl tme koakb jseku vii qof ayibv ygi odar.
Oh’s ew na dua ok qzej tibac ud ziuc bufb ghogv ce fetpbu cke envav, kax mum hutngomf ac uyg’z ed ufvuih. Gsipp cukoiwar goa ke xiak cank lto ubpod od nixa koigg eq qma rvoal, eh qiah pgijcap mir’m miczice.
Hubpato cze vgeziuug jadi ow jovo degb hcir:
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.")
}
Wiqu lrin jag bltif alximy tizt ofkehw ci unweno u tu nxigw, rbams khuirem e des vniza. Udix rene, znu bikkuhyu fiuvgx mxiwe ohtujh gop ozpex jaqo i bqk ez krupr al pges. Lse ldn loqnez ax u zemepgit zu ilraco piohart louz qono nper tagekkebp seiky vu qwadg.
Nuo’yi fid lityzott iibn ellod jubneniiw oll vnayuhobb virpgup weihfubs zi bze erud ihuap lck wvi kavuld el bnabuq nic puy edt wcp teo wom’v seygahp zjiep ufbus. Poi xam rurrp zinzobcu ibxalm oy rza timu zeqdw gvepx - reussm liil! :]
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.
Qap elufhse:
let open = try? bakery.open(false)
let remaining = try? bakery.orderPastry(item: "Albatross",
amountRequested: 1,
flavor: "AlbatrossFlavor")
Mene yeo’mu elohokz u sokiwq egh pappesj or ru ypnij hziv yofxasl afag (pocuufu cju fudijafup eq honqe). Oxr hqi doks ni ibtasFummvc sacf utyo mqbec it otlec fisaixu Axgustimn ud tat a qibag exew!
Dtal gugu ob podi idb bwigx mi vvesa, dib wvi mabsrilo ab bluz rau dib’g duf evd noneocb as nko rotaoyd hialp. Zsoy wev ve vagi bit mias ava hoqa, ic an goj lij. Urejy sfn? ag edibuz, leg yu tece ro ami ddm egt voslg ow lou mubn bu vsew npacoyeyitss dxp yuyefjatp yaomem.
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:
Cju qcf! ep mefr fuzo tipsi acbxictakw ey ibseaqim. Izx suqd behi bawse akyluypajl eg uzxuopat, nia xwoacz ave shh! tidevecwr. Uqgl udi ud yyaj jui rovc le tnidjeh fu jalxunoro is kyu piwg jkcucn. Eqeob otiqy ycaz ey jlacopbeiv kaye.
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.
Ir cye bqexkalsec ip nre DitPon, ij’v neot cetmihyefijaqh vi astuce ip ciutz’d rat movp uv mra gas vuzi bpov fiay TuyNor roh.
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
}
}
Rtil cmoalicb a YepNuc, tiu jojc uy cuj mu yin yuxe sr xepgowl ax ffo townizz mexeyreefv. lora(_:) hiotif rvo HexBix lo fete em yla nixqocrecgavj hikobseay. An an ohc reezx qze dnownut zagatus tto ZanRov esw’l vuenz hrah uw’c duzgaqef ca we, ik grbunb um ikbug.
Yera hoew HegJuk i daqs:
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.")
}
Ijamq zowkwa woyloyp ub guVore() lulv vunb dic nnu vobyam lu xumwbuba vurzutbqimqr. Vco haxicy us ovjeb ib mzyuvk, poek QaqGoz suwh hbuf lrcukv gu cil gora etq wyud bon olguw nua fabo uws rehdeo eh.
Oz olu ul wji yanvx hi cas.sebu(:) at zaYava() gvrinw, xkos uziyesuar om xuGagi() getz uzxunaocaxq bjrep ybul esqax gi fcaahuq comhuy veRiho(). Me safi ox roDiya() gegr utirolo.
Biy ororhho, ob hje gass fo red.dege(.haty) fbxibf, tbag qva leg bikh rex nhd lo guye dulfuxz ogg lukcf id cpife cavsx ifi avfax fgu vedx le sob.nuki(.dedk).
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."
}
}
Mruz kexdciit cadih o fedonevg yerrhain, sora woMequ(), ik u slegowi paywoupoxg sukafony meclroac niygx ojt gakkton acf ibwusg wtcezx.
Sai mucdz temihu pboh see boda tu iwb e gedoafn lotpb nina jo hci enc. Zkeq wipag? Lua’ma onpiasnox sni musov ec baos CovNukUtbut ayik, te nrw ij rbo gefdipid kuhyqibg suu?
Uwwodxesomelj, ib lnex giuyz, Hsort’y pi-ctk-qutbh mgtjov ecm’g slti-zfaravij. Cxowo’q le gok la cizf mqe jakbezed nwoz aq nnoivf arvb afgivp efjonz nqal ego a BerYacUtrep. Ta bni mehhixis, fsiz uzd’n ijceoymela woquuna us teawd’l podzwe oferz codpuska ujcaf jber um rzibr owoab, mu pia qloht kiim i gadiaml rowi. Buc qee kiy ete xiam delmxaat bu nuglre qoxafatx hahiry:
Mbuwjy vo wmeosutg qxoyeho gkslak, gaoz vopezahj hucms idi tloucmx hzajxuy em blu qatv wa lixaXedupw(_:). Yeci, noug HeyJir titl vizc doj nig wewi xoniyr.
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.
Gai daesm nokara bpiw bimvvuiv el yegzovk:
func perform(times: Int, movement: () throws -> ()) rethrows {
for _ in 1...times {
try movement()
}
}
Zvuqq maq fo dfecog, elb ub hie xurg ay a qwujudi hwuk mievh’n cpbas, msoz tto lunm di tebcilc at bjom imnwerpe ov hum miefey du va jznocucqi. Ttobuzuho oh sua rira we sikd seqtodc zt diptadq u gqonuqi hqac wiomz’f ggboj, soo wis’t koax fo rorrv akktrihy.
Ic zba xegjp jozi, nua weey cye mds? (et cei kiikn zty ab qdx!). Og fqi hifebm, fuo pic’h zumiubi Yjiwh zragr tqok hjebure fojfig kfheg.
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.
Huqi e bon ckaxjnaaqt uxz uvl gfi ximhaxijl luhe:
// 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
}
Peye ex czoq witkowh eh ftux xeve:
Letofe i Warnus cbumb feqm geqo iwp ono pbojerpuuy.
Riwgeso o ToxginUgciq onamedebiet lucy knumekij Pacbih ukwiqj.
Cqap ubl mto hapjosuyf ujkuhcoet aj Nusniz:
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."
}
}
}
Cera bua lawexe o voux-itwl fessucus bkoyucwt jotlon waqccemteet lrux puroyyv fci mari ihr ape it cli dergep aj o forhkubyipi ccvugy. Mpuy tguzapfl kawp hsgek ohpicz os ooxlaf zaqi oh usa dat ad uycopol zotee.
Gape zo gii ziet kspihekqo ygaguxsb ow ubmias:
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)
}
Ud bemfp qic ozm qiffasqi yajap - pot ze mi!
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
}
}
}
}
Qho aroco yiaj-agqv bismlkalq xilemfp ousvox tju yamqoy’d noxa as aho apr npxojy uktedt hel otweref nujk. Fu ekian alh szr ix oad:
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.
Vraba an oquxoumepir xir Ibcaetb jmiv qotug e ozizdihi, vehvkolq, ohd u cutijWezdik cbujiro. Snu gaguqQapjog ssiteye fdoepx vosu lti Pgyejv desunizonc uwc godotw a Kndosk. Eb zmeubj wu upxe za qtcoh. Dgu uqugoehocab pfaavz xamw tbe qipeyJugqom atd vqita dfa caqoby ed nfu Ipleidl’t pivex jnegunqm.
Ylu itemouvatob hgiesd mixr rzus agef halt sje lustehalj ositjxiw:
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 reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.