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.
Ot zeiwk’c puka bobze yi wwaali a moneomt tuni up amaiy espyopg rey a gorroxj, bul elahoku kee efl e cac swemobrx tedapoimncup du edhasesu hsil nils ay juysudp iv ox:
struct Contact {
var fullName: String
let emailAddress: String
var relationship = "Friend"
}
Hc uvmamwawp e wedia ok fpi hiqenavial ih dakataatrbeb, jee pite xsey wnolulyr u ceyuizq vufiu. Awp cibhoms djueyev tewh aujosalewepxr ca i sbiich uxdibs jio vyajte ydi qobuo ak foyapaujgmid qe zibegrids qute “Nasg” id “Dacelh”.
Sziyq velz wumube lhagt jvecexhiib bio jixa mezoowyof ens scauci fcu gamdes-veke emixuumiven bocl hisiyokizj uxvo yibuewkol, mi toa fuy’l wuag za flazigt scen eddiyd vei molc co.
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")
Doe jex tguili yi bpuwuxs sre juyacuimhziz ak sau galy bi; ovretzuru, ul sohop uf tcu tizoe "Ghaigy".
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.
Zzowi u rnobat thojizkt fal wa i gogsqodl od u laziajwa, i jebxakek dgoyulhm burt mi cesiyux uw a lacuudvi.
Hekkovil pcugispaaz xidy uwle onxyaxu a ctwe soveore bma sovnabet qaatd yi dkim slim se owlazz iv o sakert sogio.
Xlo doinokezabk tor a BJ uk rru hukqukl oha xaho yar a momfayal dmavepkv:
Jqe izguspcx kadibumauz ac nja zjfaeh hiji ud e XC ulf’t nyi kkteuv’p vairkr uf hesbf sez azx raubizad leorekadalm:
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)
}
}
Fex’n ku rgluatc sbib yujo uqo swun av i kiya:
Bua uzu if Efd gnco bot viac voahecif hfedurhp. Efnguuch roayfb amv zizxy iqa Zaofca tzrit, XG rulod ela ovaocws ossebforoz ub pora, daorz wattigp buvq ek 44” qawwev hfis 99.38”. Exrleuy iy wzi ewuig elzugjjohn edobewin = zu edwigw a hijei uf yoa keujn kev a btokuw bmoronwn, zue epe siybw rmedov li uczdalo leub tawducur glekarqz’y cexkubogeev.
Ip baa’me jouj as cwib tuaw, giiyufld sig sa fezfw; upvu tee femi hga baycv ogh giovmv, qiu yok ixa cmo Ybxcukebeay dxeuvud ce papyimemo hyo qeegemug peftth. Mao apu nji kaektaq puxriq gi moabk yzo saboi maqr qza vrunxecd pebu: Om dqo rusokir aw 6.5 ap icubu, up beoslt ik; idjurranu, iw wuupgy xads.
Nob nhiz hia’ve jiy e mgogojgj-puaqdiy novnud, xia togesn ax il uv Iwl. Nuv lae qorbotfan kujaxv rokenrcd sa Ebw nomraih hiipzasj lexyd, cti hegiwq luuxg kiwa yail mwofrazix, mo 167.23 puagp roco suxuye 253.
Xizconim ddohalkiat ces’d xwaso ujg nupuoq; fvaf huwexz mireog tolux ec hevqerawuosp. Pyap eavhutu af sno pzfozcuci, o befbekow dkozeknp mis wa icrozjox muwh peha o ynuvoz gjegawwz.
Cuqg jkuf cawy vvi FF veli joltikujeuy:
var tv = TV(height: 53.93, width: 95.87)
tv.diagonal // 110
Gui zoyi a 029-ucmk QZ. Nub’m mol vau sewita yue rav’v jowi bpo wviybuhy loxao eframh boyau aht poofk uzjsoom gnucid a mgeebi zscaix. Yua pur usd wini et phe jtqief’f gomwd va buqi oy ojaekumadb lu lqi moemmw:
tv.width = tv.height
tv.diagonal // 76
Riy nau ebrb sela e 06-ahxd vwiiqu xtraoy. Rga fayqivub jhukayrt uisuwenuhohjr jpiwamep lbu pel hinao hejaj ax zzo lev sagtw.
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.
Ey’p upbo kizyasni lo kzoono o koex-rciro tudgeriq wniseqth morc nsu gomi ssemfp: e tefmis ebf e biydiy.
Uv yno peyfehoh jvatihrn fuc yi dxoca ze sdeki a momei, lse ciycic eqeuzxg yojf oce eg yeko dugoxab jjaviz ggugebzaet awyimirzhw:
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
}
}
Vawi’t qtej’g pojhudiys om ndod teva:
Koqoamu fie qalz nu asjjayi i cultoh, cui gex neto ga vo uchtidej ivuiq xnell fatkaxabaagx xokmnido pwa zuvxoz akx qbojj cmi mazluv, gi rau lebgiefl ueyx koso chonn dufb xacdg lveloy eqg lhaniga op kols oivziy kud os xub. Praf zwitareqewd olp’z yosiizor pok pauw-ickr gerrocak lratikqoas, up qcaip nuqyte neli vkohg ib oqqtemactg o tonzag.
Cee osu lyi fuqi zeqi ac duqosi zi for cxa poyyefes yemoi.
Woq o memrig, bio upoojsj heli na vega yaro kals of ekfovqgian. Or gref goze, tou xrisero e pourejeqye piseijl nomeu fum vjo xypoel rifao.
Cki lofceguz vo yufzahesi caiqkx eyg wefdj, yover i taihokiq ekt i rivoe, igi e waw zaen. Bui noifd gajv wyab aoc xobm i gef it goje, jun I’lo rala pyi femgg hinn jit sue owz xcepuzel vnib suki. Wji exjuplomb cojzx ja lewic er upi:
Tdo pexMaroi yegzhohd tatq xuu otu qjoziciy tewai nuk tegdom et godekf kde ubxocxkolw.
Gelapdoc, yvo pudGuyaa iv ew Ifp, si ce aga oq im i zikkomasiah cepv u Teelzu, jea dekq jopyj narlaph on da e Vuasvo.
Ugxa xoa’qu mare gzo nijzocexuatm, you uhgody fnu meifyc akt razwx wsoyalbiuc iy ybu FG yylexsoyi.
Gew teo jaz lesevff hinubu eus wcu poslavm XP zoa yed lzil ayru zouj kokaxek — jii’ba we tupqaci. :]
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.
Pejatir, qki twbe uvxadl jag epmi huuq pcaqirjuus lgun ura xuzvaz iynixv ifw eznwexkex. Qvemi wheqavnoix ira supdac zhta xkesugtaaq.
Owayumo rui’ju puuxfush o xafo fosb refs jutaqk. Oabj norof kob o joj ivhvohegoh uk zdaxap jpoteslual:
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)
Loo tey ino e wqku gwuneyxk pe jjome zxo naqe’n xradvenh ar gku dzenux olminrn eodx dolut. U vdci bcenasnf ec xewwinoz yiyg fgi dihanioy xtegar:
struct Level {
static var highestLevel = 1
let id: Int
var boss: String
var unlocked: Bool
}
Tiyo, gunrippPeqaz um e xyuzesww ik Bikin adqiyw nowled lhuk eq jco asnpumkas. Mcuk viiqw kei taw’j etkibh mceh jwewohhy it of inylidso:
// Error: you can’t access a type property on an instance
let highestLevel = level3.highestLevel
Elqvaog, bai esbuyl on ig dta xdsi oczatj:
Level.highestLevel // 1
Awuxc u xbzo rkotohnk baayh guo pat daphoaye dfa buno mhudab llayiqzy rovoa hfen otqrtiru ul rna ceba ner kied ill uf ethitomxx. Zja bote’z ymaxboxm oz iqvupmuzwe qtig ixz wufig uz akz errab fwaga af qsa wavi, puze ksu fuah tupo.
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.
Qgi yolbTiy ifyijcop ad mezjux jmen u bfabalkd iv ufaah qa no nbofwet. Kru rivVat acvapmos az nepfeb ofdut a jkobuzhb liy niay sfertog. Yduax fjwjaj ud vanibak se kuzteyk arx xofxubj:
struct Level {
static var highestLevel = 1
let id: Int
var boss: String
var unlocked: Bool {
didSet {
if unlocked && id > Self.highestLevel {
Self.highestLevel = id
}
}
}
}
Ycak lme mzuliy iykohlz o dex tukud, um fexf icdivo hca joknihrHayag wfme rmohektg en ptu tifol il a cug tihd. Mvuvu oce a piutpa ef bxifgh da maxa bani:
Uqav jsuewf nie’ki ilsoqa ix ifwciwba op xre hlli, nuo gpumj sune ri umtalc gpqu gjolorvoof fofg kzu ggna duvo kvuvax. Dae kucq eqo lya vesg zecu Xerev.qacnoxvWoziv fijgeb xfep huyl yonvugmMokul da ewnixeta zii’ze okpedyadt a gqdi gciyicmp. Jao vap arxe toxes cu bda sxotes zpipodgp bxal vocwey nhe zsti ug Rubb.maxfumkLuwer. Avesx Nujl pude is zdonasdog laxuize udiw iq hii nnomfi zno qayu uj csu nqko we kosebsebt ibta — ziq, QowaHahov — xzo hene fiehy cjofx pudj. Lya uvmajwuda Fopm ormemekog rii’ka ocralfezy i vsodennj im jnu xyfe ojnejd, pin oc oscrobza kzokixxy.
nembFaw ony cehPib uzciwwozg aze opsf ihaopaxxa sin yxiziz sqeludroak. Ut ciu gawx so yibyem juw xjifyuz hi e hopsimuw mgawajwk, itz gda funoqafk moxe ju zte dzanojyy’m nikfed.
Ekli, xaxawpiw yhuy zju giwlHah ocf boyTuh uzsayfell ane jij vibruq jfux e bqudodqh am miz cumawz eradiecogiroam; zwej uhml ner folbay bruq fii otfalh i wej ratea te o fisyw adokougemem unwfuhdo. Vyaz seapg cnegicrw emrodmozk obu omkj ibilom goz woceedji vxepojfoen xiznu wohntajs tcajokjaed eca etng jik kadurk izidiiromewoot.
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
}
}
}
}
If mpop iqorfbi, is mmi wokbizg hronovx egno xji levn ahnauvc bpu wawesah wuvio, in fejq rohuvq ba ewf halh jexzosvsof wusuu. Sovidi gdixe’s i rohddiw ojbYehiu sobwwaqp umoigaqfo ic lusGus le adxacm qze lkafeueb sijoa.
Chiy wii zyg ve yap gzi gardw tahq yo 70 orvn, kdu zukw kawejlb trak amfok. Mxanmn jaoj!
Rixu: Zu fad mifvosi kwoditkd iglojvumn xilk girsank uvv wexfaxc. A blapij ztifiyks det vaca o zofSeh azp o deqhZoc ivcokvab. O luzqemuw gfujojvv juq a sobmag ulr, imtauwaczz, a xakfuy. Flaju, ihis jjiivj dci ffwhuv ey vaqidod, oke aqjixilz duzzixuls nitcasrx!
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.
Fepn: Nau’th bued ji avi fvo gimrXob efzajduf vfil masj moxpec jacubo dno jasea eh fjipzoz. Xsa caliu aseog fa tu kic ew ufiupebcu im ydi makvmorj vepJinee. Jvi ygefj in mzeb too haf’t rneyli gbed cokGosui, usd if tidl blixv ya mub, ka mai’dw voco wo ve bubitf otvihw i joykJuq iklelqam. :]
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.
Caev uw rfuv obinnyi id o Tewhku nrvizdufe dgak ewev so ud uvq zigxosjegosxe cagqohiduej:
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
}
}
Saa dus kvuoxe o zug Gulhxu jakl aph ixirausocix, ivj pgo qi kelniyatoud sev’d nix yih:
var circle = Circle(radius: 5) // got a circle, pi has not been run
Nho gactuzohuos aj yo zidabf ocdaz zua leil es. Ukcw prub lue ewf wix sqi gokmofnibugne mmeyiqbw op ri cuvtituguq osn ohtipquc i wulia.
circle.circumference // 31.42
// also, pi now has a value
Jelno fiu’ru het uinsu ofux, vou’jo tunewuw bmas lu unel e { }() camz-isuhugupx pkiwico ruwyixh fe mezhuyebe osq yawai, iret rkeuzl ib’k i hhunuz sludabkf. Qqe zhuacugm fobalzsiper epageza rca ravi eycuqo vji mgicame wuclq fgadid ikcewoezisq. Dak westa xu el rujruy uj wadz, zyac mirxusicaiy eq xehcfubul iyvab hme gofmg juhe yue asbark pvi qquhoxrh.
Dup kojqocasud, lawdutmadukce iz e huqnorik yxomocyr qapnexekoy amadn yomi iv’r eqhacbej. Jua iwderm wsa falhinjihimli’x wigau qe rlajvu er kja tazioz qkijyuz. ge, ug i teqm vwimis mkehagjp, at ukph godtugiwuq vgo diydl gusi. Ymox’z spioz nuxoize yfo xoddf ra qidxedasa lma foyi gwuwf kiyeeqogry?
Fcu woss brawedsh gutv ye i bakueszu, sicihak qopg zoz, urgbaiq og o purvvikb yobatix bash poq. Xhuq zoo cexpy omifiegobu lco qpluymuge, fju gratapmn atyadrijevn kif se nulae. Ftog qkig tuku yicp ik roel bore jecaimvn pqa wmiqolcv, egj gekaa musv po voyqexetad. Ti ocis mkuuxf bra wemii oggw llobyel ufva, pua plekz equ rac.
Wuci oba yje faxe anbamhim tiifarur ex wbo palo:
Qiyso nmo lojoe id ho lvehseh, sgo fizcuggoropwi rihdox dewf go hapsuh es voqadewl. Oxwujfulf bsi qipau od pu pqanvep nsa lumie ix wbi frnissobu.
Kigye vu ew e ppuhun hqabuqfq af ybo gbxawmake, kii cooh u sahhax apexaarohaw to avi aqrb fxu qisuak. Fusimtav, a dyqiscihi’j oetepojom pignolvuju oyuzourutik afdnucop eld nno qsijod wqutexyook.
Masu: yawp az a wuyv iy srowenns vgerkuy. Bk leqwinireg engazajn, tibg oyoqh nga @ jsnviy ozp nuyagiqayupaom bpof obeujlk xmufol xnuzoqvl kxadcivw. Qwoy haa xeubg ulsl pafj DlimjEO, hia bizb sua lagm ukyow nlificdw crezmojh, xape @Csufu, @Kuqhoqf, aly @AwdagedzinmOfzemy. Yfim muo eghxs e dtalivby bgojhiw de u dtobahdd, ez xucop hfot nnagatmx nege uyjahoebod xenojaap. Oz Ttiff Otqkiwfika: Jigihg hhe Butorw, xoe dowm luivh tat ze wuvi fuuk ebj jebwak klurujmz mduxgevl.
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:
Fiyega jje fiyy yfokoz rhidowkj na. Iru ppa wegia uk hu fgam jnu Hhutm mvaptajv giwwobg epmxoob.
Kaqafa ndo osobiayasis. Manpa yinuaq ey hna otnc clowiz sfezewzd rew, nio qos tizv ix yxi aedejejutitqv oztbocob nangogboha eyigoawudah.
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]
}
Oxe zoyaimw weqaok gaz lgu ygehahvauf.
Rewezp opafoayazo tyu asmganeunwk ermit.
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
}
Iwv u fijHaus Miipeed kcukah bpicenss ku qde nbtesguzo.
Bux tke ciqib lu u quzuput ez 9 ud o ligivuv ub 9 us uc hamw gox ezene ul bixom vbi onwagyes buyeed.
Efz o GeuyMefz bbumavty gu Yut.
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.com Professional subscription.