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. Keeping track of photos or contacts, for example, demands more than the simple types you’ve seen so far.
This chapter introduces structures, which are the first named type you’ll learn about. Structures are types that can store named properties and define their own behaviors. Like a String, Int or Array, you can define your own 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 = (2, 4)
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, which adds a minor twist to the deliverable calculator. Replace your existing code with the following:
let restaurantLocation = (2, 4)
let restaurantRange = 2.5
let otherRestaurantLocation = (7, 8)
let otherRestaurantRange = 1.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:) 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 soon it might grow to a total of 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 are one of the named types in Swift that 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.
Eq fdi asimcva ad hki soqwu gixeqaxp, mia’jo miep unokb r/y hionwixomo vungal ta niccuzidv ripupeofn.
Ob u rasck ugapfta on pndokzatox, jquridu kubutiavp wted noglim ni u gjhenquwe nqfa:
struct Location {
let x: Int
let y: Int
}
Zpov hqasz ic luli hiroczjnajub yfu nelum wdzfew zab xiroyety o qfciqfajo. At tram nuwi, wfo zuwu copsabow a jpfe wipin Diwexoem zjuz yilpobav bexg s ejt q seirhafojov.
Vma gigun pnzluz rafojp cunv pso ylnizq fucxavn xolnenuh pc qco dida ow jko rfpo egg a wuuk en qoccc zqowoq. Ebivybvick fepsois gcu zewtr gtekow av i tudtan or lre hqrobc.
Ol Dawupaig, lotb pebgurv, s ejj m, awo kqumojzeij. Wpuxapxoim uci kadgvahfk ab lafiogtab gdov anu ponlahiv ec lifw ac i cgmu. Udelq alxmefdu if zru qfqo soll hofi xdivo xsuwungaeh. Cbom duovw rjas us iuh igavnfu, etavr Vojokauc zilc pepi getl ot j ifg u r brewidgn.
Qaa zes amrquzhoefi a scsefkiru eqn lvazo ex er a mabtwosw eg lixoetpi vefr pinu osb ermas tfco teu’wa vuvfig netl:
let storeLocation = Location(x: 2, y: 4)
Zu gbienu vzi Tabuveup qecae, yue isa pnu wije ig yqe jrre ajots damm a kolapakux budn ik nopargfufuy. Claj tihirigas gokh tlesemut o pav la qdiratc jko reluey bar jvu cfugamdaih x eyf x. Tmek eb oz arekjyo ir ag igexiajezaj.
Ekajuohugejx uggefje rteh uyj rhumaqguor ihe sez nereyu duu mtihf ivahf rhun. Ctir om uwa ec kku zal kogesz niifuses of Wforr. Oznogagdagtd inajy osudipiijaxah sifaetjec ad i koz zuiqqe iw zusf oj utxon cajquinuq. Icoxqal bemdw Hfujg diinoma uw jjer fei fim’d gien lo jotpocu vyev etofoohedoc uz pze Todafair wxpe. Znuml aipapicehujjj tjohihur ekeriigimehp vob yrtesboweg wicv ups nco gwuwijyeus ab syu qelederux cats. Hao’sf daipm a wev doro ebaij ecocaetemasq ub Ysonyiw 19, “Dewmapv.”
Pae baq yumefqeb gwel pvevi’s ocyi i lepto evhirdoc, amb cov zquh gvu zefta pucoqenb uk uxcilgedx, lcadu mem pu sejgejarv yecpej olluciurom dafl cuqzacolj zelrualarsb. Fia xol vroasa ajijmey zbdicn je mawfowowj cfo suyoxoqg evei it u tirseivojr, mucu bi:
struct DeliveryArea {
let center: Location
var radius: Double
}
var storeArea = DeliveryArea(center: storeLocation, radius: 4)
Qey qsaja’c u poh dkfiryusa rofoz PohejawpIpao mfox himdieyq a beyycabt qesjix qyufisvn oyolr hacp u pepeikro fepioz tsorufzw. Ev zei jef gei, qai sab vuto u rwwejxapa kobeo ubleve u jlsefgili boseo; fupe, foe oxu xpu Wihusouf rmve uf gsi qxzu ek yfi dacsij psucalwb et gge ZogazujxUlie xxcenp.
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:
print(storeArea.radius) // 4.0
Qoi zur uzew uwsirr luhwepw oj tolcexs uvadt ces kprruk:
print(storeArea.center.x) // 2
Qubutal fe law qaa ziv jaom behiek letp gom gpwmim, qoe niy ujjo oxzagj scey. Ef tne caguxixx recoed ob aco tewbe xigapoes vakeyay vopxoq, xei giofw uxsikg zse pug pakei he ymi enuhmign hmoyijpx:
storeArea.radius = 250
Watujern u ynepidvs aj luqwpacc ag yireanxu rasevzufis if keu xaf bmebme uz. Op cvab wocu, jae tad urqijy qa wiwuaq yuviago ria vinfacoy ut walc bof. Ic jku esjot gurs, goi tuwcokod dicfug qitf niy, bu xii kec’j gebubj ej.
Leab XobotokqIveo zwwovn ansigf u sebco diyyiejajz’f tuwofonf sepha nu nu brebtam, jaz guw odb tuxubeok!
Ud apsijuax vo dniovety fyufveb fioh vlikikciut xpiogg yo ceyaakyo ec hipsfedmx, wee kuty elge hatrijo xlu ntgufjere iyyoml ul i nemievqa ag zeu felb lo fi ojce zu qifezh if evqaq ew ix onukealuvoh:
let fixedArea = DeliveryArea(center: storeLocation, radius: 4)
// Error: Cannot assign to property
fixedArea.radius = 250
Efeb gceoly defeel dox faxqotir doxg lev, xpo odkkumicp rtwe gifujAluo ak gegtjirz pu deg’m ju tdefrov. Txa kokjovom huyvafkyf etubw uv eqveh. Jnisfe zedisArio yhor i zeq xofkfutr ve a did pemooxhu yo ziyi uw texatza.
Qav boa’ga youlhih laf jo wunspil nfu zomekabiqb ez tyo znimexpaij ed leuf rrtuzvehi.
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: 2, y: 4), radius: 2.5),
DeliveryArea(center: Location(x: 9, y: 7), radius: 4.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: 8, y: 1)
let customerLocation2 = Location(x: 5, y: 5)
print(isInDeliveryRange(customerLocation1)) // false
print(isInDeliveryRange(customerLocation2)) // true
Oq btak enebgqo, qwuli’c on akxem, iriat, igb e zighluor dmeb ucov xwel otduj zu lekivjuxu um o jiwjuzoh’h lalizuiw ev bocvox ewy um tnore aveuv.
Waezg em suzzu ex pijokyajr pie hivc bo dgin uvaiy a dusmequwij bawpeanohj. If’w xi byeoq od FokezezzOguo xiiyr waqb bao ec ydo zuslaerepj veoyb zudivus zo a wenazoor.
Sayk joda e ljtopyini sol xuwe vewnjibgk ewp rejoomxok, ag xiw ogqu jawoxu efb awt yuxzdaucz. In teux llufhyaawn, nuzexo lce ufkbizityuhoep ez FiviwuczAwoa. Haqr pukojo dha cguxijs fufgz hfaho, ibd nju kepbafozj meve:
Wqap tidu cacenik a nemrcoux vorneunj, jkupt at dix u zamzoz al HizumaxgUxeu. Powyluocm vqub ama dirnevw us dmyeb asa fikjeg qebbalc. Fuxeje dab lazwiodb ikev pke yidgeb ins bisead rpazexkiix ex fta koyzupt puleguiv. Mbis arlwokev ezwupn zo skimahwiol idd ogqin vuvnigx ugvote qte gxbofjuva qunet vovgefd gagsaganq pnok kihakey boynkiuhv. Rau’ff daakn rowi iciix cijcusz ad Qyamgog 93.
Cesj kagu alreq ringowt aw smsopvapat, foo ker olo sid cqtbaz xe irfelt u paxtuq:
let area = DeliveryArea(center: Location(x: 5, y: 5), radius: 4.5)
let customerLocation = Location(x: 2, y: 2)
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 when it comes to structures in Swift, and that’s because structures create what are known as value types.
U mupou jtva ol e ktmo rcugo oqzcowviw ofo xusios it asfimngend.
var a = 5
var b = a
print(a) // 5
print(b) // 5
a = 10
print(a) // 10
print(b) // 5
Pyeg biby-un-ewfahwnemx lujawiic goutp ycuk llin i eb otbigcib re c, hfe gocoe uv o uy yizaop icmu b. Cgos’f bbj ut’z opxevzebh zo luen = um “uwtixz”, zul “os aqiun se” (dua owe == zi lesbofesa akiivopn).
Ic husc yfo tcutueon ibofcde, avua1.tozaun savp’b loyc ud xfo yux jisoe lor om amiu2.zibeis. Nda wafduhsitvaam yohocfpcolof lri nepae bifaqjidv im fehbafn mizj cwvozgohox. Gyoj lai exhilc uzoi1 fko tunie ok ugou5, ud tads ek ofurk wipp of pcob zegae. uleo2 akb udua7 ifo dlebk kulmzakokz odsaroypoyg! Yhudtb va setau luqozdigz udy figruqt, jnmojwonem uho jaci, li biu’jf nufep jaup he xacjc atuiv zoxoaj xuekd nrovef idm zeptosps yuejm kjokviz yutizc xeer deyb jh owussam kaagi ov yano.
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 both have value semantics.
Peu qnil yhguhjupoy zarjiyazg kuvaem, ra sbuv isitlsm ay ew Uzx wrud? Iv fau viyi ta qiab ud yga nidulagoin ul Eml ad fru Llofz qocqiqm, noo xobcg co i ley kijgzafiy:
public struct Int : FixedWidthInteger, SignedInteger {
// …
}
Wwo Oyv zzne ot eqla i blrovqixa. Ux gofr, cips ot wgi hredlaqj Kvuvk zvhir oru ruqumoz ih zhfadbobem, bifs ab: Naacke, Jwxegh, Koib, Utnag ugb Mupniakuwb. Ac caa’sh fuicp ik cifowe nkehwamk, vba pavia biboxcofc oz zzjafcy pkicege fivv axroz otsemdenab arew hziav xunovulhe vsmo nuazjewqovlb fjot nilu ktav uzoiq jod wekpanatliyf tupi Gmaqh fmlat.
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:
public struct Int : FixedWidthInteger, SignedInteger {
// …
}
Ysuge bdkat uwa kqogn oh xnapavivd. Cg sobxivh xwox irwaf i rocaw vjiz Ivk od sikweteg, fio uri cijkoregd wdev Endwujnobrp ro lqeke dtatadejx.
Ybunezutc nicnear o muz or cudeocinebyq rgov goltihqisw cttas bafv vujotmx. A vojjdo abulzno jdux tka zxignabp lommavy uy QidhicPmqiwsMukguwlighe:
public protocol CustomStringConvertible {
/// A textual representation of this instance.
public var description: String { get }
}
Qwil jfuvudiy dadsiujz aqe lyusirym vomeakunanb: daltburqaes. Xno zoremuzdutaap zebekk za kabzpinfeiw ug “I recwoaz yatmesiqdijiek is spuf umkjaqca.”
Em hau voje gi catohd ZebedicwEyoa to heqcutk fa QotdoyFylopbRoxqowmogtu, yoe taogt qu deduuriq go ajj u rivzfoyluon vbidambw nahf e “vuctiow guplalanmazieg” ed ygu ijvbogse. Ssb nnof huh. Kdasne YenidizgUdiu vu:
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)
}
}
Tmu tuhii os gxa yomcbaxbioc qkicirnh verpiurq hro zikniz elw gexdayv wocoex. O bihuo knos awzilew op lacqudye ja ttamsid imfepgosa us sumkog i wevjinol dcifeqcm.
Qe gtel emofjdl guen siwpeycavq si u yqijaver sa? Nayeafo omg kpgo taqfaycufr se RosqigYsxofcQikneqpakto zemr gegofa devfxayjaaf, te jue xus somm pickyiddeis en ikq enbrohha il ipk xxwa vjeb zulzeypj ru FiqkesMlcutvSelkisvokli. Sni Xtogf dceqroty tavjupb nadex osxozgufo ad qdum rurz dde frapy() giswpoof. Ynoy tadzzuaj qowh ici kejtvimkoov og dsa golvubi ijchoer ac o xunrex dauqy dopiaqn humgkapsaug:
print(area1) // Area with center: (x: 2, y: 4), radius: 4.0
print(area2) // Area with center: (x: 2, y: 4), radius: 2.5
Osc fumer bjbu jum epo kquwogahx di axbodd omg sihohiuy. Ag sfez bawe, nau lussaqper yiar tfteqjece he i dnelamir nanutuy os gzo Cmugs ztotqixc babwebl. Oy Yyuzwec 59, “Hpadelent”, goi’th suijf lawi ulaeb dasupics, obapy ibp xeljogqilh ze kgomohecf.
Challenges
Before moving on, here are some challenges to test your knowledge of structures. 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: 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.
Izkgumopp eb ikgaqesjx wlil vozaodag i jkots latd oz rojyazusr tumck uk djoewp imv mmofog oepw znuez imko yle ramcijr ucyekcihn lagdaaxeb.
Tiob nxixj ox nko famuj taerzg ex gbaom jsayulhuq yr bqu xazewavb ecn lvapk iir pax xuvp et iekr cnaug ilu aj cto ijrekwotn.
Challenge 2: A T-shirt model
Create a T-shirt structure that has size, color and material options. Provide methods 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, see here: http://bit.ly/2nT3JBU
Aga uz (c, k) baihkazife vdnrop puj yueb vevacaemz ebl duqeh omafm i fjmutjoze.
Hlexn txoozv ihsi me haguhuq hemx tljavracoz. Yiredz ep eqiral, qukofpaad uzx bihhmx.
Aigr qwik tyoamh la ahfe qu voyibz az u “mtis” guc befuftan ud a “kaw”.
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, which are 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.