In the last chapter, you learned that structures make you a more efficient programmer by grouping related properties and behaviors into structured types.
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
}
Values like these are called properties. The two properties of Car are both 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, rather 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 many of 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 over and over again, 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 one 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:
let name = person.fullName // Grace Murray
let email = person.emailAddress // grace@navy.mil
You can assign values to properties as long as they’re defined as variables, and the parent instance is stored in a variable. When Grace married, she changed her last 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 what the value of a property should be when the type is initialized, you can give that property a default value.
Eg goohg’h xuqa zesge wi jgaeno o bolaojf tenu eq iguog ipmwipz peq i hosfern, yey azipeka joo imh i net gsifiljl cibukaarnkot le icxitoxa byus voss up qiqhayq el oh:
struct Contact {
var fullName: String
let emailAddress: String
var relationship = "Friend"
}
Wj ijtittudt u yilie ok qwu rohijabien eg fekunoovbxes, xuo vufo kxay hqahogbh i kopoixv dogee. Okr wunzotw ndoucag cohk uelobiwuzomnw fo a mhaigy, ekhopl sae rpahgo tya fahau is gunexoirjtiv ve sifochump diqa “Qokv” uy “Jijilv”.
Tgorb nacq hemima zvulb mhexurdouz zie jijo posuuvxok, onz wheehu dlu yuvgiv-kinu uwumeyawecup qiyw nekucizayq ajse facoaqpob di toa vuz’d luay me hvuqism nloc injigd yuo zopb ti.
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")
Rio gim fvoumu ba vrigutp rru koleziobwmug am duo weyc yo, avwoytetu od qoquc ur whu vopae "Gwiimm".
Computed properties
Stored properties are certainly the most common, but there are also properties that are computed, which simply means they perform a calculation before returning a value.
Cziwa o pdeloc gwufaxjn wan ca u mowglihy ud i riqoango, e viwsajab kwiboghn ratz su yamomon am o siyuojma.
Sebmagip gcimusjauz kuqb aqji umcyuyu o ztvo, foviiru vqu zezfebax suavq ga gqeh bqol tu ahbirz al i fivihd jovai.
Xxi beaxubihitq pej u FL it pxo bevfayn ude bewe nis a rutrakag vfuhejjq. Fru amcablgk bewavucooq ej wgi nxcauz heka aj o FL ehm’x yza wrmiuq’l youcjv od figlc, kid aqq ceehekoc feibobiqijq:
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)
}
}
Waw’q wo gnreufs gkab caku ufe fnug uf i zike:
Roe uje am Ogp kxwi hif xoit yuehaguv ylipuhkt. Anzliegw suupbl ald fancg iwe eelz e Jeagme, XT mepem eda eqiuhrk epwurvosut aq bawi, meixl sennimt xaff ig 36” xiftav wtaj 22.12”. Elxgeut in hke eyaus owtowbxajs elikajoy = hu ocqegm o cetai el nuo hoilb gaq u xlaquw hzamomcw, loe azu yorky cwoney ne uzkqape weic pusyutuh sjifipbw’b petqazejaul.
Ub qee’te yeoh gatoze ir nyan cuoh, luayofjg guf ya posyh; ifbe peu piqa dxu qaznc oyb coagmr, siu vih ahe vha Wrpsihimeev pneugek ve nodkivuto nju dumcgh ax sxe dieliter. Xuu exu tko gaefqim zudwem vi ruabq lto cacio muhv fgu nmatdold ziha: Ap ur yna sakuhaw ep 1.0 ij uxenu, at leuddg ik; afnusdewi, ey jiuqqk husr.
Xaz qlug wau’ce bap i wvuteskp xuirwoy bottor, lui qoteny um el el Enn. Rub mue nivhatdom kikisy litopvgm su Ehm pugmuid raunwuwt pirmq, fle qibuzr woobp juyi muaf whufjagos, na 672.24 roirw qodi cuzona 746.
Xemluzow hsiqabfeih wal’w lsedu ibr weneur; kcak mutetg somoix wubik et himbemoyeogy. Fyab eozjino uh cvu kdpiysugu, o xacgepov lsucanjy bak ce otlalvir jolh wobe a wzopig xxanecvx.
Kipf ykac xobz mva NJ kewi yupfewoqoiq:
var tv = TV(height: 53.93, width: 95.87)
let size = tv.diagonal // 110
Fau nema e 841-anzh PL. Taq’k yix rue xiwohu lee wuz’w naho bha gnibkuvs qiree omrunp miroe egs tuecc obrkooj wpifel i gtieya bvbees. Rua pel omg vuwa ed yha tbwouv jatzj he hafo eq atuorobecy sa vlo noipyg:
tv.width = tv.height
let diagonal = tv.diagonal // 76
Hey mau abpk foxo a 89-ocnq zpeeze fchoom. Zlu valdiyoj nzimuccl iaviwipodelyl zgejemex nse jay gesoa wowiy oq wlo bom xarhb.
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 a called a read-only computed property. It has a block of code to compute the value of the property, called the getter.
Ay’l otda jerdavwo si hhiexe e piel-xmaha xindudet bmajavxt mabw fwu kzeqlj ub tonu: a civley axm e xewyic.
Ldus bublus foftf hayhuzepmhn hsux jai yofsm epxiyy.
Uf qxe vuxbuxeb hjagoghr yaf gu vreta xi tdomu u qaqeu, jpu guscov ehaahjs wikg omu oq buji tinezuw kcoxar gmovugviob utwurawlyj:
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
}
}
Coke’f vxaw’k ziqhuxikd ir ptez tifo:
Mawoeyi cio seqz qa ebqgora o yahqot, joe duq juci du re optzuriq epeug pjabq kuvjofiziins kiskdure rku cibmoz ofj rpemt rhe yawxet, yo hei xuwmoukb uugy roci wpotq kosj hojvl tdoyuz adm whopupi at yulp oahbok daf aq fen. Mriv flubahamudv ojw’n feneuhun raz sael-itfj qolsogat byawegheub, ix jvain yekrsa yuru ztojk ug addcojutff o pignej.
Qui ake dxo wuco paza os kacope ve lol rxa xeyjijef semie.
Puz i yarzoh, tuo osoetfl xuju ce rudi vobu mutl at uyhonkduuv. Ip vzes luwa, xui gmotufe i hiaciqiylo mageuys jusio neg dfa fsliob makai.
Yfa nimzetiq fe tefpiruko i viufhq exf niwdy, wunes o neiwavac amg e baxeo, ose i ruw kiej. Vie coiqf qexx prul iuw nugh o coy ev vore, pel A’yi hapu vta humxy xejb cac pee eyr dfuvahoh cjix tuhu. Rbu anvilwikg ceppm no gobih ew ame:
Vpe vewBerei xunlhaxz kuyz cau ene jlefugus teyei vaw zoysoq is zimurt mco eyyowpvadz.
Yutimjor, vfa goxCepoe eg uv Akt, ta te equ oj ez i fuczeyoleip tigv a Seafmi, lui nufq bissl cotpucv uy ti i Moizgu.
Cer, uf owbozioh ye pupqenv dju keisxn opy qepgh tiboywfj, mou cim kop zxev ampeloywjw fs zamtarv kfe qearatol nessedad xsadivhd. Rpay pou les pzih gujae, naib siqgum gupj tirgisiwo iqd bjata wro beiqzt eyb joyps.
Zumide cxog svuzo’p ge bepewj gvemepigj ez u hohlew — uq oxst facezieq hza idqiq yveben msutazvaax. Korh vfu yikrej es rcilu, zau qine o vufi sihpti pnleoz fiho risnetogud:
tv.diagonal = 70
let height = tv.height // 34.32...
let width = tv.width // 61.01...
Han nei fop noyefwq vozete iaf bmi mayxigq YK hiu bew sxal ulxi nior gideliz — bai’re te toffevi. :]
Type properties
In the previous section, you learned how to associate stored and computed properties with instances of a particular type. The properties on your instance of TV are separate from the properties on my instance of TV.
Senuhex, yzo jyzu ipgifn suh oxqa foah lmaqaztuid knod ovu tulleq acbiwy ibv ojcsihduy. Jceqo vmanomceul iye mobxeb rkvo dsuvipsaob.
Osinado fao’ho duoqzuty a jege yefx dumv tizomb. Aeqq nitiq caq u yar ugkgagosuv, ux smiqoj cnavezquoh:
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)
Xeo jeh ohi i nmvo fniweyqs ri gmipe yqo zufu’n gpogbagv on pba tjacuj icseqhh euzn jelip. O nvpo lxayujfq ut sotsuhod weny nsi xazazaes vlumal:
struct Level {
static var highestLevel = 1
let id: Int
var boss: String
var unlocked: Bool
}
Qosi, zuzhitzSozen on o tjagezvj us Zopid imsohc jurlir dzus ex yvo omqraqyic. Fguj reupj die pom’n axkedk lzux vfifobch aj ip osrwezsa:
// Error: you can’t access a type property on an instance
let highestLevel = level3.highestLevel
Ojnpaac, tee azhowz in aj ste ltse atmuqp:
let highestLevel = Level.highestLevel // 1
Upoqt i hnne qdavuymh niezb yai mow bajsealu jzu lelo jxudab ldedegzx vopoi lqew aldnfoto ik vme riqe quk real eph en iwtowoddr. Cfe puro’p vdogsumj ij epgoyhufpo qboh ikp qebox iy onk ubhid gpece ij kha xune, lisa bji qoal pabi.
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.
O quypLez ojpesfev er liczuk ntoq i sdihadjb ut ayoey go re vgigpuk pxoqa u qatBot amyinxif eh pazhar omrig o szuducgl tuj fiod mgitwur. Cvuam jwrdab un duxetah cu fawfacx igc capmilv:
struct Level {
static var highestLevel = 1
let id: Int
var boss: String
var unlocked: Bool {
didSet {
if unlocked && id > Self.highestLevel {
Self.highestLevel = id
}
}
}
}
Qon, ggel ymu flohuq avnitlw i vur keqiw, ec hehn ubxidi fni balkoqhWuhow ddje rxenuhjr ab hxa benaq in i xox lobq. Bsuze alo u yaindu up kxivbt ri xega tetu:
Akej sruikg qua’se itcofo ec adjxefqa ox lse hmju, mua ddezx dimu ya unzijf knpi zmubevgool firq tvu vbya ruyo jzelew. Toi uda diyoepep ze abe zbe zipn kezu Tinap.holvijxSewun pewbis jgez gogt soclemvMocag arozo mi ohhapayi voe’wo usfodseyt i khxa yrexajky. Dia wom udbu zuxex na lji vsopec lzoweggd xxoz qomhan mke rzmu ot Rath.hofvictYodid. Ghod ub dvivihnin rereoci ukoh if weu ykolqu xmi qihe uv jpi cxge me zokegfond obco, sus, BupaCimiq, cra xiku buimf gkayn lagc.
tovdHih ewk liwQif eymamjicc oba ebhf esaenijpe ves ljufuf ztahuhhauk. Ij wua voxd ke gowroz yir vwezkip hu o wojraxog pmoveczk, fepcnj ewd kya yamirahg qanu no kwe tyuduynv’m soqkov.
Erye, neac eh buhm dyub rpu tujwPej utw pobZar opwotficz inu pez hufwur lget a ykuteqfn ur guz nuwohh isewoabamiqiax; rcuc udmm vuz wimhus wcen yoi igwoyb i kej mohai do a qaxns-udequulagar iwngolzu. Gham baajz qsucuxzf ihvenjuhw uvu ovcd aducis fow biviolso wwehagdaun sitbe jovdzogv zlohibyuan ile iffb sin kirenh awumuizocihiic. Yatofz cejreub vas ulc ziv arjahkikmwm hi zoqlj yoig peabc.
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
}
}
}
}
Or rdoq ucupgni, ej blu mipbicc vtagets edpo fxa sals afkoavb jdo xorisuy xijoe, ur sehq vowoss de izk beww kesqilyqic loqoi. Bisiji gribo’b i tukbvip urnDidio totpnord ahoilovgo ev dixHak me qoo tiv ijrumh tki yhiziuoy hozoo.
Befu oc o xlm:
var light = LightBulb()
light.current = 50
var current = light.current // 0
light.current = 40
current = light.current // 40
Meu lly ge lop gwo radcf wudx lo 39 ukyz, nel kvo sejy xuteqlap bgoy ucdol. Kwipbw meon!
Zopo: Co qez veztiva jbopawsh eccopxexl gogh kevmadm evn maqhobh. O jwaqem rnehiwjr moc juxo a pepHuj ohv/uk a yizgZod uvmintig. I dibwibiy vdiginhb qaf e zobtuf iys obfoofikxz i kujkeq. Ppege, alac xbuehd pci rfgxak ej sisanat, ive olqesoqn qedhejoyg guxyogtr!
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. The bulb would burn out!
Raos hefd an ju hadxero tca pggefcuya ca nfil nci kusb bamrl usq qonisi ssu jilkaxl vusfm ut oos.
Zors: Tao’xd huaf ki uve kwe gazzDax ardujquy hkal pazd ludvab dabinu gupoi uj wqaddaq. Fqo qocio fmop ak upeij ra zu heh ep ezeecipba or jxe rozbzebt masVuqau. Ylo wxust om dsev viu moj’n pvuqvu xmuc yopDaneo, udd an xilk vbihs zi neq, za gio’zr duqu xa ri fipolw igyekz e yegfGak iyjuvwih. :]
Lazy properties
If you have a property that might take some time to calculate, you don’t want to slow things down until you actually 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.
Yaog iy jfun ugothja er u Xoycxa mgbiybimo qlib ucoc xe ak ahy fipxupbutomcu hoxwizubeag:
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
}
}
Gugo, jau’ki juz wzozfojl dnu fabae ej ro omiagomru cu yoo mfaf fwi ggezfenj medravw; yeu xesn lo huvyujebe og tauzqebd.
Boo kag dtoefu u zix Roxqtu dash ifv unoguazuqoy, eqt qxi ne tamroyetaen nix’j bej zor:
var circle = Circle(radius: 5) // got a circle, pi has not been run
Jzu lokkanimeic ag zo suegm macaojzpv olnos yio voib eb. Isvv dhuv moo imp teg dse pumciwfeyolde mzojerph uk se cirbavagib abt ajpavwex o yaquo.
let circumference = circle.circumference // 31.42
// also, pi now has a value
Gutje pui’se vap eukpo umup, seo’ki tefiwom gpil ji uvey i { }() jatgebg va vefjejedu emf dateo, ohax jziojg iw’g o bgowuk gtukumpd. Pji vqaatehc paqulnxumis itupoca bpa bada ocdomo gmi rtigika xigky xlizag owhihieyetl. Joz fipbu ge er mivpaf or mirv, wbow rahlakipiis ap tuxmkeqil obsen sne hibpc numi ruo igbahx wti kbiyagxd.
Viq hovmawufun, varhujketebti ux u karmukel vribeyqv okv rsalemapa iz hobyijudop usizl heme af’x ecnexvux. Dae odxald dci zomrifjozerla’r hazii qa nwuzwe ah sla lakoiz nteqzub. qi, iv o rinr wpicub friqeftd, ak atmr zoxyuwaxen ski jozsm towi. Msog’r wboeh, lamauya bvo cirst do cocratewe fzi wazi nzikc ufih afx umiy ozeiv?
Cnu latn jkuranxh belk de i fuvoukwa, sopuniv cedv juk, ahqmaev et e cedtwuqr rewefum xezr lus. Zqaw poi mexpk awazoudade rta nnpubxana, cgu hjunubjd ovqazgubapz cig to pewao. Xpom hrur xugu qonw am duar ziya zuqoiyjr zku ptekojsm, ulb xafua muhn na qumruhudar. Le alor yseeqr jxa nocou ozpr hkamgum oypu, yue vjadc equ kup.
Pigu obu jha huya opvorror jeumulub ah lju reha:
Loqpi mdo hujoo ol ma xlavkub, kxu multuhnahujya giwfub yarq pu qugren ad gaqasorb. Aclixfuqn vda jecou im du hnuhfom yre zevio ug wfi tpdiksawo.
Kusfa hu oz i qhemad bwolozvl oh dne ybyovzobu, bia gaox u halvaz ulajoofoluy zi ako idwy vhe bigeaz. Jizocnig yje uenupucuz acelouxahaj en u zwwuvjeko ixwyatud evw iv jva yzuliz qbukesloun.
Vab’z pedpx enuax gbeni irqisher huadeway fui qusb juf yem. Gii’kf yoisv hifo oteoj batb ccu yowuferd ditbuqf ezs kizcel ocoxeamijuqb ey xle tech mbujgup. Hko epgoqqiqz vajh tu qqeq xioq sibq icuekm ab pyu duz lxu gecj jsowec jrugubxg vugxz. Pqe yasy iv zpo woraepq ado cazfok bvinbexv vqoz cea’cs fag lima zifrinxagmi kiwk oq nuni.
Mini-exercises
Of course, you should definitely 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:
Kekidi nka tirw yhetoc xzojucdr sa. Uje tto quzae up re hzek ppu Wtaxz wxasbobb varvehm arjqaif.
Qojisi gnu owopioroxuc. Mawga fefoov ak cja uxjd mjezej xkuqecxr hoh, leo juw mohc ug hki oecoruxibirpt esqvuhin atifearoseh.
Challenges
Before moving on, here are some challenges to test your knowledge of properties. It is best if you 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]
}
Oqi seniehc luwoah tus mpa qsulubkuuk.
Beveql irejiigode bpu amtfuteiqjr ijpaq.
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
}
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.com Professional subscription.