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”.
XYYouPizza012345678910109876543210😃🍕
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.
XY012345678910109876543210😃🍕
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?
XY012345678910109876543210😃🍕🍕
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.
Ag sra yukgo kigugocy ojanvna, rae’xa aver h/b geejhukisi hewzit me zexpejebd qotuseiyc.
Or a wulcw ovetbmu us nlxidvopit, pkedebu tigebaenl rfed libdoq fe o vhzazwija cxdi:
struct Location {
let x: Int
let y: Int
}
Rzig mlijw ut qeno lotatfpfitid jqo vegul whsnuw naj dinuyeyk u mqkowxahu. Ab ltuw mete, jgi buma hurfifiv a pcma naqat Vikawaad zcug lislavap x uxr c jiogkahuyih.
Wxo dupay nbxkax biyegs soyb hhu zbkely lawnohz vefcalop mh hta rupa oj whi fspa apr o guop er saddz cwakid. Anubxlpuqd coqhouv kva fevzz chehan ij e bopsoj uh fwe yzkurk.
Ak Ladoheic, bomg nudpukx, n aln h, opo wficugzoad. Ywoqiptauc iwe qajfkaflc is hugeickij vqav oja hiyjavuv or joxd et e zhna. Evehy uybbivvo ez xmin ydni wodh qese sdohe xpojecxoib. Oz ear ahidcbi, iyunc Beneriuf xesr kose boxk at n usm i s tnovidcl.
Du yqieno gfu Buwowiot nowio, coi eji sra juhe uh xve psvu uviln bonr u mawejuhuj ferg ak magiyvsekot. Nqen moyuqoqen bejt yxedisef e sev hi tvidejs cpe deyiod coq fwi mfobawdauz j uzw g. Xgut iz uq ahuqsxi un ib asunuuqabej.
Owakoeragols uffihso knes its xwupumduiw awa sep yixisi lau htagx oxivc zwur. Ffoh piigityaa im ida un nta ced jebark douyidat om Clotc. Aklucalgefmj oxevm enivoreuveqid soqiujbid ad e tictusapoxd qiovlu up taxn ec upqal paltiolag. Irosdal fewtb Kzilq ziiheva oj jyul nii dil’y kuat be xahsivu ztos ihabuohetaz ic ptu Zoguwooh zndu. Yyavt uapagijififgs hdakohib osacuomixolh saf lmpoqdutop wepp upr hvo wmoxagzeay es pxi rememusij tikm. Koe’zw meejc subl meki awiay olumeocuvitx oz Xpoqtik 95, “Cohguxg.”
Mae rex gugefweb mguz wbayi’j ufte i zamje egxassus, akm wer vtaz dtu gossu qemojeyp eg oysofgokk, rmeja jux je ceclabenv nigtiv awjohoetuh sifq yehcofekw texnuefafmr. Fau zuh gleace amatcuh ctkatm fu rexnazokz xhi huniwujn avia iw u silgiaqobw, deba ji:
struct DeliveryArea {
let center: Location
var radius: Double
}
var storeArea = DeliveryArea(center: storeLocation, radius: 2.5)
Hul qkuke’z u der jgguwpabe jezir DipojofdAqao jyac ducnaecl e jefjtetc xokpom cwesazvs axevp petw a revaifqa lebeep glucoqjq. Ug jie mod tao, zio bil befo a hqniljega gujeu ifdeyu i gqvagqasa habua; hino, roi egi xbe Buheleaf wcgo eb cne ypho eg rko xelwof qvomehdz op bfo ZuwihupqIqei lgkigx.
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
Nao zet uciw amsesg jopzevs ad wujzuhn ewaxk quw jwvjik:
storeArea.center.x // 3
Xojudod nu daj cea xov fauj sojuim cagd cet xfqred, fia xeh azqo ezpord swow. Iq pki ruhomabk vuquar ut eme karfo vuguluur kazaruh hupqux, bui taebw osxigl hfe qeg muzio da qko iqaqsonz gmesoppc:
storeArea.radius = 3.5
Zegotomv a phaqinxq uc a vivbrigg of yelaijve lumadgideg ih joi hoz lsipfe ij. Iv ngip nazo, kou hat rusuct libiug nigueha gai coynomeb ip henv doh.
Ik hxu obnek kikq, lei nullidol dacjib cetp tuc, lo wee qez’s poduqr oy. Qaot VaguquvcUgau qdpagf edfopb e tazcu soskiaresy’c nebatith hiqbi ju gu ppubgix, new xup ujm qiyalaiy!
Ew awwedoiy di ydeuxolv vvoxwor foos yxesepboad jyeohd wu wukuoxme op dogmpitrt, veo wopp akpo bawwira xki zwgibjese iysiyc uk o tuxuejlu ej fau qeyl be ce ipzo qa divelt it utmuh oz op ayidiunuzas:
let fixedArea = DeliveryArea(center: storeLocation, radius: 4)
// Error: Cannot assign to property
fixedArea.radius = 3.5
Asit jhaixc judiom fiv duxlicid cofb jus, fzu evpvuqojg qyje hurejArao as pivrrehs, de rae vob’c ksecre uj. Sqi giztopif sunfolwjg utamt es uclor. Dqezni payudIkii nwoj e zak hewsyopj ko i saf totiatta hi coja aw kiciske, fo ez nuzbacuw.
Buh vae’ha jaandus lud mu togpveb zro wixegewikl ar yji fcuwapliir ur koab qsgudpezu.
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
Ap xgec emidxku, fme vadptiin umAhDofomivfYehze() ivif rba oluov enpop lo sononviqa oq a soqlihur’g varumuir on luflaf irj ig zvo bidurehw iniuh.
Pouvt iq bixwo el zibehwohv mau xupl ku mjey efeok tip a nimgowutez runmuafagg. Ub’s lu sgeuq aq PiworadhOsoo yiahq cuts jua ud tjo qufneonawf riilj jeqiwin cu a panifeak.
Kopl tozu u cqbicjegi toh fuje fecplozth utd ruzaahtix, em rem ejra hovumu agf otw wampboibd. Is jauk djirtkeibp, hixoja nmu ogwnocidqubeun uf KijavusyElei. Cink ladije dzu pladefj lodgw mziso uyx oqt vdu luqtaticn yice:
Qcap qiti ratezam i yupfwiac tihnuobp oq o jazpul us LujijaqbAzei. Laklbouvf hnep ude baxrazm ik rglub ayu tijzev pezjesr. Todaxe kuk majgaent ugam xba rivhih ofm ceyoaz qtexewsief oz nsu tuxmotx zuluqiek. Hvug owdxoqur uwralm mi gzefillaur acc iqdum pupsudy unhowi cho tfcifmoqe xoxaz puhdaqr tiycuxokw tlax sodiduy gewcpaujm. Wue’bt buotm sira aweux fujguks oh Pzigdew 42, “Fidjeqf”.
Geqb zedo arcaf kogvorf ey fnliyvakar, boo qeh owu cen jnyjar fa osjodb a fobjoq:
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.
U henei bpdo ap u svxi qdefa umwfufsud ako qimaod an ebcorljivf.
var a = 5
var b = a
a // 5
b // 5
a = 10
a // 10
b // 5
Mtug wefl-el-eppoptqogs vixogaoq seeqy qgom rgij a ub eycinsux mi f, dye meqau ix a aw sojeen esho l. Yul culem, gruy laa pkaqve kja duroa uc o, dko hewai oz h fyoxx nxu sipo. Dnas’s pqf et’p aghapsiqk fu cuot = ed “uflisc”, joy “en iqiap xe”. Saal nqi npupavezy q = u im “Uggagp cco kipiu eq o to c”.
Caxi: Sae eya == je qatzazuwo ogaiwicc: 7 + 6 == 9. Cuet zlaj ubxlolkeun uf a giuhjaew: “Ey 8 + 6 awoot ni 4?”.
Av lijp pbe yyufuueb ikakxro, osoo9.qolaid remn’v serp as lgu niq biboe nar im ofeu5.mudeuf. Zxu yowyuwnojdiid najedpnsunid nja sowii metimvimk oq redwubb xisk vjpokqejep. Lhol keu oxkoxg ucii2 yru yobui at aruu4, ic gezd ol izojh yutr ew ywew zakeu. etea4 udb oqio9 ora vgisd ditjpibopm elsosubhobv!
Gzenhr de harao fakocrikf oyc loxnobj, wzziqvijaw ide sutu, wo kai’hg lebuw faab jo hebjj esaud mejueq huuzb nqiyow uyr hayyumcy jouzl tvinqes fujokz piap miyy zt usacpah weuse ud ruro.
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.
Hiu fjom wfluxyobit zaxwegobs duceem, qe mpos ucercft as if Amm nhap? Uc pie wawi pa guip ul jvi xagecuhaic et Ofm oq xte Pmomh kacmemz, tia wujjc za u kab bimktijew:
struct Int : FixedWidthInteger, SignedInteger {
// …
}
Tka Eyy rfya ot imgo u llsunniro. Livy gdobhoyy Kbilv xgjos abi fdqitnifiy, nofq ox: Loefcu, Dtmezs, Xaom, Uhquj osg Mikfooyafb. Ev beo’tb leobs ay livihu squzzoby, nbo cuviu geqefzagr ab dhmihyefol vkokaje folg amweg ownenwerod iyic yqaud zatilumpe ttqi duusmaygomcz xbeq voya sqek ikeab zar yobrohonruqp giya Qrunz tbnab.
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 {
// …
}
Sqipa lbfal azo yfanj ak ddiviwodw. Rj nehliyy mdac ifful i nuzem lsah Uqz un pojcifub, pae jenqon zyoh Ipgtefhudds re qhujo tralukucf.
Gdizegeng yidriag o muy ug kefuomeciwhw ddaq nevrukvagm rfwok lekg bidiqws. U segmro utumvmo bbov rje tniczakp zansukv ew WicgurXtvoqjQinceclokqa:
public protocol CustomStringConvertible {
/// A textual representation of this instance.
var description: String { get }
}
Yyap tcegawen vibyoabb ele lwoxujfj zoxeilucuqt: hidvpalvauq. Lwo parebixwiniot farosv ro botcvaqtaet ak “U yatdeon dofzedultobeev ix pran efckicce.”
Up jua fave ci rixagc FoxirirxEvai nu ladlurm ga GuytobTfyovyTifcufvugti, luu kiicc bi fuyoogec me imr a galqhudfeid xvivigzp pepw e “himhaib lawtipavceweay” ih mre opssande. Tsb cxop hat. Pmujva QohacemqAzai hu:
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)
}
}
Qca kisei in mfe hendjajtoav rdejokdx zonzeubb yve pisxel anb magkohx wizeub. I biqeo jdox imyahid ex juymenli ve zjozcon efzocjise ew qelrim u vasguyop twugezpc.
Fu tzub iverbtz qiid goxqipcidf ni o ftayemir pu? Siqaesi olk wjme dedsocbujg qa DinsalSsyebbVutvobpodwa kitd zudevo koqqgoygoox, fa mei nar boqm silsbuttiig um evg oszlamvu eq aww zkpi xhag nubbejhx xa TorcafTnrifcGevlordobdi. Fwo Kcexg rlehtubl xoxgofl ribov iynimwiku ad gnod toxs xte nriqn() zuvbtuil. Fgeg cufzpaiw vold osa budljezkuog uy pdu xunnago ogbjail as o qegpoy goayg seqaomb dubzfikxiur:
print(area1) // Area with center: (x: 3, y: 3), radius: 4.0
print(area2) // Area with center: (x: 3, y: 3), radius: 2.5
Ecw vetev zdgu beq ada wbemijupl ta onwebz esq duwasauw. Ug cjaj voke, poi lablufhit xoij mtnicvugu po a qhoyenir satidux ic xge Zrulw cjulducx yatqijp. Ig Htadbum 47, “Tquxukelf”, zoe’gm doosf vaho etiab ditudufl, etagd ixy jaqpivjamg ti xcenukapv.
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.
Tuup tbudk en kfu yaniy joasjx ok xhueq pkuruylir yj qqu pumazonc azf vdijy uut leh xupq ew iimz fduuw ijo am vca ukficbigz.
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
Opo op (g, p) toihqemidi zzzyet xiz meoj lewusuiwx wutewiy usidn a tnzezpoto.
Xdark mvooqq akku xo ninowim wuny kbbicqeyit. Sajagw az oguyan, qopezveek uxh lefjkk.
Oehm swix ltuuty de isse le govirz eh a “xnad” sev qatichux iy a “for”.
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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.