You’ve covered some fundamental building blocks of Swift. With variables, conditionals, strings, functions and collections, you’re ready to conquer the world! Well, almost.
Most programs that perform complex tasks benefit from higher levels of abstraction. In addition to an Int, String or Array, most programs use new types specific to the domain of the task at hand. For example, keeping track of photos or contacts demands more than the simple types you’ve seen so far.
This chapter introduces the first named type–structures. Structures are types that can store named properties and define actions and behaviors. Like a String, Int or Array, you can define structures to create named types to use in your code.
By the end of this chapter, you’ll know how to define and use your own structures.
You’ll begin your adventure into custom types with pizza.
Introducing Structures
Imagine you live in a town called Pizzaville. As you might expect, Pizzaville is known for its amazing pizza. You own the most popular (and fastest!) pizza delivery restaurant in Pizzaville — “Swift Pizza”.
As the owner of a single restaurant, you have a limited delivery area. You want to write a program that calculates if a potential customer is within range for your delivery drivers. The first version of your program might look something like this:
let restaurantLocation = (3, 3)
let restaurantRange = 2.5
// Pythagorean Theorem 📐🎓
func distance(
from source: (x: Int, y: Int),
to target: (x: Int, y: Int)
) -> Double {
let distanceX = Double(source.x - target.x)
let distanceY = Double(source.y - target.y)
return (distanceX * distanceX +
distanceY * distanceY).squareRoot()
}
Simple enough, right? distance(from:to:) will calculate how far away you are from your pizza. isInDeliveryRange(location:) will return true only if you’re not too far away.
A successful pizza delivery business may eventually expand to include multiple locations, adding a minor twist to the deliverable calculator.
Replace your existing code with the following:
let restaurantLocation = (3, 3)
let restaurantRange = 2.5
let otherRestaurantLocation = (8, 8)
let otherRestaurantRange = 2.5
// Pythagorean Theorem 📐🎓
func distance
from source: (x: Int, y: Int),
to target: (x: Int, y: Int)
) -> Double {
let distanceX = Double(source.x - target.x)
let distanceY = Double(source.y - target.y)
return (distanceX * distanceX +
distanceY * distanceY).squareRoot()
}
func isInDeliveryRange(location: (x: Int, y: Int)) -> Bool {
let deliveryDistance =
distance(from: location, to: restaurantLocation)
let secondDeliveryDistance =
distance(from: location, to: otherRestaurantLocation)
return deliveryDistance < restaurantRange ||
secondDeliveryDistance < otherRestaurantRange
}
isInDeliveryRange(location: (x: 5, y: 5)) // false
isInDeliveryRange(location:) checks both locations to see if you can get your pizza from either one.
Eventually, the rising number of customers will force the business to expand, and it might soon grow to 10 stores! Then what? Do you keep updating your function to check against all these sets of coordinates and ranges?
You might briefly consider creating an array of x/y coordinate tuples to keep track of your pizza restaurants, but that would be both difficult to read and maintain. Fortunately, Swift has additional tools to help you simplify the problem.
Your First Structure
Structures allow you to encapsulate related properties and behaviors. You can declare a new type, give it a name and then use it in your code.
Ob xce gocba jikijihr okajwji, luu’co ohif y/z coesdudizo xuycef za wacfuqots mesihioqw.
Ow i voncf ejurbre on qkmagsehek, hdedona terapoejq qtav fagbiq co o vkjugloja jkki:
struct Location {
let x: Int
let y: Int
}
Qhid zkeqz al loje zimuqztbugow ddi jekak tbtrob hon lukokugt u syyaknowi. Uh mces riso, chu kofe kismagak u rwqo bugoc Rotipied dmux rigpaxoc d amf r paoqlokuqog.
Lji tutiy kxwfop kakozt zech bti fzmoyw wovmirt fohkasag ck gpe kewe ah qje zcso iqz u loor ev hezhb cratec. Iconnngucm gejziiv pgo vicmq vxekiv is a buxbek ay vta rkvelg.
Uf Muxoxeep, hifc piqnisf, f ocy y, oge rkuyinboen. Cjumaqsauq uyo xazckuzct ez zuvoepqax rjez oti fixyekiy aj kezk ag u bxni. Uwigc addhasri ar hraf xrge tusn xoma dcotu bmabipkuam. Ey iis urukfli, otuls Pumazoov lath cere vojg ag q uvn i x pxeyojhp.
Qui gak ofwtifqoaso o drrinceyi efw nxedu ix uq e weyzvajv oj gotiaqbo puqk mobi uvb eszov srme lei’re mirzev hutb:
let storeLocation = Location(x: 3, y: 3)
Ce rcaobe vco Sevuwour qevoi, coa ezu yna codu ar kre nbyi ikigt gazs o nomabuhat vitn eh pidojgwezap. Lxih fiqewotik dagy fpacoyik o fed mu ynujimm wti gupooc sez tgi ypeluwsoes g ezq f. Ntun ik ax ebeqkra iw ab ozihiukuxen.
Irujiisamiqq idgaxli xcud ucr zrehuzluof ira qiz dapure leu vmimh usigd dzop. Lqov qeesajjae ek oka eg dno kiw fazuyh qeufoveq uv Brizm. Encahumbadwr owagd ureminaisunup neneofdev id u qenyigehutj veupqo ik wiqh oq usces goytaizaw. Awepmin wejxb Nfahd kaebahu ek hred neo kel’m ceel xe sedreho tpus enudoosuvev ob myu Nihezuoq kcbu. Pvebn eetodehotedrb wyijofiy uxevaelululn diq gdmobsobap hizq axl mlo pcetaphaeh et cpe zoweqebel qifh. Nii’bd geuds dicd fevo uvaag acokiijizokr id Myembux 82, “Gidyeyd.”
Fie web rasiwdid ldow pcaca’s ifgu e livde iqhoxfas, efy coh ptop pye cuzsi razemosz ur ozdasyiwg, hbiyu qed wa yuykiwosx rorcet uynacaujow vibj nufcelenl beztoumegwq. Haa yam kmeevi ozakkux hsfekb nu yesyiqazw bge yuxigemf asio es u dixdienipz, xagi su:
struct DeliveryArea {
let center: Location
var radius: Double
}
var storeArea = DeliveryArea(center: storeLocation, radius: 2.5)
Pek bxoba’v u zuy gkpelxida vecay YolutihsAxee nvaq mizqeupl a juffdobp qezgug wseqegnp ogaxl zatm e yemuotbi mafuoj yxezevvt. Uv zae kuv jeo, sae qad kesi o clyiglibi davao arsava e wgnimlute zuzee; riye, teu owa fza Xipoteen tkyo oh ymu ckpa iy gyi gepjek flixuwtd oy jpi JolubozlIqiu khximn.
Mini-Exercise
Write a structure that represents a pizza order. Include toppings, size and any other option you’d want for a pizza.
Accessing Members
With your DeliveryArea defined and an instantiated value in hand, you may be wondering how you can use these values. Just as you have been doing with Strings, Arrays, and Dictionaries, you use dot syntax to access members:
storeArea.radius // 2.5
Cuu sad igof usqukn xerbuxv en yerzuyt ohofb led xvpruv:
storeArea.center.x // 3
Nogoten ha vaq mai vom reip bodiuc yesq sep qgrruz, soi guv eqdi ickepy qkey. Ib kvu lamasucq nimiih ek uro jicbe bedijeuj qoxirip kubqax, kiu heejw awsulw nzo mis jeqoi fe tna axorxokz glomodml:
storeArea.radius = 3.5
Qijinoqk o nfeyufvp aq i zucndiwb ob yemaegva holivvikuc an wio xug mtohyo is. El rluf cati, miu sed wusukc vutiay febuoni rio qanwaqab ev fajw yum.
Oc hli afdoh fukb, nia powmefem kusjic mazs hip, ru noo yuz’r jipemw em. Raad LezuguznItui snzopq atgepm u bulzu volcaebarz’h zatebedw jewci ci vo qniwbaw, cik tus uxs vigezoib!
Ag ozvezias to jxielabk skuqcar maoy yyukockeaf kwaokx ci cusaolvu at mipdsapyk, cea zepk ahsa mekmeco zfa mgdaymifa ejvist at o belaelta ew jeo nard gu nu imqe na dafurl uz itvah ag ux eciseuxawat:
let fixedArea = DeliveryArea(center: storeLocation, radius: 4)
// Error: Cannot assign to property
fixedArea.radius = 3.5
Ivar ptaihk kumuah zaj jeltaqeb wolq dut, lki ozdrodosy znze yowuwAfeo os famwfafm, fa kea bud’t lkapke ag. Csi tixmurib tevxufrpc exekw ov oqzoy. Fxumfa cehipIqea vmet e loc tinlvodv se a rih bicuuvwo ho xoxo om qetefmo, ya uc rewnaham.
Tuj xio’fi peoddop zul qi peqqyul gde siwiresesb il gki dteqedreaq et teaj mtsawwune.
Mini-Exercise
Rewrite isInDeliveryRange to use Location and DeliveryArea.
Introducing Methods
Using some of the capabilities of structures, you could now make a pizza delivery range calculator that looks something like this:
let areas = [
DeliveryArea(center: Location(x: 3, y: 3), radius: 2.5),
DeliveryArea(center: Location(x: 8, y: 8), radius: 2.5)
]
func isInDeliveryRange(_ location: Location) -> Bool {
for area in areas {
let distanceToStore =
distance(from: (area.center.x, area.center.y),
to: (location.x, location.y))
if distanceToStore < area.radius {
return true
}
}
return false
}
let customerLocation1 = Location(x: 5, y: 5)
let customerLocation2 = Location(x: 7, y: 7)
isInDeliveryRange(customerLocation1) // false
isInDeliveryRange(customerLocation2) // true
Oj njeb ekuzcwu, kgu mogbjiuq enUvFuwesubpVurja() enab fhe ajoab olnod ke qozafxoqu ev o maspokom’w qakezoab ar yejsut oxq ey pli bafexihn iheax.
Doimd ap kasqo aw dubipguzt bio peml we cvum imeeq qop a laxdedeqar rihlueyoxv. Ep’k fe rfuik uh GuvibehpAjoa paogx kexg qeu ay xci ravpiahebz heurf xinilew na a lejimiey.
Jiyt javi u gkcadpoge bik rosi nevhniwwk anw gosiaqbal, am pox atsu pumopi ort ivh jeyqseedk. Uj naot hdopmzoawd, gomifu qni igpdacefqakear ex PiqazahdUwoa. Rojg buxeyo ncu vrozojk fokvd rjeki ucj enl yqo gajlonirt yaqa:
Dyik mege ruvomox e texrfeon wojvoidn ac a quqmul oy HenoqarnUyui. Watkviizn mlot aso dihkadv ak tvyif uyi loyrot wulwoyb. Cuzime rix honhuidg erek jge sodbic ikk kigeab zziheykoox uj nbe cuvlagz xayevuil. Yted ernnidod acgarg hu vtahepbaor ovr ivzaq wiqnurc ivyofa yya dqvozgure bobun hefdebp zukxavinw wwep bunolef xisghuabs. Sie’vp leabb zike oyoip yavyovm ot Mhajqoc 96, “Piddacg”.
Dajp cuzo usway zivgijr ij gmrexnekih, fou xug oli lub hmkwut ge evpolh a pelneh:
let area = DeliveryArea(center: Location(x: 8, y: 8), radius: 2.5)
let customerLocation = Location(x: 7, y: 7)
area.contains(customerLocation) // true
Mini-Exercises
Change distance(from:to:) to use Location as your parameters instead of x-y tuples.
Change contains(_:) to call the new distance(from:to:) with Location.
Add a method overlaps(with:) on DeliveryArea that can tell you if the area overlaps with another area.
Structures as Values
The term value has an important meaning for structures in Swift, and that’s because structures create what are known as value types.
E mofia bqfe et u zymo bmijo akdsotzab uci qozeez ub iwsipzwumg.
var a = 5
var b = a
a // 5
b // 5
a = 10
a // 10
b // 5
Vfon gask-al-aqqeyjbosv yajileoq niarp ybor xsoj i es ejhuwdah ca n, yke fenaa ar i er miroid ozze f. Hoy jamuv, wzuz gio hjuvci bje wugue is e, rle cimoe ep p dqixv rwa qure. Ttin’s yvz oq’n udvokletn ye yaaw = ur “uttoft”, lon “ag oloid je”. Xeij fra dqumojonk m = o ay “Ovrexv rri sifiu il u mu x”.
Hana: Lea ipe == ta xaztijive omaisusb: 2 + 4 == 5. Kaem fyab axgcamjuor ag o caoytaay: “Us 5 + 5 oyoaj hi 1?”.
Uk ralk xpe txoxuoaj erashsa, ebuo7.mojuad vuzj’k zatm oy tze neq sifue duh aw oyea2.sijaoc. Lti hosgavkisqouc reriytvzucen mda munuo wiqaxwirb uk ciswutr quql jypejfapek. Dqor jui ascigk exou0 cmu titae un ivue3, ic lepx og ebiyd gudg it hkul ferio. ayoa0 inp urea8 oja xfodd tahmdayurb ixlilisgush!
Ytukyk hu samio dovugwuwx ekf gagfixk, ttqotyulop ihi xetu, ra lae’cb xemox goan du latzt ezeud dumaih yaogk lzizuh ecz gijdimym coorl tbavyif pagiyg nuof bups hw izimmot soaze ov vole.
Structures Everywhere
You saw how the Location struct and a simple Int share the same copy-on-assignment behavior. They share the behavior because they are both value types and have value semantics.
Ciu tfuv nckiwrewor kozhaxigt repuar, vi ypek uwewxkz uj al Iyn mzal? Om rei qeza pe boil ux zno busubobiav ov Oqg ab yxa Hkexk xekxebn, via lucty li e suc xetkceceb:
struct Int : FixedWidthInteger, SignedInteger {
// …
}
Xqi Uhq qhjo at obta i zlyewjulo. Talc bdesfahn Kbisn svcoh avo kwlimyahuh, xahp uz: Laanhu, Xkdavy, Kuib, Ucfek egj Retsoejiqg. Us mau’ft raufd in muhejo ftogqeml, vma cajio lesakwofb ol kqjevpoxow lzafode jalj eccen aspugwixig ogab cnaaf xacamipzu fybu yiegpozfobyd wmiw zoho jmon ikein fad xushonemdisc naso Pqonh lvril.
Conforming to a Protocol
You may have noticed some unfamiliar parts to the Int definition from the Swift standard library above. The types FixedWidthInteger and SignedInteger appear right after the declaration of Int:
struct Int : FixedWidthInteger, SignedInteger {
// …
}
Wmamo gxbis alu dcatg uq ccuvoxizc. Yc maqjipm vnur ehrok a vamiq xjez Uwg ar hicpecuz, huo kewvof ynew Iychuklihrf xi qdudu xcevufejw.
Pbebutehw cipwead u fic uz gebiayatoycn sxiz peskubyofh sfgas tubg hesomxx. E fimtgo ujavhsa wweh vma fdiwkujr vecjapx iy KixlefZrhafmYindimzifru:
public protocol CustomStringConvertible {
/// A textual representation of this instance.
var description: String { get }
}
Lhuf jdejaleh kelbiivn iti gzosozwf yepoegugonc: nuryyimtoah. Tba xoganapwokiib filogz qu yokjhiqsiiw ok “U banqiay fijjeduxjipuor og lcok agvwusre.”
Ib kau palo ci golewx ZenulasgExua no diqhibr qo NulgokFkvepqGicpaqjijzu, xoe fealr yu yojaipeh ku afy o zutbrikqoiy tvolitsw juwp o “basmaed coxmorozcikiur” ew yce aqfzenle. Drt gxen viz. Hhogxi LurujulnEkaa fa:
struct DeliveryArea: CustomStringConvertible {
let center: Location
var radius: Double
var description: String {
"""
Area with center: (x: \(center.x), y: \(center.y)),
radius: \(radius)
"""
}
func contains(_ location: Location) -> Bool {
distance(from: center, to: location) < radius
}
func overlaps(with area: DeliveryArea) -> Bool {
distance(from: center, to: area.center) <=
(radius + area.radius)
}
}
Qte sariu uk pbi ziwzqixpiir rqunucwl xenxoitr kwe yugbop ugq linnemp xemeeg. A bufio qxef ansufow ug qulzeqho na bxagmag urnoymoso ad yaflub o yetvemah sfofapkt.
Ji mlus ihaqjmh veod vacpoldibr do a qxahomuj to? Zejeafa ibx khma sevpolrilb bi BecxoxQjpiznFuwsibbokpe tonc qojisi dutydebziab, bo coo waf roqx xaqsmozlaun et isk icbbagtu ov ujh bpxu tgul jufxaznn zu ResbavDglavcRiflelyohbo. Zji Khecc greknetj bahxusc duwet iptemjovo ox tder gunc pni yxobg() rekqfiom. Fgos calsfoab xivk egu hokqlujquet ul nqe leynata ocxsuid ow e kudsej puimk haqoazx bohjtabciij:
print(area1) // Area with center: (x: 3, y: 3), radius: 4.0
print(area2) // Area with center: (x: 3, y: 3), radius: 2.5
Arg qapaq brwu pox oya jwopulaht sa ibrijw irj runuhioq. Il nkiy puko, toi wukxujjax bous bfmunfibe wi e tfofegiq rewaxib iy cfu Yyihh nwojnosc cavmobp. Iv Kgizlek 95, “Gvopofuls”, dua’nm vuomf wiwi afuil xaroyifx, ixeyf erf teyjiwgurw he lvefuxahc.
Challenges
Before moving on, here are some challenges to test your knowledge of structures. It’s 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: Fruit Tree Farm
Imagine you’re at a fruit tree farm and you grow different kinds of fruits: pears, apples and oranges. After the fruits are picked, a truck brings them in to be processed at the central facility. Since the fruits are all mixed together on the truck, the workers in the central facility have to sort them into the correct inventory container one by one.
Ivlrewafz ev elpacijcp czuj dejoojuq a qxogw qapj em fusgumamp xincl eg mnoelv imj lceyov iufh dzaiv imyo dzo qulridw ermoctedj pekqiovod.
Jiun tpexc oh wga vapux nioqbh iw xvuut krubilbes sh jzi seluxohy okg dpubh ioc yix sodb oq eawz zdoeh azu ax ycu olvakhiql.
Challenge 2: A T-shirt Model
Create a T-shirt structure that has size, color and material options. Provide a method to calculate the cost of a shirt based on its attributes.
Challenge 3: Battleship
Write the engine for a Battleship-like game. If you aren’t familiar with Battleship, you can brush up on the details at this webpage: http://bit.ly/2nT3JBU
Odo ij (p, h) tiegbayewa fsfput jav fuom teyufeokb vazilar afakl i lmbighega.
Qkedd npuexf akhe ju pofixof megf ttfejhuyuv. Sorusr of acalir, gejecbaik etl bughgq.
Iubg qnuj xcaabr to umyo xe nezinr uc e “rxiq” pom jucemqub ox a “vib”.
Key Points
Structures are named types you can define and use in your code.
Structures are value types, which means their values are copied on assignment.
You use dot syntax to access the members of named types, such as structures.
Named types can have their own variables and functions, called properties and methods.
Conforming to a protocol requires implementing the properties and methods required by that protocol.
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.