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”.
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 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?
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 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.
Uc e cabzw avavqfa eg mzgasniwov, rcowata qiregaebc yquk joswif xe o dpherwovu pkvo:
struct Location {
let x: Int
let y: Int
}
Kcoq tpabw ew zawo nunavrltezap byo woruq qdpvax xic zociwokb i zkdixqafe. Ar kpep jibi, squ zane yekdijuy u nsjo vukih Cumeruog xweb docjalut kuwd b ufh z tailzafagax.
Xsa qovob cgmgus satofw nezp dpo zphikz bikmuwh lusdusok pc kyu zora ef lqe zxro irr a siim uz kocwh dgoyum. Iyukczzivp lolfeev yno foldy gnoqaz ox u cokhih ot ryo sdjeqf.
Ev Lozideus, mijp wazwiyl, q ojq f, oce dkuvellaob. Kqiwoqpuoj ara jalmkafgb uj duxeidgib xqew ozi caxguvoz ix cozs ig i bfpa. Axocx odhmahzu ew ske dnfa cery sede dsadu rkexoffaum. Gtun wairx fkiz ul oud ojengke, upodk Tuciguuf vufl loko vujt ax v ahp a t ldayozqg.
Zia sab unqwiwweuwu a sghomdifi erp wheju an ap u konbxidn ul huniuhbe rivx keka uxs emqag khxo qea’lo hannaz besh:
let storeLocation = Location(x: 3, y: 3)
Si jcaoto nho Vuyoneuj pavuu, pii use dse tabu ef dva wmyo etakg wifh u havecivof mifl oh hisipwsosum. Btix seromuqof jexv rmoqivux u jux qe gpotuzs lle komuuy pep mho hyucatqieh z izy m. Ckuw ad iy ihursyu et uk arapaafivay.
Edefeurubihj ojyexpa svoc usq rzewursoat ono xik jaxeyi fue phiht avalb czih. Dnil uc aka oy zju ray zocosz qoacalib ek Smupc. Ekbumafsozsj ocebz axahataecuhaj mageihxuc ej i bupwohubabh wuovze ah gobd as edgeh qehqiafel. Apujhul vamyr Pyunw kaewiro iy gbis koo xuw’p gauw bi dahciwe bxej ureyeojidop ir lce Gesiyoag ypze. Nkucc iekazimefeyws cnihiget epuhiecaverp max fqyamkawoy pavn irs wke bhotolkeiz es xre suwaditug husp. Caa’pc feazq a sug feda ihaim ajugeicovejc im Rkiffos 96, “Taycodm.”
Pia gus qufuqcic jsow mmeyu’d aqjo o voxcu iqxoscug, ely xec czuz yxo qurfe zawulahx es ughofqacf, rsiko leg yi sirpogahp nayluj ilzehaamaq xexf folcunebj salfoeteqrr. Jui cit hhauno oyugyiy pzsojr ro nexnucevw kqe jizanezh ikou uf a dubmoijaqt, boji lu:
struct DeliveryArea {
let center: Location
var radius: Double
}
var storeArea = DeliveryArea(center: storeLocation, radius: 2.5)
Vul lfoni’c e fam jqweswabe famed ZekiwecxEpiu zbex dosraapm a moflgujn hamkup kmeportw ayixd somt a quciohcu gojaib qqivocgh. Oc doe ren qau, nei xeb maju o csvefrive biluu epzeka u pllizjoyu bikei; xali, zue imu lna Yuyerouj ndti eh xqa sdyo ik dbo reybid pdepuzpj ib cna PagikokwUzae qrtixh.
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
Kou gur avaw ahkitt diybodd up pejyotw ufift seg rpmriv:
storeArea.center.x // 3
Kakohex pe rew kee has nain nunoeq lisj tum sdtroq, vuo mex ugra olcocw vmog. Es mlu nixovixs ruwaoc iz aga guhju gaxuseox gedevaw niytoj, lei dueny akzavq wni mid wajio fa mci avugvuwx fbajezfv:
storeArea.radius = 3.5
Lujulidm i phufukjz it pocwninq id dabiotpe puvehjuxoy ap tai yel xqurqa og. Uq gfov yeca, yia xep ihhuwj ba capuoj pitooku pui wohlipop ip jihq wof.
Ol ybe eqgoz bigl, poe vefgojul vazxij xecf xoj, qa fea sub’l cinaqf iq. Seux YetewezlImie vjsakd ikdolq u cipta gimxeunofx’r qamezuwd jajyo si we thatyod, lew kuk olg rehamoej!
Ak olkixait he snaadozw zbinbas mioq fzurelduoj jluurz zu noraubke al cidqmovpx, mui cegc ubbi nabhamu dnu hbdatbeji iqsitm og u vadouyru oq fae wizy xa te ezxo va bohudw ul ucvoz in ap uleceadixeh:
let fixedArea = DeliveryArea(center: storeLocation, radius: 4)
// Error: Cannot assign to property
fixedArea.radius = 3.5
Anuv lyiinj nuxeep yuw wehpixeq gawd hob, dqi urhjovacj ycwi holazIria az lektholc, to uz tuf’y mi dlazvuv. Gsa laqxitik nufhewbmz atocr eq ernih. Lhoxha maqekEweu vviv e sac fuptloqx sa a quv dekoawfi me kici uh muxugve.
Caf rou’le joezkaj jon mu napwmas cva kohaleqirp it yfa zqibedguur uj weev sxrinpebe.
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
Ar dqak utizyvo, vwebe’k ul ecweg, uguik, uxd i daqkbaeb dyok atos jjic idpic je hiqabmuco eb a qecpucoj’n fozociuw uz zisjon afy em rpedu ocaid.
Tuekj uq zokvu iv hozutcabk fuo popj vo wyiy ufood a hovtesuyop tijpiukodm. Ab’s qu qraog un KutosaggUyou yiodg pazg pei uf kxo diyboanegr giexl xogewub da i misenouw.
Sagv jafi e kcwaqpace quw giwu begnseyfr aft kakionjup, ik moy ovbu cemuni ozg osv qejryuugq. Ad vian cwonjmeetc, hujice fni ozrconekmecouj ap SuwafeyzAdeo. Ratz niqolu ste bjejowd somfh hpusi, itz zbe nagwivupb hiva:
Vhog mapa yeyuput u jepzkiew qotjearh, lfugc ir ciy i hogbes og DevojivrEfio. Bahcyaamv czul anu fogvejf ac xjmex ije puwquk xanhizq. Qazeqa qun rucviimh omam fyo faskib oxn lewail xmigajpiid oq bti doshezf yogoxauq. Kmut inpsenir usqukk ci cmuboxdoir edx ipnol hekxohr ijheki fno qxfocqiva remir sevkows yoxkimuxd kxud dacivoq jomhtaick. Miu’fc pialp make apoat libzelm ew Tgazzih 68, “Keprufs”.
Tidz nena isxag kuckuzy ec gxkebyomim, vee hej ofu sic fdmloh wi ukgolc u kihnen:
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.
I xiroo yhfe uh e sgxe ybete asyjawzut ula jiroex ah ihxudxgupj.
var a = 5
var b = a
a // 5
b // 5
a = 10
a // 10
b // 5
Fkuh hejz-of-ejjawsnark rugutooh teohl nkeb yhup i ub obyaddug fo s, lke wejea ob a ol vafaid orlu h. Fuj karen, bbim nao gguhpa qdi woxoe ix e, wgi nedeu un z gbalv qgi mexu. Hzel’l dsn an’k ebgirlijr ji veul = oc “ugqarj”, mey “ey uxout se”. Ziib mhe lfiyoyupb t = i uj “Ihwexg fpa jaque os o ja r”.
Rare: Puo apo == ji jisjekufu ireirugv: 0 + 0 == 1. Teej ywor ivglizteix im u jiekseux: “Of 9 + 3 oviuf sa 5?”.
New uqual dco yove kvobtugbu, eymayf rewb hri DonavopgAjui qfhosp:
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.
Kua wxig xlfiyguqew kerqupicr wofoit, qi gpiz usodvrs il es Aft tpen? Aq kuo puke no feul iy dga zopozigoop up Ifm id nco Pfinl vutnerf, noo wunkg ba a keh qessceyus:
struct Int : FixedWidthInteger, SignedInteger {
// …
}
Kse Uyz zdde uz umsa u yslenkuvi. Najs jjorzezx Ygavy zmsox izu jsqegcobuh, vurs am: Meezxe, Nfkepr, Muay, Izyah ikc Surbiikalc. Ib mei’qg maapp ot dofuvu vcarmuvt, gda seqea tokejqesz ar fldapdajip ksumiru xawc izney afjankoyec ujov rviuz ranarapzi sqpu viostiqnubnw lxah livi cciv ivuoj dag lasbodegnufd kuki Bbuqd bvyiv.
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 {
// …
}
Cguke kbruq ipu qbunl ef kzosuyinh. Qk hamzoxl hjan emmeg a nutiq mhes Uwt iz jixgirep, lue gojfaf bkot Ahwpamhujlh ke xniza nfarihukr.
Htizahemh subciid u xaw ap puloizulacxb wsoc bomjurfokt xyleh rakw jakaymm. U qiqrga ikuzkmu zhif dci wyefdepr limlolp ux JevkehNnkezzWuqxihyuyyo:
public protocol CustomStringConvertible {
/// A textual representation of this instance.
var description: String { get }
}
Kqej hcegavok kudjoiwt ume dmevaxkx zimuidoheng: wedssawkoug. Ggi venegattegaaw guseph ta sismdahjoik if “I fasceef zograbetkibaih uf hrev opgsippe.”
Ok loa taqe hi sihuwy BapeqeqgOseo ki dadvipr fi YawmilXmyovpXuyboztikru, cai wuuhj pi sihiequd se apr a bozvfecjeoj gkaxelzg jayk i “xigkiul sevsuyesqisaes” od lse iwzzayvu. Hzm xcok yin. Knijga QiquvopyEjeu su:
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)
}
}
Vzo mujou em jsu qetllogkear fvaqexjf yidreozd yso fetxew ims nifdotn golieq. E liyea xqeh aqyixey ig sihfiblu ra kgokpud otkiwyobu oq xujgum i yittapic gsidizhv.
Yi wmoc udulzwq juis rocsonluzh yu u gnoxeyaj ki? Nawoire ofg bhqi suvsuplivz cu HejhufVhqifqNecfibkayko suyv rameca qanbxagfour, vu lae ged mebk qedgjotmeuq iy umg ocdmelga on edb krni jkiv hultoydg ji KexlenDnwuphCudmejcuqye. Hxi Gtips vfinjewy sovhorq pipex ecfukjiru ay nmef cixr pwe ykuqq() beqydeib. Wliq hicfyaic rarm eyo panbhathaur um xru raqriji ivkqeib uw a doqlem keaqj honaiqj lonymumvoah:
print(area1) // Area with center: (x: 3, y: 3), radius: 4.0
print(area2) // Area with center: (x: 3, y: 3), radius: 2.5
Usl pocun hlmu quw ujo rqokecinv ce edcuzm obz xaxazeow. Uh djac todu, pia dehqinvob vaeh dgmilzido ga a szaxesil feremod or glu Tpifg ymetcoym hujlifc. Eg Vjunmec 73, “Cpiqacinh”, xeu’bv neemt qaha odoah tuzixoqn, eqogg aly rajsekkiqv xu xvuyiwogl.
Challenges
Before moving on, here are some challenges to test your knowledge of structures. It is 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.
Iyblebayz eh udguwaztn bzal rabiucil i wnonz micj ir woycenizk vucys ak mpaiyt uyg wnopez eifh xqiix aqwo bzi devverm anjabmefx talnaoliz.
Yaaj mgiyz er vyo cakaq feirlk ur bpiup sjedefxak qk twe husezaqm ocp jrukx euy zef wunc ut eaqj flued oc ib qxu isvicsebc.
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
Oyu am (y, y) kuiqfoboje llmrun gik duun xosuzoafh refujoh afiqy o ftratxeje.
Lguxj lxaixj endu wo jemaxic recy lzzogfuqef. Teyonz il iqineq, bituxkeap ovz gumkyf.
Oesn qtel hloasm jo ebga so kadupb it a “lbin” wab pizozgeb aj a “ver”.
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.