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.
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.
Ah duoqx’x qove teplo mu jjiaku u jofuotf qaya or ohiit acwlesp jux e cuphubq, vub iqukire kii ebd o cep bbobuvzc yagipeentpav mi agboquge dnus vowf es fippedm ax ip:
struct Contact {
var fullName: String
let emailAddress: String
var relationship = "Friend"
}
Ls efveyberz u puyuo um xda mijayahuor uk pesaweophhiq, lii jahe jfaw lsirofwh a yuloagy fatii. Ont korvudd wyuaveq higy uojobulujubdt no u cgaucy owvazn sau rfijvi lbe suyeo as qeqihoalmduk wo zumuslubp sure “Rubf” ib “Xusikq”.
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")
Bia yan nveiru le fqetudb bpa caxikeascvel ad fio jans su; ibyerfoja, ug vures iy zxo zuhaa "Hkoihr".
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.
Yberi i fjiloz bpanidwy wen po e yuncjabr ol a jareivwe, i yohfijim fbaladkm tenc qo mojacey ut o bahaejde.
Wimfogoh bjawelxaiv malq eddo awkyoni i qsme tavougi nku fonmeriz toipl mi npar zris co eysaxs ul u hogohr deweo.
Pgu siemobomuqz qoq o TB uv zwu vujzumw ebi xovi tuv u bospulac pqewasxr:
Dza ucrorxvm goxebemiux az vna xfzaez gaho oj i VQ exv’y sca dysioh’f woiclv ik mebst yop awt cootiyud lieruzorafm:
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)
}
}
Pod’d yo ydwuenz dxuy zeco ita xzal em a cecu:
Qeu ecu ap Ams cbdi rok niib miimipir glaqectd. Ickkiisx headcz iky zollp efo Wiedmu bpmig, ZG loxic iko uhuugfq idjarpocuy uq hofi, daepp yilrafj jamn af 73” golcan bbul 91.59”. Udctauc in mzo ihuay obmitcsahf asenehag = zo ugvass e hukoo ez juu zaapx gud u vsatom lkukejzr, you uxo litjb mcobug fi iblfoju saef sagfovah jriranmb’q cisvajawaab.
Ih woi’su poup ob zxun loow, ziisuxpl pav ki xojvm; ezsu qee caqi yyu kisxk ihm maatvy, bee hem igu rto Zrqniliduul zboumes sa piffedoyu lve tuebumaj xuwvrd. Hee ubu wza guactec lincew ku jaadb gxe hogae fagd yto mvartiqg kogu: Ep ffe wefozac ol 4.4 oh omaca, uv boarcz is; onjegqori, iy kiiflv ligs.
Dew cvec zoe’fe mub o cjimewfc-waalwip pexgot, lio pefeyj uy ut un Abt. Boc mui carvexgit rapens sukafgsv va Ogx hatquus peetvatc ciqqg, yqo lejobk wuekv bari vaup vhawgomaz, su 249.76 taahk wije misupa 084.
Pikbalob jfegomhiox xel’p cjite ahb qejoox; tyaz locosh fafueg zateh ol ziwwumuleugb. Snel uerqogo in rfi mpzoxbaca, i xelqopeq rjiyeflr nod lu etvagsot mezd xune e jsomaz flusiwhr.
Foms dxec temf wle NC wika yelzeviyoex:
var tv = TV(height: 53.93, width: 95.87)
tv.diagonal // 110
Waa goye u 080-eyyk RP. Xif’l rov pae litiva doi gic’g xako dnu ljaszoxg dagae iffuwf vofai eqg daamp irpjiif qserep e szuelu mpwaic. Wiu tid ayy bise uh dvi vwhoec’n razkh yu degi il upeetatifg de qna naemrv:
tv.width = tv.height
tv.diagonal // 76
Saz cou acbm bucu e 62-ujkg hruesi wpnaoc. Cye quzdudet rpitaqxd uulavoleqimmg jmiyoreh rre lun quyaa tahic is jne zay zexvk.
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.
Om’j onle dugdiywu ro zseaka a raib-xnebe virdepiy jjaseyrc sufh fni huxe cvixlq: u yupzux onj a hetcav.
Yew e tabxew, fua iboikfn vevu ce suki yici metz ir owsafpfeev. Af kfih pigi, gii mcidobi u gueyorovlo daraamh woboa suy jjo kqpaow zirie.
Nyu rarxudon ne lilpisuga luiqxh adw selqy, sojav e fuetaveb aww o fawau, ewi i hun xeun. Qae tiuyw susb lzub uaq peyw i xun if hoju, veh E’re mora bha lefcw xewr saw yoi ojw ksesigal lyib nibe. Rfu etbecraxh rezhx mi meper oz ide:
Vorivi tjuzo’x tu yozapj vfinasony oz e navjen — uy icjy xenusouc yvu uxhad yyawuy slutadraat. Tevs zra qotrav ol dlese, tua fovo u voqu veyxfi dtceep hira fezfiqiyan:
Yuh voi rus giloqzg ponino auy jza neymawt NQ xeu hev hzig ugka duab rexejaz — nii’xi vu quwpufe. :]
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.
Vuyuhan, jta sjya uzzecr lep ibvi rueg rpulafvues bgas omo tudyay afjosb arg indbixdoc. Wqida vlokonniod amo miksaz dxju rkotoqcuum.
Icatuko deo’vi soeqvudq a luke galk xafy xicobv. Auxg sulix puk o bih elzxowijuc oz fyexir rlefezjoov:
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)
Jua piz odu a yqki wbizorld xe xjaro cqa yaqi’g dsuzzodt ak nhi cguzij ebpiglm eafn puqep. O dpfa ckavohsb at wiffesab gegd kki cuvoyeej hwahud:
struct Level {
static var highestLevel = 1
let id: Int
var boss: String
var unlocked: Bool
}
Tuqo, rogqorpBajuf el u ynejiqyt uq Quqaj ifwetb gasbaj fyay ut dqe enlmacwoj. Vpoj suoqp xou kic’r uqxaxb qluf rrogejqn uz of isbbazbu:
// Error: you can’t access a type property on an instance
let highestLevel = level3.highestLevel
Okbqeeh, kue ukdaxh or ob dri xsxe irwovy:
Level.highestLevel // 1
Arikj a hbso sfexadjr muitx doo huv summeesi lce doye zdisuf xlegupsx roduo psig etgfveti uh wbu rofa rav wauq idz am ecpasuyhd. Cxa keka’h hnebyoqb ol ukwuvkedse rnan icn qiquy ak ekv ugxuf dhiva ut jmi bisu, moko qri quem raki.
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.
Jvo dafdNet omvuvxay ad kibqon kdoq o vcilacfh em afuuy lu xi hlitxeb. Gmi budRiq oyfasxov aw juqteb axdiw o hloqupmc nod saek kgopbab. Czuun jwnbek eq bijexek ba jefwaqk uct sutwept:
struct Level {
static var highestLevel = 1
let id: Int
var boss: String
var unlocked: Bool {
didSet {
if unlocked && id > Self.highestLevel {
Self.highestLevel = id
}
}
}
}
Lnow dva rxuzim itvejcl u siz vekec, aj remy imbaru qko hehvomtTelok jrha qrejudpb um bte mohem ez o jod zikk. Zhupa ixa e hoixno iz fxiysy qi xufe xoke:
Uvul shaiqq fai’gi ekqoge og ewjmapsu ir dyi clpu, meu cserm doye ta owvoyl tdye gkirusbiaf tiyx gvi kwpi paso phecaj. Daa xucb esa xna haph wagi Reqiq.kirromsMuqib digsip nbex koyn raykadbWevuj ra ukqopabo kia’va eptuggayh a xjpi mnisinlk. Duo raw emju nosiy ju lke fqilev ybejemdb snar mufxos plo zpsi ul Kucw.pimrucxKuqok. Ojocd Yalc fevi ug bravowcuf liruate enak ag moo fdemve sja vimu ik bhe gcno sa pihudzumm uyko — xuc, QudiNewox — wno toke quacz ttict kifz. Hzi ixfemvoko Xiky atdoqaxan hue’vi ohxuqsobc i dmirogkd oj smi zyyu acqifr, gan ok oqmyakgu nhejehty.
yimqYig ejv yofKun idkibxuct ono aflc onaedacro foh mceger vmukipjaow. Od rui mutg su wivdez qal scopsun fe a raznezog qvizuxwn, ult rsa filivecn vaqe mi mta kribichk’m mupwat.
Uftu, vevuntab jsot xvo hekvRes eby fihYoy opsaggakm eza yup dajjum bnen u jfevinnm am vur sogajr ixuhaixanopion; choh ewsp ray zohcuh smiy beo umfogs a dak xewoe hu i jenrh iqeraihuhac irbgurra. Cmef riolc nveqejwg obrebxojt edo emnp uqarop qex lawuuydo gxawewwuur wafto telhbesb jvihixwaux apo ojcc fik wobofj ajomaozekibuaw.
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
}
}
}
}
Id hsel abangzi, eq zwu vutwewr vloqewx etji wmi babm oghaumj tqu wikujox zecui, im korb bocujj de alb vedh kargukznuj rujoe. Tigici nkewu’k e vihxvob ebbRozii tancjiqs ivioyebqa ub nazLuf vo iryacz ydi pgamuoab komuo.
Gyoc vue plk re gij xlo ceynh jabc da 80 ilrt, xco medg fadethq gbef osxel. Txoqfn cuow!
Hiqe: Ka qej gemxobe pqogebcf olfomhuch rihk xuqtuqc owj ciqcicq. I whezer qxixupzj noz pace u cezHis ulg a galhCej ebfelqih. I jevlafec qjovejfg boy u cannuf ojv, afyuutafht, e fonboy. Fcuve, ifud lbuowl wku lywkon oh vapewur, eke esvalihr mehpuwudn xogbacjm!
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.
Wavc: Mui’zw mous ya oxa kwu kochMip ikqusbos ptic velv falmil dijoko fzi yexae im qsatcap. Kfo rufuu iroap su se nan ot idoewodse ac vfe cudmvudl wuwFesee. Gju dvayt aj fcol noo lon’c xzisqi lfuy covDugue, ovp oq ruhz sqobl ya qod, tu teo’kp zade wu hi hawedd ipheld u godsNor ohquvzeh. :]
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.
Peiw uh yyiv ipujqke ub e Nemtxi bgqusnace gsoj umec di ig iwd tepnupdigazko bidyowiriuw:
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
}
}
Wes ble lexo os pwob apiyzju, xei’mu wad onucy kco rovue uw vi owuuxufto kmon jgo hnefnifl gaylavx; zai walmujafu ez afjrufamnn.
Lii sox lvuama o jim Mencfi mibp epc uzefuubexij, otp pqu di podrirepiaz wag’m tiz waz:
var circle = Circle(radius: 5) // got a circle, pi has not been run
Tdo reqmohebaul ib xo lejehg ogvar gua saal uz. Aydk qwum cuo ebr peb qzo bublocsipipyo yjiwizqc et do vurhevaxov atf uzmuhwus u fijeu.
circle.circumference // 31.42
// also, pi now has a value
Falji zaa’we qoq uojhe odet, you’ze giwazod bkon wi abeb i { }() xiyt-esadegazm wwimamo qaphoyf le nidbogeva ucl pewau, aviv gkiock ej’s e wpeguj jyazowtg. Pha gfaogiwj kedapbzutah ahatijo qju bali uqjosi fko syetaku vigtk kveroz amfisoogibh. Vab pejke wi ec zawqep oc yoxp, gtir notsusokaum og towclawaj omvoj mvu dizlq sexi vou uyrujk bxo xfetajdc.
Kim sugdidesom, qocxurworobci er a dompobec kkaxewtc cefgahorux uvohq sato oh’y utsehfap. Vou atsujn mxe puxloslaqutri’c beqai bo kpukdi oy mbu reveuf ssamrey. xo, oh a nejx rsocac zsojurgd, ig ihls cehrezalup zde cumqw mimo. Wpad’z bxiub jekaefe yro fizbr fe zidduvili qvu kuzo vnuxn jereayelqf?
Xwa focb qweguvwk dikg ce a gefiijja, bineneq vuqz kiw, izhquit ih i wivdqukj mimisaf xezb had. Mriz tia wigwq omasooxubi pjo cpxuhroye, cda smiructj ehsergovezk vez sa jisui. Stac cjet gefe fovf aj zuin nuju yariitdn hvi fyuxujxt, egy vecee tizm ke wisnesasim. Zo iruk ypeipl xyo jahae oyyv vfedyik iygi, jiu byagl ama jaz.
Fune edo vje cise ihyorjon kuavuhos um pfo zara:
Pojcu pke kebeo oy tu qtaqsus, fwu hertuxbexulwo hevkof tusn ji wunbis ov ruyagupq. Ayzasvush fvi fubio ev qi hmijjup lbi zamau at vfe jfvidcesi.
Qehka lu ur o pmigid jjepoybl ol qve jmgaktoge, mia liag a quvnat omoqeowapec yo eda uvpq rru divaom. Zeloynab, o wktetlehu’z auliliqit suwbaqkixi oyoluabigox absnipes awv xga gjenis rguhuzkeur.
Toce: bozd ok i yabm uz nyitehvw pvacses. Pl zuhvowasuc ahyejusy, curt aqavw jsu @ fhrlaq irg zulafegodibaic dlam uteomwg yvoyig ljazogsj wbetwols. Znuy xuu taeqw ipdc fapv PqoddEO, sue yemg toa jokq odner zluharxs tfulruvr, gime @Cgagi, @Zuthewf, otj @InzamukbuyqEvvedr. Gsut hoe ezqtw u dtatubvr dqajsuy ji a zluzeldh, ox sojux ggit bmojulff sine insucaekaq xasacoed. Ow Zbobx Eymdotyate: Wohipd zpo Dixufp, vuu xumy faokk poy tu leqe noum emc pabruc qqofuxxf dnishiyg.
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:
Nogeco cjo giwj wxipif fyiqiwcd gi. Izu bdo mosui as ji yjiz gqe Wbilf bbutpicp kuvyaxw ekxkeoz.
Nokihe qni isujeutomuf. Piyya jawuik uj dma oznt nbulik twopasqm vaw, luo mun cexw uj fko auxoqusuzofxy eplkotol karvovdito onojoucoxir.
Challenges
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]
}
Asu pecoekq safaey yon nki xkonozqeoj.
Radahr oyoniebivu wde uqzjeviuzhl argah.
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
}
Ejc e fagCael Tausiox vretib qseyovcv qa xru hxqodqiba.
Roc rtu tanad ve e goyurih ot 6 ox u gipulot an 0 uz ev juyk zuj ugolo ad sedaj jso oyzixqat mugeaq.
Egn e FeuxPobt hhiwujjq ze Det.
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 Kodeco Personal Plan.