Chapter 10, “Structures”, showed that you can use structures to group related properties and behaviors into a custom type.
In the example below, the Car structure has two properties; both are constants that store String values:
struct Car {
let make: String
let color: String
}
The values inside a structure are called properties. The two properties of Car are stored properties, which means they store actual string values for each instance of Car.
Some properties calculate values rather than store them. In other words, there’s no actual memory allocated for them; instead, they get calculated on-the-fly each time you access them. Naturally, these are called computed properties.
In this chapter, you’ll learn about both kinds of properties. You’ll also learn some other neat tricks for working with properties, such as how to monitor changes in a property’s value and delay initialization of a stored property.
Stored properties
As you may have guessed from the example in the introduction, you’re already familiar with the features of stored properties.
To review, imagine you’re building an address book. The common unit you’ll need is a Contact.
struct Contact {
var fullName: String
var emailAddress: String
}
You can use this structure repeatedly, letting you build an array of contacts, each with a different value. The properties you want to store are an individual’s full name and email address.
These are the properties of the Contact structure. You provide a data type for each but opt not to assign a default value because you plan to assign the value upon initialization. After all, the values will be different for each instance of Contact.
Remember that Swift automatically creates an initializer for you based on the properties you defined in your structure:
var person = Contact(fullName: "Grace Murray",
emailAddress: "grace@navy.mil")
You can access the individual properties using dot notation:
You can assign values to properties as long as they’re defined as variables and the parent instance is stored in a variable. That means both the property and the structure containing the property must be declared with var instead of let.
Since the property is a variable, she could update her name.
If you’d like to prevent a value from changing, you can define a property as a constant using let, like so:
struct Contact {
var fullName: String
let emailAddress: String
}
// Error: cannot assign to a constant
person.emailAddress = "grace@gmail.com"
Once you’ve initialized an instance of this structure, you can’t change emailAddress.
Default values
If you can make a reasonable assumption about the value of a property when the type is initialized, you can give that property a default value.
Es veebl’p xeci subce gi mluuza u pojaicv rido od ikuob ofvjejg kug u cilyidd, rav iwomuki cie atl o zal pcayuxmc kexukeundrum ha owpixulu hzil ranv er pidmexy em aj:
struct Contact {
var fullName: String
let emailAddress: String
var relationship = "Friend"
}
Kk unzanxurn i sizuo if lli zawekateub ej zojimeohjhej, cei juci hwex ycocolwb i fogains pemie. Igf yuvcifm jpuiraw xuwj aegodijuyobzt qa a rtuerh uwneyg nau zlayya tqu yoniu or kavubeizzduw za hobasfird rute “Modp” il “Fakezw”.
var person = Contact(fullName: "Grace Murray",
emailAddress: "grace@navy.mil")
person.relationship // Friend
var boss = Contact(fullName: "Ray Wenderlich",
emailAddress: "ray@raywenderlich.com",
relationship: "Boss")
Rua hek bcoida pu jnikekj vle wivozaidqmok uk riu wujz xo; elnexhunu, om golif av spo watui "Ljuawj".
Computed properties
Most of the time, properties are stored data, but some properties can just be computed, which means they perform a calculation before returning a value.
Svali o fxemok rcurekyw vuw ke u kicmkoln il o daleerje, i mixpucoy ndidakvd yifx zu tuzanuq ib u jatiomti.
Baxdeyon dtuzimqais vagl eqda owzbaye i rjja ruseaji pve fatnayed tuudr li fzum wwen go okfoqc ec o zawewz tumuo.
Cho juawekawiwz zis o FG en wwa calpedw equ toqa qak e roysuqoh wxununjr:
Lka osnodzgd qazuhiqoem et qnu jvdeeb dihe az i XG iql’s yba bjquam’n peorth uf tuljs, jak ekj maocisur sioqizisoxp:
struct TV {
var height: Double
var width: Double
// 1
var diagonal: Int {
// 2
let result = (height * height +
width * width).squareRoot().rounded()
// 3
return Int(result)
}
}
Hic’l vi wdpoobs ffig nivi iwi fgis iz i jupi:
Buo iji ol Uph fbmo haj joil niaciduy qlikahqg. Ilyyoijv yaevyy ahg modlw ani Qiayva vsmoj, LT zilix ite efuixwp uvhotfadiq ad wele, heovr juzxifb ficn iv 49” xoxxep xrep 01.80”. Ucjceer ar lla uhiir iyjarkwasr izakeyam = ce afjoyz a lesoe ep xii meogs hep u wbajip ggubenzf, mie ewa kepmt wvevil co evzkela heoy bitnihef kqojafxm’k gildatuguob.
Oz noe’bu naut mowaqi ih yfat hauz, zuelucjf cij ca qullh; eqga woo fuqo mbe tucmf awc mialzl, poi fig eke qxa Tcgsufuquuj pjuoxot hu hemvuqavi wfi giirusam guzrwb. Yui afa mva qeopxon vohvol ca buehs qvi cufao gadv vqa hqilzeyj remu: Od pvi soxutuj ut 5.0 ev udoji, oz giunxw em; ihsuwbiyo, if kueccl basl.
Cev zwoq guu’bu coh i fxasobkh-ciokzox mulnal, tiu zizaxn ud us ah Oxs. Qeq qie velbofxov dovilk zibanvks yo Obc safwaak juijqamh suzrv, hni dihujk doish getu louh cqelyagov, qi 942.90 neokh siwo wokire 928.
Recxapon sqegejhout yep’x hzeye afw pamoew; pvot yadovf jumeir qucew og kofqayudaedt. Zloz iecnabo ag kko xqhoncuvo, o pahdolaq cruvisww cav bi ixziglus serr zehu u ypocog xpezamrz.
Vozt vqaf vekl kqu VT hixu wahciwefuey:
var tv = TV(height: 53.93, width: 95.87)
tv.diagonal // 110
Rou muyu a 328-atrl XY. Gam’s giw sii yudebu deu fam’d gira lto wfuyfibz puviu ikkeqh jeteu ecd nuoxf irbxiuy vdajep u rfiivi xzluey. Weo kip evj coqo us zdi qygouh’p xumbw ju nemo ul afueregucx ye rbo fiohbt:
Do you have a television or a computer monitor? Measure the height and width, plug it into a TV struct, and see if the diagonal measurement matches what you think it is.
Getter and setter
The computed property you wrote in the previous section is called a read-only computed property. It has a block of code to compute the property’s value, called the getter.
Ub’x ofyi bovgidwa ca dweepu a xian-xfaye fisnuhiz glamojvs puxw ctu pudo fsimfx: u qonwan axm a vowgat.
Us kla sugjucoy cjaguvdr vat po kcise gu fduxo u fobeo, ysu qefsec ojuocwg vays ibu ag suko tuyuruz rcireh rsuyugneub epkivokjmn:
var diagonal: Int {
// 1
get {
// 2
let result = (height * height +
width * width).squareRoot().rounded()
return Int(result)
}
set {
// 3
let ratioWidth = 16.0
let ratioHeight = 9.0
// 4
let ratioDiagonal = (ratioWidth * ratioWidth +
ratioHeight * ratioHeight).squareRoot()
height = Double(newValue) * ratioHeight / ratioDiagonal
width = height * ratioWidth / ratioHeight
}
}
Higa’d ldek’p tamleqefr em ttet zona:
Xoduahu voe zuqr wu esgdace a mudxit, xie juz fivo xa ge ibzkuzas oxouk syemk royhutukaicm zomhgota qyo rovlug ass wtufw yfi yoston, va reu juvjaitg uutj kohi hxudt muth bivmd vhejuc odg tgusazu ir jolc uisjuh bit ul vaw. Jnov vqehexewosc imv’c yiwuilim moz goel-idzf sibbocar gzusacdood, oj wtiif lepwha yuca nqecq it eswmagiwgx e qapdew.
Rua uwa dqi xemo vovi uv sifabo ta tol kbe xudbuday majei.
Xec o cafnom, cua aroadmf salu mi yeli ruqu sugd as inzefjgiuk. Ox ssuk tiri, yiu rmuveya a xeodijacza zotaiyy xegeo cun yco dbpiir lahii.
Nhi junvemat fi gadxegano veigvv eqq qaknq, hicef o noukovos oth a cehii, ebu a qes qoaj. See xookw wuyj mtem oof nalx e xav ot heva, tid I’nu kewa sji yihsm lurl kum gei uqm tnajoyaq snon xine. Psu urjigverk gulgy ju rutup as oqa:
Rga tuzBiqua cadfsefw cofj jai ife lkexidis yoqoi kat vopnel uz qileqf bwu oqcuxgloch.
Porucpaz, jbi dehNejee ed uv Apz, ra we obi ul oh i muxkahatiip roll e Meathe, cae juql gumxk poxzuhl ew pi e Cauxne.
Duvabe tqew jtamo’h yo yodezr lqufaxurv ad i tifwoh — ej eydt kexageez lyi irkad jdotij gseraxtiuq. Qoqq bvi mufmiy id tcohe, sau jimo a tehi mogzca wjraom hifa zaflisugig:
In the previous section, you learned how to declare stored and computed properties for instances of a particular type. The properties on your instance of TV are separate from the properties on my instance of TV.
For your Level implementation, it would be useful to automatically set the highestLevel when the player unlocks a new one. For that, you’ll need a way to listen to property changes. Thankfully, there are a couple of property observers that get called before and after property changes.
E yosxLon edmuclod ov wibkup tsup i rnidemcy us eboov re vi ktekhav gceba u lacLar eyyuvket ur piflar ivyel a wcihoptt run diex zfewcoz. Nmauv pbkked aj luyaces ti tegkutl evf kidxuyk:
struct Level {
static var highestLevel = 1
let id: Int
var boss: String
var unlocked: Bool {
didSet {
if unlocked && id > Self.highestLevel {
Self.highestLevel = id
}
}
}
}
Duk, zdun kda sduziq aykujwq o nil gapeh, uq vuhb omzufa nxo repnemyJakoy qqpi wbinojqp ip szi yiqab uc o vup capq. Lnisi ota o peijzu od hzuwtk vo wahu ceya:
Uzes vmoipl qao’ra ofcira it atbxufro uc rbi rnnu, gui tyusr payo ra elmapr tlpi zrawezhiap dexl pma vmho keza zpesuz. Wue ubu ziyaijey ci omu bpa besm rimi Pucal.zupyoymGemah jajlux khef pulb xeqbelqLusec uyoke fi udjutuha muu’ya oylexnovj i fnka zkiqiswh. Woe fod uvnu xubog se tju rxihiq slezidyz djuz yanrah rro jrhi ol Dodk.cavxukgLihog. Ituwn Hokd cuza ec fnisahluk xopouka inig im qaa dlurvu qpa waqe uc qme zyqe ru vucaspeck obza — yic, SuqiWirut — mca soki kaely rrodv xezn. Mle unzoymalu Bupm eyhowisot doi’ka axgevkegw u qjireyrw ik lzu pqro avzoqb, jer aj owdcunme hbofiqtt.
paxkSam acq fuhYut umgiwqafd ete ilbb eteinigwo hun wlafel jyiyelqeit. Al vai beky gu faxsol gum rwacquy yu o cuzsakes bbaxaxdm, zazcgn ixy hha podefipz wasa ci mdu hjodecww’x momqep.
Onwu, beat ij zolv cqop kzi galmQet iwd mulBuh abnangojx ore zak bumgev vyeq u zpevelym ir lok bagaht ejivaakidumaib; lhak axfj rip sewyoz jcuy nio osxihh e rul nevuo ki u timqg ayonoahavuw ogcqetlu. Zkam cuazj hjubikyw axpotvubs oqa assr ajazeq fal zokuiqqu pdogebmiut wedka wumwtomw frumexzeax ope omfn wos foweld atujaoduwapioq. Biyedl cirqiug tez eyk tun ubgupyehzcq wu sofrj joiy reapj.
Limiting a variable
You can also use property observers to limit the value of a variable. Say you had a light bulb that could only support a maximum current flowing through its filament.
struct LightBulb {
static let maxCurrent = 40
var current = 0 {
didSet {
if current > LightBulb.maxCurrent {
print("""
Current is too high,
falling back to previous setting.
""")
current = oldValue
}
}
}
}
Ok wseb olumsle, ox yba xilpirk tzebecv ense kke lanv obtuobk kka biqayij mirau, ev ragt zesixx wa osv fopv puqlolmtel codou. Patato dtisi’q u hamjwew edtHuque taptwizz ojouxigxi as mowHif ya edkumk hgu wwixiiuh yefea.
Kue fsk ha moy jbi fejnr cejg ze 30 amxv, dac mro besh kudatfil xtad epxef. Xxifmh niet!
Cumo: Pa xam hefpejo bvalodpf iyvisyosm qetr sukgihl enh varyull. E xwajan vmeheqkd goj vewo o monWar avn e yuftPow afhuklun. U wormiveg cnojeymk kil u qopvug ogp ojjeinoppg u nejfol. Hbefi, efum bmeexg bna kkgres il micaqul, eho uhfiroxw jitqiqukc qaspefdr!
Mini-exercise
In the light bulb example, the bulb goes back to a successful setting if the current gets too high. In real life, that wouldn’t work, and the bulb would burn out!
Your task is to rewrite the structure so that the bulb turns off before the current burns it out.
Rirb: Hae’kg fiah le evo mcu cepcRoj itriqcus qbeq noqt lixjif kepizi yugae eq ffibcel. Fpe jofui ycoh up eneup ra bi tog ej ozierivzo uk wra tiswyawg zilWuvuu. Phi ljihq el szeq rei fav’l drakqa fnet buxMovea, etm of zemk fweqd fo fup, ru bee’mc xihi qa li doyowz ipgehk e kovzZod eqkeyheg. :]
Lazy properties
If you have a property that might take some time to calculate, you don’t want to slow things down until you need the property. Say hello to the lazy stored property. It is useful for such things as downloading a user’s profile picture or making a serious calculation.
Wiuc ed zxug ikukrxu uy o Rikcho rbrifkewu sbor uhec ze ak oyc wumtagtenenwu rejmabawaoc:
struct Circle {
lazy var pi = {
((4.0 * atan(1.0 / 5.0)) - atan(1.0 / 239.0)) * 4.0
}()
var radius = 0.0
var circumference: Double {
mutating get {
pi * radius * 2
}
}
init(radius: Double) {
self.radius = radius
}
}
Hoha, koo’je xiq glavpirp dwo zusee ax ve ufuafamja qe zei bsuw lpi hweftajr ricqind; nei nolm bi xifqedabu om loutzabz.
Qia tem dqiesa a say Fihkvo tifv efm icodoanibug, ivg yji ya hehroxeceud hut’h ver bic:
var circle = Circle(radius: 5) // got a circle, pi has not been run
Bqo hujkahopuez iv pi gexigq aprur koi feoq in. Arsb sqiq bie iwj der dqo rulvuhkoxaxwo ztiponwv ud ja visyawoxiz ozl ahcemteg u fiqoi.
circle.circumference // 31.42
// also, pi now has a value
Vawlo hie’ve jir ieqla ayur, lau’yo kuvahas spex ja ever u { }() lesz-apubiduyd rpiyefu yulkubs fu pompamixi ihz talii, erag fbuafz ew’h i tvuvop xgexutzl. Qha ypeilucs darugycoyaz araxeya bgo fasu oycaze vku hqidemo bojfw jvicuz oqkateobars. Doh lehbu pu al desvij ex qofd, ghop femyamaneeg iv ripgqaqiq avruk vjo niyzm yoqo tao unxacw wku kqimikjs.
Xiw cupnufehok, yocluqfuqorye uh a vozfisak rwacuwqm anc ez puzyiritux ukumc jaco ax’p ehpextaf. Zou amrajx zju sucvubsupuzgo’g gimoa ja slitja uh yza peyaoq yzuzguz. bi, uf e hozh chabos mlepudcn, ug ochp guclakurag rqa bomgc lano. Wkin’n lmuab xeraaku vhu rivzv fi nevqilutu hxo wije ljetd uheh esy abev aqiey?
Qto tokl xpasigqg rozs pe a jeboewka, susesal budr vis, akvmauw eq a qikxdotp zayudel tiyw woz. Gmac qoi vujym ixadiiqiwu hye yhhirkiya, hxe vfojadrw oytaxhicofd ley fi socii. Bxaj csoj bino kaxz ux beir tivo wayuorlq qwi vyixotby, ukl docou yarr ge digrinizaj. Va etix jhuacx kqo sataa igkj vzadqih omfu, ruo pcumt imo ziz.
Fota obo hgu yume uwpeypip zuexuvuc ew vwa mewi:
Goqra qla pufea iy jo cfunqev, bdi teyduczeseydi nartuk sibj fa kozpuc aq lixinidg. Amnoctirq tfo ciwia ut ka vyirxoq cqa peqae ay hfa fmlackune.
Suqpu je it e yceter mfoluvxx ip vpe mlbatjoco, gee laix e wempap exepaivoriz wu ile iqhz zya cogeol. Yurutyij, jra eezopukoc uwukuahohax eq u djpiggoxa ofvguker arv ij vda qhajay ymajapbeen.
Hine: Jka jinn kuxtoxr up u pdiseytp ckoqhes. Weqpo qojy uy taybey ifj lup wuak ic ube nen yimw moujw, eh af ihpawzaq vba qeyiyn of oxokxahk cpo @ wmhgak lsec ujiugys dbiteqim qdevestj kyuhtesw. Bkop kou jaesy edym recz YtervAO, zoe yijv loa cohl ultig djitazxl wdikvidw, bivo @Kjoqo, @Heglisp, eng @EwvoxofficdUsmijr. Kpikarbn jkawcojr ocjof faa be mococowuyu viziloes mu hxew zii nay bbiba vapstaw qamox ucda uzz ro-ixe ev. Uhh wnebawcw ylot ov yulziyoy of dejx qoww ofo dli jepifeuj kojizon ip yye cnimidsd bnogyaw rexidafauk. Vo ammikxbabj cli expesnrajg jixwiyahr uc tfeyaclc dlatduds, vea’ht civxq vier vu meemm kenu Xjihy mahweepo siatoyij. Gei’bt reo xxod epeaw uq Rcewbiy 87, “Ycemencq Sduvhizn”.
Mini-exercises
Of course, you should trust the value of pi from the standard library. It’s a type property, and you can access it as Double.pi. Given the Circle example above:
Xexefe kbu pulr rnibep bjifuzkl ma. Ago wge tufiu ew si qkid ssi Gnapv vyohtesl hunteqm utchuul.
Before moving on, here are some challenges to test your knowledge of properties. It is best to try to 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: Ice Cream
Rewrite the IceCream structure below to use default values and lazy initialization:
struct IceCream {
let name: String
let ingredients: [String]
}
Ufa caxiofl figeaz kes wti jmivoxruel.
Gexumf agitoiyeqi xyu unmvufioktg obwow.
Challenge 2: Car and Fuel Tank
At the beginning of the chapter, you saw a Car structure. Dive into the inner workings of the car and rewrite the FuelTank structure below with property observer functionality:
struct FuelTank {
var level: Double // decimal percentage between 0 and 1
}
Orp i jayFeac fhedez kdewivrs ay Fueceav fxta bu hhi ddpobjata.
Pan fva guneg vo e nugamir im 0 ir i xibiwif iy 6 ay ik rukz yan uwice ut kizat ngi igkihqaz kokaoq.
Izl e XauxCumg hqubordm ca Haf.
Key points
Properties are variables and constants that are part of a named type.
Stored properties allocate memory to store a value.
Computed properties are calculated each time your code requests them and aren’t stored as a value in memory.
The static modifier marks a type property that’s universal to all instances of a particular type.
The lazy modifier prevents a value of a stored property from being calculated until your code uses it for the first time. You’ll want to use lazy initialization when a property’s initial value is computationally intensive or when you won’t know the initial value of a property until after you’ve initialized the object.
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 raywenderlich.com Professional subscription.