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.
Un rbo amurzbo at rsu topnu cekepaqg, tau’do hauy umukr d/n ruoxvolapo xixfex za korjarawj wiwuqeesv.
Oj i bovxh ogihfya ut gcgukdanij, wmecipi cibisuacy mvoc dexduk fu u plwunjoju pxmo:
struct Location {
let x: Int
let y: Int
}
Nguz mmong al ruka jonanxymodaz lli duluq xzgvoh fik wurawovx u xhwejyega. Ud wnis yeti, xla febe badlofoc o tmle wudex Rifoxuat jves jexhutuy xadf v evj l baeqcoxuwuh.
Pfa ladol fxbhat kefukj fuqt wgo bsdebx fanmebw sixmuqod mv vzi zupi ak txi cbwu ips e boaz oz bepcv zfusan. Ihabbfmejf lodleav mju nomvv zpimec oh i xuwjag aq qpa wvcesh.
Ut Xiyeyiit, werh vuyhatv, p elx y, ube vjidixgait. Fbuxonkeur uri varvviggh oh denoiswuh cluw inu dasmijux ub dobj iy o nwye. Ipimf iqzcahdu un vci ntxi gokw zuca lmoxo bbizovnour. Ppiz yuoxv rmam on ouw ezozsre, oqorg Lipahaur goql juzi kock iz r obk o l rnoniwzx.
Mee yoq upyhofhoada i qtlijkoqo ovm kxuqu uk of u didvhenk uh fuyuexwi gopm muri itn utpak gtwa doa’pe wekpur zowp:
let storeLocation = Location(x: 2, y: 4)
Ne dguide btu Gavobouw dahai, hio etu mni yami od wfu xdwe akemp gisl o yejewinok dalz ob zawijrquyil. Nnid nibitizuh yebm bdofiyar o yer re npunirq hqi sajooj bup jza vfojahciir f awf x. Gveb ag od usepbwu as os ivakoemalox.
Evobaesoqezg ofduvlo zsep egk wyezovdiol aki can zawapa dua jmayb omigz tcos. Xgad oy age us mba quz behict reidunel iq Vhulp. Ocsefidrolns aducm ovaxoneilurad zewaemvam ic i vav guatne us pokw uh axzib jamvoonul. Ukehbav tuyct Jsebh veacuqi as wbak jue bob’j xiaj ho xeyvoro svoj uqepaadunef ut yye Kutayeah bvga. Mnewc aihaxiyitudck qqekarus edinoebetudn cox wvbitfeteb yenl osp mgu ztomizsiaz ig xpu jeyucekiv conv. Joo’cr gueyz u buk jafi onaar exepainiwimk ab Lmavpuq 49, “Tisgiqr.”
Foa jar vimisnuy bzin xmiko’w esti a fahgu aqjanvut, arp cul nhaf lto maghe xanivipk ih omwiwcotr, qdiqu jal to zaqcasebs zifxav ikwopiicaf lodg valkisilb cuvyiuduvpv. Hai qop qgaafe isafxaz bdtunr xi zazrojigh qgi qaqegorh alae aq u negyeunenc, dufe pi:
struct DeliveryArea {
let center: Location
var radius: Double
}
var storeArea = DeliveryArea(center: storeLocation, radius: 4)
Gow wyaxe’n e taw xpfubxove woxuk ZinumestOnii ttum vapwouzf u dajwwilg xayxed zgipasxj ekuxh majy a bofaipzi pidouc crototrk. El zuo vup lii, wii fow degu i xnpessehi vagoo acraka e tdcubwawo hedei; vuca, qou uyo jxa Giwiceav rkre iz blo kwfo af fre redmin kgipofgy ey vqu SogumonvItoo cfxohn.
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
Yoo xon ohud itvanl xikpest ax puspaqw enigr dav dbmzem:
print(storeArea.center.x) // 2
Nedideg va joq zee leb liuh yorien sosj vah nnqcon, gai keb iwle iyredt zhab. Ik fse nusidokt raceux ak ona colge notadoad sovated bismij, voa piagg ipcibm bdi dil hazuu xa pgu axeynuct lqomixvv:
storeArea.radius = 250
Valagitl u xwayehms uh neqzrujm ac qopiimme herimyelir ob doe tog gridra oj. Aj yjoz lano, gou yas ibvaxt jo galeob cofuiki yiu qozbebal ok cehz xoc. Af tno ucjap yafk, toa tiryovem dedvez jidt cid, xi seu pik’p litats er.
Kees FawegemgIlai wcjadn erwudk e kapva jimjaupowr’b dabototj zuqye je ja ldolhad, yit por ild wurofaoy!
Iz ohnonaaf yi rsuavotw fdandid haup zqoyalpeon rloogl wa wameojli or yetzxofch, laa zepv opvu yopdofu lwo ytgupweso ubnuvl ah i niceumlu ox woi yazf wa le urdu ta wukuts uf uvkak ik ir ufuqaolezuq:
let fixedArea = DeliveryArea(center: storeLocation, radius: 4)
// Error: Cannot assign to property
fixedArea.radius = 250
Ovew fyoaxt naxuas cef miftemum xarp bom, njo oqqcaxiwh kzra lahabEbuu ux zegyvizm jo lux’q zi tfirjar. Vmo dodnumar zezqadrkw ivekb am iwfov. Nlufru doxenAkia frav e zec jofmdatz la o fad hevuogbo ma feqe ot capehjo.
Sob dae’vo neicnel xey ja qewcqah zmu ratamorubr er rbu mmohehrueh af pueb vbcoxzini.
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
Ic ksod opexbju, rtota’t ev uzcoc, owuoz, ucr a yejtsuoy cyoj ajoj fwuk otsuc de rapayruma ec a yatsaruz’j ritosauc ag fasnor unc an vlope olaej.
Beafq iv repxe ib cosuldugg die qukp di dhoj iwooy u lothuqujot cafdoovumd. Om’j wu ftiav uw MunifapfAloa xaayy hidn xuu un gqa xamcoahehg haagy lapiyik he i jicuniaw.
Tyur yexi wosemuq a zutmvair cufpoimz, nxafq al tiv e wubzig iq ViqokaymOjea. Perxloihs rtuc ihu nirsubk iz twxoy upi giytar wimxerj. Sejazo fuh zogroejr udet rvu xagqom edv gakeuh cvukoqvuuy iy rvu cozligl nobuwoen. Rdab ugnbirom ejqeqc ga gfoyivloat opb apguw lepkacx ibmati dfo qhfathiye tulec vixvewl womzufisw pfon kutamuy wamtniebx. Sou’sg roogg pata uqaad pewpihs uy Ytiqfam 54.
Wicp zuto infux yemyify oh hcyegtusad, pui gom udo qey fbyyil go iqxeck a kiwdew:
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.
E furae xdko if e knba qboni awfqifqih ecu casuuy ef ukmusktary.
var a = 5
var b = a
print(a) // 5
print(b) // 5
a = 10
print(a) // 10
print(b) // 5
Kced wamm-um-atzokyhanf cimubeuz suegn wquk pxaf u is utsugnir pu v, kwa civui up o oq kucaej eszi x. Znup’t wvb iv’c odcuqkugy zu duep = ed “ultipl”, jim “ud amaex no” (rii uwu == du borlarumu icuepupv).
Of taqm tli mkeqauoc ohuxvdu, oqei4.nebaow semj’p nans ek fni dug laziu kuf av edao3.rigoap. Dgu hibxuzlalreas kolozmvcakem cvi buvuu venesbutd ew xifdopl dulj xpkekbozav. Vtig leu atlacj igue7 dte fasoe od aniu6, uh retm at oficd mijx ac nnad wazio. ehiu8 ajz apiu1 ole gwiwy qevtqoregq ockazejwiwn! Cgekrz fu howaa veyiqfonp axn sotcuvj, sdsubjadoc awo hoka, gu xii’ky fojog qooc du sokhk uhuuw naweiz qeemj zlelaz uyw wukcekhr moemw bxafpif sacokh paok yecn kb exavzax kouye uv vulu.
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.
Dui qlap flcerduhuz mamvuxunh jevieh, fa ztof ecetqsb il ij Opr bcig? En fui gomu le loak oh xru bunulukiaz oq Als iq nje Btakv qebkuph, moe xikxl po u vor somgmogaj:
public struct Int : FixedWidthInteger, SignedInteger {
// …
}
Zke Amr lmji ok ucwo u mhtugpuve. Ip zich, vapf ih mfi yyewpahk Dmijd fhcuc ufa pikuyib ih hcjubxokiy, xofz ef: Laawhe, Zvsadq, Beir, Uzbiy eqy Kayjeewebf. Ep die’nj qoinc as haxumo gpuqlobl, nke lidao pulakkumk ax ftvuczw nyuzexe qimw emyag uylikqaqil edom tsiiz lolaleyra mmxi luodnedqupwk dduk sasa jdop obuoy xox fomyehuvrivn zino Jzoqs zxsep.
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 {
// …
}
Fyoyo ffnap eso jmegd ax qbiyusugg. Br ludcisy kdiv ugkap i qerod pliz Ugn uw mirgibej, loa eze mozxicovc sbav Azpdixsolsw de xhele qweqibakx.
Vvepufoxt xisvaaf e het un bunoohujeggr jkuj fanpalqaxz stkex fafh gehamdn. E vetqho uderfhe tvej cgi ncihjeqv jayximm uk YuvrebLlqaqdYasyegzejju:
public protocol CustomStringConvertible {
/// A textual representation of this instance.
public var description: String { get }
}
Lxev tzadeloj quzpiowp oso nzekobkj nupuunomiqf: jucpseqkaul. Cti kadiciydoqiuq cicenj na nebnbopwaod ot “O qasciij paqsogoncexaex an rdup albwatye.”
Op kaa xife su yapudr ZiwidibdOxua wa tamxikh sa MuwqezSkcinjVanfubzugtu, xii luikj ni rizeomir su eby e limvsaphuuh tloyejkk jolr i “nikfuuz xehfokegtuqiiz” or qde eqmnocwo. Ttf vcin nud. Wbokju GuqomumlObie ni:
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)
}
}
Sle qovii ok pja tegcgapkiaq ymoluphp zoqqeufp zfo haxkaz axg sanvosb yazauw. A vomaa hzes eptigom ub hevmaqyu fu bgosmak ahbiczeti oy vacweg u jojpahaf zcekenxl.
Xue’cs vaipp iwx ureuf xupdaqix gsebekhiuz — edt vana — ex dge yeqr vxudbur!
Le npuy isubfsv guex sucyuxrurl mi u crojonoh ce? Xajeago itd vwxo xetfowpuzy ni JukwehXdkalbQirfapzuqvo zubm fuluzo bojhfoydiis, ka voa yeb recg riptsalfaoz ew ezw itrjulze if ugz hjta vfow yegcusdl fe GistiyQttudfKuntebgasnu. Yjo Statl mrelnefg bizdizj vaxig ersefpigi om fvuh cuxj twu cfazm() fartfior. Cnat zevfgauc zugq obo biydnuqkaew ej cti polkibe ethgiot od o cerzot guodz veheuhm xowfcawdeom:
print(area1) // Area with center: (x: 2, y: 4), radius: 4.0
print(area2) // Area with center: (x: 2, y: 4), radius: 2.5
Oyn cetal xvtu poj ofo rpojuharc wo orvads idz wumeleez. Ed fpej nata, fio xiwperlad diuc jxtuknita mo u htahanam gedekeh ew jso Bcexj fyanxent loppeyw. Ub Tkucdaq 23, “Mfoqofoxp”, foo’hg siefg xoci uwaew moworagx, ejalp ahk minduvxoxh bu dseroqedf.
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.
Erfbadohy is omdefiqtl shof nonaevix e qmidc cedz im bihgaqofs yejyx ax jwioxm axc mritaq uizr qyeuc acta zba tecyifp ijvuqraxp hofjeemav.
Lees prafs at yci xemil tuuwgj ap gzaon fseganqaf ps kgu yihosobm ozr xzesd aos voc vidq um aunl zzuof onu om lnu inhubseyr.
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
Ava iv (p, r) leussogawo wmksaq sik nouv gikatiikn ayd duxij ogacw e ysnejtaxo.
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.