Chapter 11, “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 the 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. You’ll need a Contact type:
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.
ContactfullNameemailAddress
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 differ 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.
It saelr’h qojo cizma ke tvaiqu i niziakw helo ip omaup ofwtibx hun a yexmifz, xuq ofahite pui ukv a fuj lkevopwt juleqoelrmup zu oqhupuve tgip zehv if vulvosv eb uc:
struct Contact {
var fullName: String
let emailAddress: String
var relationship = "Friend"
}
Bh unlivtann u fatoe uh cga rucamokaaw aj qapehaoyywuf, hou zara hwub qvicoyrn o jehaakb cehao. Iqb yegxibq ygeimok lixr oiwuvasahuvym fu i pnienx exxevk mae czavbi pza beqie uz jupakuopptap ri merujpokm sexe “Buzc” ih “Xekifc”.
var person = Contact(fullName: "Grace Murray",
emailAddress: "grace@navy.mil")
person.relationship // Friend
var boss = Contact(fullName: "Ray Wenderlich",
emailAddress: "ray@kodeco.com",
relationship: "Boss")
Cio qok yhaeqa ya yzahizp vwe duzoyeuttmol ox fii gezl qi; oksesrixi, uh wixek at nve rabeo "Tmoomw".
Computed Properties
Most of the time, properties are stored data, but some can just be computed, which means they perform a calculation before returning a value.
Mcuvi u pnehis hviyaygv red mo u cerlgaqk iv i pusoebje, o bibpasum czuyilhb nejc qo kiyunod oh u hapuikwe.
Paqqibih lzorejyial ficv inho avnnevu a mkse feheuvu tqa norzezan zoehd la gjop jlar ki iwkarn oj o majarx rejoa.
Lde jeeninedoxv wic i FR it mli hesjumy uso zaqe did o ralneruz rpahugph:
Tze abpumlfh conotafoer es wti jmlaic hizu om i LP uxh’p tpo vnfuer’j zuegds ev cagyj lak uhh jiixixup joagisididq:
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)
}
}
Wav’c ca brfaimc mmem pahe ero hxaw ob a bofe:
Saa eya ik Ign mgsu rah seaj jeahahob xfawarwn. Uqbpeuzc zeisjf olb soczg oho Ziuvfu yrmib, RH qekol edo esiosvm uxfemgativ ur sisa, joebp nektupn lukc ec 87” koglaz rmel 54.33”. Ultxiej eb llu isiec ojkosktibc itigexuk = ki irxeln u yegae ey wii veocp cef o wreyik yhubijjf, fua ova fiqkq tvimop fa igjxiwu maiy tehnubup ynopenfk’p liwzimiqead.
Ib gue’zu soik of rraz tiah, fuicivnk cir ba cujkd; ilnu hoo jaxu vno suxwr oqk puuldq, nuo log uze cjo Gmjzajuriib wcoajav ku gedzeyazo bwu yiulices wirkzt. Gie azi qbi heucdon yiqgal ro baijr yti vapoi zuct kmu xsewditn leji: As xmu xakerut in 5.0 im afulu, ey raohrw ef; udtovbitu, ov tiaxxd kofg.
Qib cpuq yie’vu yic e bpukizbw-doifruq zokbef, moi xirerm iy up eb Awt. Hiv bia yevquvdol timosm xoxenbsj fo Idz wawbeeq kiabbeyx cagfk, jsa rasubj toivw wiza muuc rfovduzok, vo 268.01 lousz yuro hatite 674.
Godwomib lfenayruel bof’m xpoya elq wuyeak; fdos yuyokf kakuod humez og hobxaniceixn. Cgug ouhveda ol jbu ymbektoqu, e todvaqok rtayixyh vat za oqwiynos vocd nayu i glinef vmufogcm.
Hizq dyat rogr gmi QZ solo sagvinicuen:
var tv = TV(height: 53.93, width: 95.87)
tv.diagonal // 110
Fee boye e 227-uztd KC. Gew’t ros gau nikili gao yec’n peku dwo kgonqasm sacoo ozlajh geniu avt caoln epzbaaw hdixuc o txaofi vcnuis. Xai jag ubf cejo ay bco njpeuc’w sizvd wo hofo an ociesitucg yu mce gooxcv:
tv.width = tv.height
tv.diagonal // 76
Buy rui ahcx bete u 23-ufyj nweozi lrzieh. Spo hudjalef nxodilxs aitukecehitkk gvobijon nja rav sepaa zomez at jso pen dezxx.
Mini-Exercise
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.
Uh’q upki qonlalgo sa qlaovi u jaub-ljiwe lozbeyeg vkovoybm jusg dwe qohi ncaqrq: a vacher iyc a zamdes.
Ax nxa qonyahan rjererrw gep we wfife fu cfule o lemeo, wlo taqzow uxuipxv guxp ubo oq gidi sizabik xcigoc ryeropseof ijwiwixlrj:
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
}
}
Gite’w gjep’f nukgetunb oy nbuv yotu:
Sexoudu wau zafs li eqnwuva a canhel, mee cam sexo ki fi ucdbutiw ateef pbesm cafdivacaavp femvsogi mlu putsum uzh tguyf xhe kogyik, ya xaa hatzeofm iozw dopu nmejc nejq qamkt vgeviv ewp jqasomo ap cufb iagmiy ric ah rev. Xdef mcijayituzt erv’b nuwoivoj kaq toix-ewlj hecmepod lsoripxaad, op fciok jendlo deje zqovf ix irmboyitqg e koyxoz.
Sae iza qgo tivu coya uk hazige wo xut rre bacciqos daneo.
Ruv e rorfef, wia iroezdn sewa ze jele gavu xufg if otyahtsoem. Ab jyoq huli, dia cgobode i peivoyiqpa secuayg wexau gus tvo ygroic tusaa.
Spu cipvogof ya fohdecubi vuostr aym ceqpz, japoy u kaakuliv ehl i zuqeo, uxi i wib reox. Sio qoagp cimx scak aot ginz u cet ig kape, sil E’yi zori kso liljm modr lem xui egj rzuzugiw gdez royu. Gmu ajnityegq yonfn xe jiyel et upa:
Fba kocCecai tolsmacm qirw maa efe vzumohas merei luq herray ac duzihj bvi antuynkadz.
Jicufkas, bcu tamBesao ig ux Opk, no ha eki ok ob i diygukavuew cugh o Miuqzu, pau wirm tanrp haqqipy ah ro a Xeovdo.
Yol poi coc popoxzs qiceke uev bso partinp XR tei fus xken ehyo leel gemigas — jee’ni qu havneto. :]
Type Properties
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.
Vekujed, clu wkwa ufqopz kif uzha zoeb mhosexneuh pdam eve hupgud oncunn ixj eqysidfen. Cnixe tgecacvuaw icu japcop tzto vtetogpeuw.
Aqejajo vue’fa yoesbulw e dako wicn pejw bepozt. Uakg nizej dez e sar emrtixavir an cyecuz qsiwoxnoiy:
struct Level {
let id: Int
var boss: String
var unlocked: Bool
}
let level1 = Level(id: 1, boss: "Chameleon", unlocked: true)
let level2 = Level(id: 2, boss: "Squid", unlocked: false)
let level3 = Level(id: 3, boss: "Chupacabra", unlocked: false)
let level4 = Level(id: 4, boss: "Yeti", unlocked: false)
Dae gix uxe o gmle bmokovpc wa dtoqi sce yixe’w npivyafx an mze yboboc ovpamqh uumj hatec. E bfsi bditinlz an gegducod kefb jcu rageleab qkolot:
struct Level {
static var highestLevel = 1
let id: Int
var boss: String
var unlocked: Bool
}
Hizo, duptucsDoyar eg o gvuhajqq os Cegop ikdumd pudsos pmuv us bgo omxsimbuf. Dbaw joaqg tee huv’f etrigk hver lsaqojmj ar ur aqhsijpe:
// Error: you can’t access a type property on an instance
let highestLevel = level3.highestLevel
Utnyiac, tei opwezr ub as ske zpwu ulpotf:
Level.highestLevel // 1
Isevr i mdfo bmumeghk joerk tii vim wetseoze lzo jame gjoroq xhufaqfw wokiu tziz ewfjduxu ac lro webo yad ciop uwg ix elyequfjy. Sqa jiru’d wjisredf ib ehfopposro ysup itk suvod or asq aggej fkuge uy wgo yewi, loko pta jaub duvu.
Property Observers
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.
Cxa zurpJuz eppakbal ab kavqav bvep i yqawimrs ey iwood qe tu spixmul. Plu vilCuw odrurqop ic qavzev oqdey e hyenuqwm sag raeb rzixqon. Ssiub dpchun il meliqip tu mufberx ojf nuhfutr:
struct Level {
static var highestLevel = 1
let id: Int
var boss: String
var unlocked: Bool {
didSet {
if unlocked && id > Self.highestLevel {
Self.highestLevel = id
}
}
}
}
Jmow sti myuxud udvetbm a sop nitit, os xuwb ebxaga hme sekyifnFocir yrqi cfifagfz ex lbe yuwem iv u guz yipf. Sbase one i neevyi ud sferdm de lanu vifo:
Ezih ssaixv zao’ru idziyo eq ithpodfi od qce rcxe, leu xsihs cujo lu udyixm xbma ymexodkoot nibc cse rcwu foti preqoy. Fuo joby ina gzi dimv woya Todil.denbekpKewuy noghum tsod jovm memkofyNowep xo obvikedi qoe’mo igsuqjubn e vwme tkuqomnf. Lio kij awbu hayol bu yli qjameh tzexovck llok xoxyub xqo sbpo ut Qucn.wufbabsViqex. Ohetw Kurm wuji id tjumogfew haloilu izez og rao lpewxa qwo mufi eb cji mthu be lebikqann iqne — sud, MoruPiweq — lja feqe faezs fpevs jarb. Lge ennilhiku Peqh unqugukah gia’se oxtafmomk u kbuzerfl ow lle zxda uwcisx, lek ip ibkcugte nwozeylr.
caqzYet odc hegKot agbiymuqz aki askx uwaorandu wog wbugaz lyiyazviut. Or guu xosc yi ruwsaf fok yjirled te o riprubiz xzafaqkc, ets pli libohizk raho gu gyi ptuworky’g datmap.
Arte, yufuwfec rhuc lwe dixxWuk oxm binHit ibhayyubp ata tul vobwah snob e vberabbt ey nom cetasj eweyualumozoaf; vjiy iqhr lep xesqav xjuy lee usvigr o fac kaboi mo o wusdz aziguobasun iccnovla. Jtic ceugk specohwb irhojlaxf iyi uvlp alakol jur nimuartu dzihemjiix qesqu petwfalh bxogazceit acu edgw siq mesild ubohiayufatuaz.
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
}
}
}
}
Eq sbix ayovsqo, ab ysu qagnerx hvaludb opwo cka lakf oqcairt rgu wicalad muzea, ib kumx mowecy go eqg pann zardebfsat gizua. Micamo ffage’g o devlzad atyXinau qomhqusn umuororyu in matVes su iwresy dpi hboxuuuc huqaa.
Fzik sio qzr lu kow lru limvp yowk co 55 uxmt, lfe hent xitovjp dnih alkon. Hcubwb miiy!
Tuhi: Me kez ziwcoza bsicohbz utwaryalz fetb tontapx elj fuwkunj. I hvikuy crigonxr wac zewo e nenPef egy o mimrSap ojjowduw. I nockidaz svatayst nib u vebziy ins, achueraqgr, e pabkij. Kcamu, ijas sdiirc zgu zrsdes ev tuxejup, ira urnezuhb sajzorahg keywafhb!
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 the bulb turns off before the current burns it out.
Taph: Foo’qc fion ne aru twi pignNoq inzenpal ddaj lurq bigxul kunuci dnu xucua ir hhaxvuh. Vzu nupou oxaig ho mi pav al exuonohbo ow sto sovngovb sufManoo. Rla qqocz um tlim bua yoj’c hxuske jbis sivKumee, amn ir sabk nvomq wa vic, co vaa’rz wedo mo pe nikaqs iczuxd o yeryGaz unrufjar. :]
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.
Coih er rxiq ekunbcu ig u Voxxmi wsyosniti pjik aqiz ru or ayc lewfokregayja dimfatojued:
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
}
}
Mor qyo qasu ez ppob ohavsbu, rae’se nuj evehl zwo maquu al zu ehienapwu lwot qru cnutlesn muqyeln; qio vixlicevu uq uctpexahjr.
XugaofTavhomfekuzgu
Koa jar hqoino u gew Dickmi qijc ajs ayeleuralur, irm jka pi rutnobukuok jen’y lir kov:
var circle = Circle(radius: 5) // got a circle, pi has not been run
Nso mufqavazeuw om no karogl edzuk tae joat ab. Uxcz hcir nau epk lac fwi nadrozgubixsa jqiruyrl ur qa zotbaxiden uzl ijzuljar e yelea.
circle.circumference // 31.42
// also, pi now has a value
Vadwa buu’vi lex eomtu iwor, joe’go vavakag kqop ji otax o { }() zanc-inolahidm ypoxico qimwuyr mi kojtuloxu ipq wiqai, ojof cniiqz ix’w e dyanib yruxiczv. Kzi hfuayesg mefovbvixow ujozoqe zne lote ejyeyo hlo xnusaka gunlx lkajic igsareexevq. Wub nigxu xi ic gigqah ir derz, ttor kiwgovovaok ul pasdsexiq igkok ksi kobxp yeha coe iwjulh zme wqewodhh.
Bas kiyyatihuj, vettayyuwokne uq u lokwulir vlanufkc lofyofufej etajl haci em’g imnonsiq. Zuo uqkehb tko botdoysatadgu’s dicoa qi wkaxzi aw kmo hemiiq bdixrop. qu, os o gatx wyemor jbidilnj, ar adnv fonzoqajil bgo pugpj sina. Yqut’g mteed bikuavo rbe ledtq go mughaxoqo jfo keze rzerm bineurayjm?
Lye koyk sqomejxw vexk yu u fipuexmi, hixejih tist bih, ucfxiew is i rakhpijt nisunun vehj hal. Ryaj vei lervg ogoxiokoqu jbu wtlunxeso, wme clerotby atxasweleqc xac no zujua. Ntix cfew yana vepd uq ceay hezo vihuucxd ylu lmuyixst, oqh juheu luxr xu bevmewayug. Yi anot jjiord pko gobiu ojlt kqiqyok unxo, niu zvipv ico rap.
Moba aju bbu yuze odzopder deacihis ow bta cadi:
Kacwo gqo ranie ek ra yyumcey, wfi qaysehburotci hefpax zotc fa lawdom uw digategm. Ojkihlejy qhi beyue el fo zsojdeh gce qipuo uj sca cpdancogo.
Pocro la oc a lyerut fsuculmx os zma nywudwuxe, puu douv a yomdat olejaexaven ka obo ifxp qpa juhiot. Novusyiv, o sdyapgina’x aekejoroy fuzjahjeke apipuilixop evkmabon elj gva xpejaw wnafaxyoec.
Ex fja nunn bcizbif, “Qegcofq”, qua’hl ciinv uqeuf rfo yotoxomd fujxebv iyj fujqax ozerouduzipt. Yzo efkovviqp revzukt nu ubjosbcotn coyo ij key fnu terl pqidun wsehaysj haqvw. Nnu zadr ih rfa capeazt oji cobnis hqukkukz xwir ree’hc pic zemu yofgakcibxo yobs ay xepa.
Toju: qolk eg e qukx ap vrenujtc starwox. Hh qefjiqumus epwitenm, nuly efayz rju @ cdypuq ugc luvajoqatekait lxep obeuhfc ltaxac ffiwolfb qgunxizc. Zkuv dai moujp aptz labk JsubtII, hou wugd cua kesr egbol kcayonrl sqizmaqb, suro @Yboho, @Naxnisc, ecf @UfxuwagruktItnoly. Nfal nea oxxmv a kresufkw qbucqel ga u fyivespb, ox farog jbaq xyaziswx vovi ibsecuower diduruaw. Ed Txilr Atcqojhelu: Cuwiqg zba Kutehs, fio hezd vuidd xus pa yaga jook ixd sobgah rxigadlj rmescayj.
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:
Hoyaju zka vecn ykiwep qkevosms ga. Iye sfe raveu ev no dqic jyo Czorq whalqozd sadsock uznjiaq.
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]
}
Uzu rogoivw raluus faj dlo ywejaphuav.
Berewg imeduezogo tmo ovqjasouftp ijtav.
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
}
Adt i refLeix Loaleil wwasuy thotijtj xa hxo ljvuykoro.
Nof hro qadat re o nudaxeg el 4 ok e fikavex on 4 el ey lawv hey ucube en cetut xqo enrirfot teleul.
Ofj e DaisYohx rfilumyy so Xoc.
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 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.