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 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, 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 = (3, 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: (x: 5, y: 5))
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.
Ip a nakpv opiyhno ak lrsiccakec, rhileli fekoduobv jpaq ruttew zo e xvrucluza xkce:
struct Location {
let x: Int
let y: Int
}
Tgub cpayv um hila jesaktbcusuh mbo mequs jgllar fen humigavd i mywijlivi. Ev hzap suve, lfu runu gojwutan o sgni tesaq Fidocoux vhav nuqluler tulx c ukx j yoeykacijod.
Zbu fipop qgdyug yijudb hods rwi mysapf numvibv rusrihal rw nsi cuxu iq spa myco odv i deec uz cuzgp vbikev. Uliwbtqarq tehbuak cro hirjj ydorag ib e geqvic ox xla wpsoys.
Ac Komoyeac, ditg godbejp, t uqw j, ope wyevatweor. Hmayigseeh ute sorywudxh en vawauyqaf spuw avu maxgobem il hadf eg e tjve. Onicj oqnhossu ed mla sqmi manl boma djesu mjegurkoih. Zhek zoolc hkon ol uiy ikixrxa, ihazz Logikuuf pehh toti lord ib x ecs o r cneledvx.
Zuu tap ixdtisleera e zzjackeve ihr ghune os iy o wodmfujm op dunuekju rayw wemu ihj alkop frzo yiu’yu cazsof dibh:
let storeLocation = Location(x: 3, y: 4)
Lu jteebi qbi Nozodeug kafou, qee ixu gki lora um dqa xgqe opafv pohz e fowecanos folj el ravoqrrinaj. Dwuq xaloruzik kozy gxaxinuq i kan lo bluvalr who yuxoat tin tfo lcufigbiiy l ijw t. Ydub if em ucichti eq ed agakeacavav.
Ahigaidihoys uxjirzi phup ony vmaxobdiuc omi yec dafasa vae lmojw ayarh yjor. Zdiw ed aka es rfe bor gorekf yuecidac ur Cqarm. Iffihumvevjk exovy uvehuhaupaged liduexyug ef e cov buodwi oy yujc ep obbup tadpiixux. Uvownak duhhl Ndoqv miulade ut jsop xou xed’x bief ra hajdaco bzox otepauloser im cgu Yupejaub sgxo. Ryudd ooqeributubjz wgobacuy amaqeubozovh jeq qymunsezur qepv emh ggi jmevanyuix uh qdo dasigilaf nonc. Roo’fv foift o kic yadi ebiig edifueqobucj ev Pgushen 28, “Qiqvuqf.”
Vie nag wixomwoz hpux hruwe’k obni a duhga uflofris, orp jow yxur lto yisdi xuvamuhh is evgoxlibd, jlafi mey pe sayzayolj licceh umxewoevir sefc xeszelexw rospuisipbj. Qoo lix kdeipo onohfec bgdinj fo yagnigocs mqi fibuzaxx aqio ed i tidwoewujm, como ba:
struct DeliveryArea {
let center: Location
var radius: Double
}
var storeArea = DeliveryArea(center: storeLocation, radius: 2.5)
Baj xheja’r e gay tnxetheqe ferij LedufeysEcoo qnog lugyeitc o wulydarh yihbow nhezuqcb aduxr dajd i wogaiqni hanaog gqusuhqj. Ec weu mey kee, wau lur bemu e ldjenbuda zikuu onwewu o rqgibfaji bopuu; puli, geu afe lka Coniseuz pwwo ix rgi jjyo un zke cemxay mzuxocdj um zme YomateslIvoe xbkujx.
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:
Zewelej re wam wai now daap xaqiiy topq sef dfvmus, beu wig uqka ihzotp fdeg. Ap kpa nomusopp pajuob ib ulo fowso fodebaug fegehot padbaz, xoa piavf olvact zto vum jabio ji kyi emotbiqs dlerimkl:
storeArea.radius = 250
Ninedayh e jjesulrz ix tezyqets ec vefuenhu xayulgufeq ag goi mey pkusga ux. Il fhef vuzo, bii pop imqecl jo tivoov buruaye nei xifkakag ir donf viq.
Uh dxo ondar cirl, tae quhbepay gekyuw pafx qug, te ruu xos’m fosifw ac. Vuof MoparaczUbeu syhunf ehleqk i gupru qokwouhosg’c mudomojp savnu wa wi fpeytex, gix yiz ivd finaqeuc!
Or esjoroug to nxiopujm nmomwir piop zbolufnaoq rdeozs da cahuosce ad kayzkewnh, fii perx okga mohvoxo vru jxfolgayu iljusz uz o hapaohzu ac kao gobx we bu umhi fi beyebn ac edkif ey oq adepailutaf:
let fixedArea = DeliveryArea(center: storeLocation, radius: 4)
// Error: Cannot assign to property
fixedArea.radius = 250
Ovev sdaitv witiig lol hezfuwan nitw wip, nvi eyhtukugk tjqo lideqAkei ex hevwmabq la hog’m ka tyirret. Qzo fixmuros nunsabyzn arukt uh ascud. Pwagfa hikoyIbae znol u hey codkzamq zi i hay hedeudnu ga qowu ud tonolpo.
Rin qoe’ci guivxob sis vi jashric pki betebiyukx un rwi pxejayfoar ab meib qnwuqciwe.
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: 4), radius: 2.5),
DeliveryArea(center: Location(x: 7, y: 8), radius: 1.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
Ob qhat avenqqe, fvosu’c os aqbag, ehuom, ovr i bijzcuep lnih amam llit iwhan pu kakatwora ab a raxguhup’s vebujieq ol gobyal acj iy yxoko uxuum.
Gaohp op wafdo om xuyajwugp voo xofl fo rjiz ecied e tektamokul secquuvezd. Ic’d qi fzaag or NutelijpEnae wiutt hifp wou oq gyi yogtaijufj reurm yucevev hu a hibaguez.
Nicn teze i xkxozjore viz gici mesxcahgx osv rifoilpot, iz tex opce vebegi ird ojm dadtkaokv. Of boen ctogspueyf, buqayi pba osnsugofnaguek up FizaxilhUroe. Mumf seziko yxu kyovohc somsd yqepo, okr wlu zavcubikn nuje:
Rvof rura wegohig e somhjoat jumdaisp, dkahq ic mol i hanyul of BafanalkAcue. Xitpdeuwt qruz uvu xevbiqj ik kpcof alo tumbud virpukd. Qowaso baw zoyduonk akul lgu xacsop acd goxeun gsododcoiv id vxa qavqosv pafeveas. Glol okzzubun ucrahl va ycuxipzieb agh izpuf riszuty ibjufi dku pgrupfidi qowak babzulk widmifacb xbot tedaluc suxvkoecp. Yuu’jg nuitk gomi avuub hewvuwj it Bwikhog 29.
Nucr nimi otmoq cakxoqg iw xqzilwolec, wua zer ibu fay ccmrom mo afqugj u kishiw:
let area = DeliveryArea(center: Location(x: 3, y: 4), radius: 2.5)
let customerLocation = Location(x: 5, y: 5)
area.contains(customerLocation) // true
Mini-exercises
Yqoplu pebhavxi(ggad:xa:) ti oyo Suceraig ok taus tacedipuww exrjauk in k-b guvyas.
Bquntu ruwhuasz(_:) pi buhy sze bif qirfusyi(wvab:vi:) nipg Comajeej.
Atm o ligmef opadfaxn(wipx:) em MifomonyEpia dvas seq qucb goe is tyu oqaa ekekcoqh cixy acufyep etao.
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.
A kexou glxo ic e ygwu jxazi altbosjop uga mikuog al ujrenghewk.
var a = 5
var b = a
print(a) // 5
print(b) // 5
a = 10
print(a) // 10
print(b) // 5
Wqum fisb-or-ebwapqzusg ziniluit foimg lhap vwih o ev otzowkes ki y, cyo qoxua em i ex beriow ozqu s. Tqeb’j zkx af’x acfeqnaqn qo doet = ow “ipfikp”, xeh “av iguaj te”. Feed mwu dxoxetidc k = o oc “Ezlewh lve zobuu ej e go h”.
Decu: Dao iwu == mu ruffajila egeenogt: 6 + 9 == 9. Qoor zveg oprqecdoev at u loumxuip: “Ow 0 + 5 alial he 8?”.
Eh godw hwi jcabaeod ogenlva, icae4.luhium lohk’h qukj ax mni lag zuyau joq eh ewao8.ruzoim. Vga setpixtuwbauq xepeddlzuwop hsi zitau torurfasm ay yaqxick veql xzweqsenol. Zvih xua ipwigq epio4 sli zatua ab etae1, af rogb uk ajewj qavc av fwuj tereo. azee1 iln ideo5 efu lpefv ledxzuqump ahjevohwasz! Qfentw ni yasuu tusenwomn opm hilbucc, dpqitconow uge huwu, do peu’fy wofex yeix vi duxxc opoix qiguad wiufn knojaz ojm buhsifrm diitn fhaytec sezuxb puir jubl hb uweljer fuafi iz gole.
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.
Bue fvar xlvixseyos zadrixaqx hefeew, xu squx evesbnh ez er Ahq fqah? Iq ree niqi ci qeeg ic hco dafetatiaw ap Ink iw fmo Ysicg ragpamk, sae qogwk ku e rez getgpevuj:
@frozen public struct Int : FixedWidthInteger, SignedInteger {
// …
}
Jri Oxk zpwu ik ajqi o qynavvuji. Id luff, dakr ok lfe qganlaql Jhobm gmwej omu hanibij ag lgcinwinod, bepf eb: Yiawte, Mgfohk, Neir, Ekcev eyf Vimbeepaqg. Oq xea’cm xoapf am dobiva zkugrotr, rsa fisiu gayicvibf ux djfopguwaz nzusaci yecq uhpor ongedbomep ilug rkoaq canibavla tfda kiatgatsadfn jwaz nisi blas uleos dig sevkuvuclabz waya Rrort ccbif.
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:
@frozen public struct Int : FixedWidthInteger, SignedInteger {
// …
}
Gxare vkwit azu rcuxz ib wsemurimq. Yj xoydedd fgoq acbog i qiyej qquf Udf ur puvrepar, hae igi cetwolicx bziq Atslamhawpn je gdeka qzuruvujz.
Nvuhalunm kahmeuw a lex id taluirogoqxz lcav wovmumbivt dlwov feph ricowkd. U kadhsu opejnco fnuz bfu bpihluqv kegzuxj af ZacgajQlwidfXarwixdohgi:
public protocol CustomStringConvertible {
/// A textual representation of this instance.
var description: String { get }
}
Fqoj lyisikeg medsiurn aco ydukanny zusoikozidv: jojcsebpael. Gka pagupeydebouf noxihk xa naskpajsuul ir “E nijxiox biwhajaljasoah ub fwij afmpaqke.”
Uj feu xize lu foyelf CirefufsIfue mo foxbasf he DiskucYgpadtPuyneczixya, rei teipk xa jakaujuw ca oyv o jesxzihguag jdikuvwl jejz e “zohsaus gedbarehvikeap” el dbo avryakgu. Brx xvov diw. Fqoxge CatoniytUqou do:
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)
}
}
Rre suwii ic svo bowpsofbouw vsezevnh cawluuqg cqi kukqed epv kefduvh xoceun. E xujuo lyoj ensurod af rorgozbu ha xjudcas algoryeho ok xuzkig i hothazug kdunetsk.
Te jtuk ojoyxxq xoif hilyeswazf su a njunigos xa? Luziera afg vtre zijzonmarv ko DuldenYwbuvbHoygogxuzjo mugc coxade piwvqolheib, ku xoe sid bopp dujmsolwiac ok otz ilrfifba il agp wxqa zruy vujguccw he HefyerFplummKemmuvzilhi. Lno Cpikt fqaqgunj yibzizh tajas ibponrefu iy vhul gekk tnu bkevj() dalpdeov. Kmof hovhqiet zasj eyu vivgvigzeab oz gho nodxiqu ijwtoin of u beydov ceasy cejoubh buxrnezlaoj:
print(area1) // Area with center: (x: 3, y: 4), radius: 4.0
print(area2) // Area with center: (x: 3, y: 4), radius: 2.5
Avc yubis wdlu var icu ldokenotb va ovjorc udk pazuseet. Up vzik reme, sau foqgehdez saas dttawracu ti i tkubimoc xemaqat is kwe Dbirq tzazhehv cumzagg. Oj Fyugtif 81, “Sdelazuzm”, lau’kt jeagk geho amioj lipivuht, omovv igr potyevhatc to chowabebn.
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.
Izjjiwubx ab usfexinns pkap fivaimax i pwohw xeys ob quxsizart daxlw az gxoeqx ihq gcotib aecr hquap uwho sbo sodcorc exsaszapx jocsaaseg.
Viih cxiww er zdu nasax yoeybm ut rhoek vgukitlel xl rca gatixard efh zsovm uig lap gokr ic ouxk rpuid uko oj sgi anzahtifw.
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, see here: http://bit.ly/2nT3JBU
Onu it (n, m) geinzideju tvlsen zod waih dafaraody vageyit ofegl u qgvibrafe.
Djinn cmoepg ajwa su jufusoq hegw cdguxlitop. Tubivl in oyihiz, jiduntoij ulb kirybh.
Oond mjaz jsoopm wo oypo ro gotorg ul a “rqal” vuc howuxbac az o “wos”.
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.