Welcome to Swift - Marek Piaseckimarek.piasecki.staff.iiar.pwr.wroc.pl/dydaktyka/pam/iOS... ·...

Post on 16-Jun-2020

3 views 0 download

Transcript of Welcome to Swift - Marek Piaseckimarek.piasecki.staff.iiar.pwr.wroc.pl/dydaktyka/pam/iOS... ·...

MP
Linia

WelcometoSwift

AboutSwift

Swiftisafantasticwaytowritesoftware,whetherit’sforphones,desktops,servers,oranythingelsethatrunscode.It’sasafe,fast,andinteractiveprogramminglanguagethatcombinesthebestinmodernlanguagethinkingwithwisdomfromthewiderAppleengineeringcultureandthediversecontributionsfromitsopen-sourcecommunity.Thecompilerisoptimizedforperformanceandthelanguageisoptimizedfordevelopment,withoutcompromisingoneither.

Swiftisfriendlytonewprogrammers.It’sanindustrial-qualityprogramminglanguagethat’sasexpressiveandenjoyableasascriptinglanguage.WritingSwiftcodeinaplaygroundletsyouexperimentwithcodeandseetheresultsimmediately,withouttheoverheadofbuildingandrunninganapp.

Swiftdefinesawaylargeclassesofcommonprogrammingerrorsbyadoptingmodernprogrammingpatterns:

Variablesarealwaysinitializedbeforeuse.

Arrayindicesarecheckedforout-of-boundserrors.

Integersarecheckedforoverflow.

Optionalsensurethatnilvaluesarehandledexplicitly.

Memoryismanagedautomatically.

Errorhandlingallowscontrolledrecoveryfromunexpectedfailures.

Swiftcodeiscompiledandoptimizedtogetthemostoutofmodernhardware.Thesyntaxandstandardlibraryhavebeendesignedbasedontheguidingprinciplethattheobviouswaytowriteyourcodeshouldalsoperformthebest.ItscombinationofsafetyandspeedmakeSwiftanexcellentchoiceforeverythingfrom“Hello,world!”toanentireoperatingsystem.

Swiftcombinespowerfultypeinferenceandpatternmatchingwithamodern,lightweightsyntax,allowingcomplexideastobeexpressedinaclearand

MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie

concisemanner.Asaresult,codeisnotjusteasiertowrite,buteasiertoreadandmaintainaswell.

Swifthasbeenyearsinthemaking,anditcontinuestoevolvewithnewfeaturesandcapabilities.OurgoalsforSwiftareambitious.Wecan’twaittoseewhatyoucreatewithit.

VersionCompatibility

ThisbookdescribesSwift5.1,thedefaultversionofSwiftthat’sincludedinXcode11.YoucanuseXcode11tobuildtargetsthatarewrittenineitherSwift5.1,Swift4.2,orSwift4.

WhenyouuseXcode11tobuildSwift4andSwift4.2code,mostSwift5.1functionalityisavailable.Thatsaid,thefollowingchangesareavailableonlytocodethatusesSwift5.1orlater:

FunctionsthatreturnanopaquetyperequiretheSwift5.1runtime.

Thetry?expressiondoesn’tintroduceanextralevelofoptionalitytoexpressionsthatalreadyreturnoptionals.

Largeintegerliteralinitializationexpressionsareinferredtobeofthecorrectintegertype.Forexample,UInt64(0xffff_ffff_ffff_ffff)evaluatestothecorrectvalueratherthanoverflowing.

AtargetwritteninSwift5.1candependonatargetthat’swritteninSwift4.2orSwift4,andviceversa.Thismeans,ifyouhavealargeprojectthat’sdividedintomultipleframeworks,youcanmigrateyourcodefromSwift4toSwift5.1oneframeworkatatime.

MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie

ASwiftTour

Traditionsuggeststhatthefirstprograminanewlanguageshouldprintthewords“Hello,world!”onthescreen.InSwift,thiscanbedoneinasingleline:

1 print("Hello,world!")

2 //Prints"Hello,world!"

IfyouhavewrittencodeinCorObjective-C,thissyntaxlooksfamiliartoyou—inSwift,thislineofcodeisacompleteprogram.Youdon’tneedtoimportaseparatelibraryforfunctionalitylikeinput/outputorstringhandling.Codewrittenatglobalscopeisusedastheentrypointfortheprogram,soyoudon’tneedamain()function.Youalsodon’tneedtowritesemicolonsattheendofeverystatement.

ThistourgivesyouenoughinformationtostartwritingcodeinSwiftbyshowingyouhowtoaccomplishavarietyofprogrammingtasks.Don’tworryifyoudon’tunderstandsomething—everythingintroducedinthistourisexplainedindetailintherestofthisbook.

NOTE

OnaMacwithXcodeinstalled,oronaniPadwithSwiftPlaygrounds,youcanopenthischapterasaplayground.Playgroundsallowyoutoeditthecodelistingsandseetheresultimmediately.

DownloadPlayground

SimpleValues

Uselettomakeaconstantandvartomakeavariable.Thevalueofaconstantdoesn’tneedtobeknownatcompiletime,butyoumustassignitavalueexactlyonce.Thismeansyoucanuseconstantstonameavaluethatyoudetermineoncebutuseinmanyplaces.

MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Prostokąt

1 varmyVariable=42

2 myVariable=50

3 letmyConstant=42

Aconstantorvariablemusthavethesametypeasthevalueyouwanttoassigntoit.However,youdon’talwayshavetowritethetypeexplicitly.Providingavaluewhenyoucreateaconstantorvariableletsthecompilerinferitstype.Intheexampleabove,thecompilerinfersthatmyVariableisanintegerbecauseitsinitialvalueisaninteger.

Iftheinitialvaluedoesn’tprovideenoughinformation(orifthereisnoinitialvalue),specifythetypebywritingitafterthevariable,separatedbyacolon.

1 letimplicitInteger=70

2 letimplicitDouble=70.0

3 letexplicitDouble:Double=70

EXPER IMENT

CreateaconstantwithanexplicittypeofFloatandavalueof4.

Valuesareneverimplicitlyconvertedtoanothertype.Ifyouneedtoconvertavaluetoadifferenttype,explicitlymakeaninstanceofthedesiredtype.

1 letlabel="Thewidthis"

2 letwidth=94

3 letwidthLabel=label+String(width)

EXPER IMENT

TryremovingtheconversiontoStringfromthelastline.Whaterrordoyouget?

There’sanevensimplerwaytoincludevaluesinstrings:Writethevalueinparentheses,andwriteabackslash(\)beforetheparentheses.Forexample:

MP
Prostokąt
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie

1 letapples=3

2 letoranges=5

3 letappleSummary="Ihave\(apples)apples."

4 letfruitSummary="Ihave\(apples+oranges)piecesof

fruit."

EXPER IMENT

Use\()toincludeafloating-pointcalculationinastringandtoincludesomeone’snameinagreeting.

Usethreedoublequotationmarks(""")forstringsthattakeupmultiplelines.Indentationatthestartofeachquotedlineisremoved,aslongasitmatchestheindentationoftheclosingquotationmarks.Forexample:

1 letquotation="""

2 Isaid"Ihave\(apples)apples."

3 AndthenIsaid"Ihave\(apples+oranges)piecesoffruit."

4 """

Createarraysanddictionariesusingbrackets([]),andaccesstheirelementsbywritingtheindexorkeyinbrackets.Acommaisallowedafterthelastelement.

1 varshoppingList=["catfish","water","tulips"]

2 shoppingList[1]="bottleofwater"

3

4 varoccupations=[

5 "Malcolm":"Captain",

6 "Kaylee":"Mechanic",

7 ]

8 occupations["Jayne"]="PublicRelations"

Arraysautomaticallygrowasyouaddelements.

MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Prostokąt
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie

1 shoppingList.append("bluepaint")

2 print(shoppingList)

Tocreateanemptyarrayordictionary,usetheinitializersyntax.

1 letemptyArray=[String]()

2 letemptyDictionary=[String:Float]()

Iftypeinformationcanbeinferred,youcanwriteanemptyarrayas[]andanemptydictionaryas[:]—forexample,whenyousetanewvalueforavariableorpassanargumenttoafunction.

1 shoppingList=[]

2 occupations=[:]

ControlFlow

Useifandswitchtomakeconditionals,andusefor-in,while,andrepeat-whiletomakeloops.Parenthesesaroundtheconditionorloopvariableareoptional.Bracesaroundthebodyarerequired.

1 letindividualScores=[75,43,103,87,12]

2 varteamScore=0

3 forscoreinindividualScores{

4 ifscore>50{

5 teamScore+=3

6 }else{

7 teamScore+=1

8 }

9 }

10 print(teamScore)

MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Linia

11 //Prints"11"

Inanifstatement,theconditionalmustbeaBooleanexpression—thismeansthatcodesuchasifscore{...}isanerror,notanimplicitcomparisontozero.

Youcanuseifandlettogethertoworkwithvaluesthatmightbemissing.Thesevaluesarerepresentedasoptionals.Anoptionalvalueeithercontainsavalueorcontainsniltoindicatethatavalueismissing.Writeaquestionmark(?)afterthetypeofavaluetomarkthevalueasoptional.

1 varoptionalString:String?="Hello"

2 print(optionalString==nil)

3 //Prints"false"

4

5 varoptionalName:String?="JohnAppleseed"

6 vargreeting="Hello!"

7 ifletname=optionalName{

8 greeting="Hello,\(name)"

9 }

EXPER IMENT

ChangeoptionalNametonil.Whatgreetingdoyouget?AddanelseclausethatsetsadifferentgreetingifoptionalNameisnil.

Iftheoptionalvalueisnil,theconditionalisfalseandthecodeinbracesisskipped.Otherwise,theoptionalvalueisunwrappedandassignedtotheconstantafterlet,whichmakestheunwrappedvalueavailableinsidetheblockofcode.

Anotherwaytohandleoptionalvaluesistoprovideadefaultvalueusingthe??operator.Iftheoptionalvalueismissing,thedefaultvalueisusedinstead.

1 letnickName:String?=nil

2 letfullName:String="JohnAppleseed"

MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie

3 letinformalGreeting="Hi\(nickName??fullName)"

Switchessupportanykindofdataandawidevarietyofcomparisonoperations—theyaren’tlimitedtointegersandtestsforequality.

1 letvegetable="redpepper"

2 switchvegetable{

3 case"celery":

4 print("Addsomeraisinsandmakeantsonalog.")

5 case"cucumber","watercress":

6 print("Thatwouldmakeagoodteasandwich.")

7 caseletxwherex.hasSuffix("pepper"):

8 print("Isitaspicy\(x)?")

9 default:

10 print("Everythingtastesgoodinsoup.")

11 }

12 //Prints"Isitaspicyredpepper?"

EXPER IMENT

Tryremovingthedefaultcase.Whaterrordoyouget?

Noticehowletcanbeusedinapatterntoassignthevaluethatmatchedthepatterntoaconstant.

Afterexecutingthecodeinsidetheswitchcasethatmatched,theprogramexitsfromtheswitchstatement.Executiondoesn’tcontinuetothenextcase,sothereisnoneedtoexplicitlybreakoutoftheswitchattheendofeachcase’scode.

Youusefor-intoiterateoveritemsinadictionarybyprovidingapairofnamestouseforeachkey-valuepair.Dictionariesareanunorderedcollection,sotheirkeysandvaluesareiteratedoverinanarbitraryorder.

1 letinterestingNumbers=[

MP
Wyróżnianie
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie

2 "Prime":[2,3,5,7,11,13],

3 "Fibonacci":[1,1,2,3,5,8],

4 "Square":[1,4,9,16,25],

5 ]

6 varlargest=0

7 for(kind,numbers)ininterestingNumbers{

8 fornumberinnumbers{

9 ifnumber>largest{

10 largest=number

11 }

12 }

13 }

14 print(largest)

15 //Prints"25"

EXPER IMENT

Addanothervariabletokeeptrackofwhichkindofnumberwasthelargest,aswellaswhatthatlargestnumberwas.

Usewhiletorepeatablockofcodeuntilaconditionchanges.Theconditionofaloopcanbeattheendinstead,ensuringthattheloopisrunatleastonce.

1 varn=2

2 whilen<100{

3 n*=2

4 }

5 print(n)

6 //Prints"128"

7

8 varm=2

9 repeat{

MP
Linia
MP
Linia
MP
Linia
MP
Linia

10 m*=2

11 }whilem<100

12 print(m)

13 //Prints"128"

Youcankeepanindexinaloopbyusing..<tomakearangeofindexes.

1 vartotal=0

2 foriin0..<4{

3 total+=i

4 }

5 print(total)

6 //Prints"6"

Use..<tomakearangethatomitsitsuppervalue,anduse...tomakearangethatincludesbothvalues.

FunctionsandClosures

Usefunctodeclareafunction.Callafunctionbyfollowingitsnamewithalistofargumentsinparentheses.Use->toseparatetheparameternamesandtypesfromthefunction’sreturntype.

1 funcgreet(person:String,day:String)->String{

2 return"Hello\(person),todayis\(day)."

3 }

4 greet(person:"Bob",day:"Tuesday")

EXPER IMENT

Removethedayparameter.Addaparametertoincludetoday’slunchspecialinthegreeting.

MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Wyróżnianie
MP
Linia
MP
Wyróżnianie
MP
Linia
MP
Linia

Bydefault,functionsusetheirparameternamesaslabelsfortheirarguments.Writeacustomargumentlabelbeforetheparametername,orwrite_tousenoargumentlabel.

1 funcgreet(_person:String,onday:String)->String{

2 return"Hello\(person),todayis\(day)."

3 }

4 greet("John",on:"Wednesday")

Useatupletomakeacompoundvalue—forexample,toreturnmultiplevaluesfromafunction.Theelementsofatuplecanbereferredtoeitherbynameorbynumber.

1 funccalculateStatistics(scores:[Int])->(min:Int,max:Int,

sum:Int){

2 varmin=scores[0]

3 varmax=scores[0]

4 varsum=0

5

6 forscoreinscores{

7 ifscore>max{

8 max=score

9 }elseifscore<min{

10 min=score

11 }

12 sum+=score

13 }

14

15 return(min,max,sum)

16 }

17 letstatistics=calculateStatistics(scores:[5,3,100,3,

MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Linia
MP
Linia
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia

9])

18 print(statistics.sum)

19 //Prints"120"

20 print(statistics.2)

21 //Prints"120"

Functionscanbenested.Nestedfunctionshaveaccesstovariablesthatweredeclaredintheouterfunction.Youcanusenestedfunctionstoorganizethecodeinafunctionthatislongorcomplex.

1 funcreturnFifteen()->Int{

2 vary=10

3 funcadd(){

4 y+=5

5 }

6 add()

7 returny

8 }

9 returnFifteen()

Functionsareafirst-classtype.Thismeansthatafunctioncanreturnanotherfunctionasitsvalue.

1 funcmakeIncrementer()->((Int)->Int){

2 funcaddOne(number:Int)->Int{

3 return1+number

4 }

5 returnaddOne

6 }

7 varincrement=makeIncrementer()

8 increment(7)

MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Prostokąt
MP
Prostokąt
MP
Wyróżnianie
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Prostokąt
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia

Afunctioncantakeanotherfunctionasoneofitsarguments.

1 funchasAnyMatches(list:[Int],condition:(Int)->Bool)->

Bool{

2 foriteminlist{

3 ifcondition(item){

4 returntrue

5 }

6 }

7 returnfalse

8 }

9 funclessThanTen(number:Int)->Bool{

10 returnnumber<10

11 }

12 varnumbers=[20,19,7,12]

13 hasAnyMatches(list:numbers,condition:lessThanTen)

Functionsareactuallyaspecialcaseofclosures:blocksofcodethatcanbecalledlater.Thecodeinaclosurehasaccesstothingslikevariablesandfunctionsthatwereavailableinthescopewheretheclosurewascreated,eveniftheclosureisinadifferentscopewhenitisexecuted—yousawanexampleofthisalreadywithnestedfunctions.Youcanwriteaclosurewithoutanamebysurroundingcodewithbraces({}).Useintoseparatetheargumentsandreturntypefromthebody.

1 numbers.map({(number:Int)->Intin

2 letresult=3*number

3 returnresult

4 })

EXPER IMENT

Rewritetheclosuretoreturnzeroforalloddnumbers.

MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Prostokąt
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Prostokąt
MP
Linia

Youhaveseveraloptionsforwritingclosuresmoreconcisely.Whenaclosure’stypeisalreadyknown,suchasthecallbackforadelegate,youcanomitthetypeofitsparameters,itsreturntype,orboth.Singlestatementclosuresimplicitlyreturnthevalueoftheironlystatement.

1 letmappedNumbers=numbers.map({numberin3*number})

2 print(mappedNumbers)

3 //Prints"[60,57,21,36]"

Youcanrefertoparametersbynumberinsteadofbyname—thisapproachisespeciallyusefulinveryshortclosures.Aclosurepassedasthelastargumenttoafunctioncanappearimmediatelyaftertheparentheses.Whenaclosureistheonlyargumenttoafunction,youcanomittheparenthesesentirely.

1 letsortedNumbers=numbers.sorted{$0>$1}

2 print(sortedNumbers)

3 //Prints"[20,19,12,7]"

ObjectsandClasses

Useclassfollowedbytheclass’snametocreateaclass.Apropertydeclarationinaclassiswrittenthesamewayasaconstantorvariabledeclaration,exceptthatitisinthecontextofaclass.Likewise,methodandfunctiondeclarationsarewrittenthesameway.

1 classShape{

2 varnumberOfSides=0

3 funcsimpleDescription()->String{

4 return"Ashapewith\(numberOfSides)sides."

5 }

6 }

MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie

EXPER IMENT

Addaconstantpropertywithlet,andaddanothermethodthattakesanargument.

Createaninstanceofaclassbyputtingparenthesesaftertheclassname.Usedotsyntaxtoaccessthepropertiesandmethodsoftheinstance.

1 varshape=Shape()

2 shape.numberOfSides=7

3 varshapeDescription=shape.simpleDescription()

ThisversionoftheShapeclassismissingsomethingimportant:aninitializertosetuptheclasswhenaninstanceiscreated.Useinittocreateone.

1 classNamedShape{

2 varnumberOfSides:Int=0

3 varname:String

4

5 init(name:String){

6 self.name=name

7 }

8

9 funcsimpleDescription()->String{

10 return"Ashapewith\(numberOfSides)sides."

11 }

12 }

Noticehowselfisusedtodistinguishthenamepropertyfromthenameargumenttotheinitializer.Theargumentstotheinitializerarepassedlikeafunctioncallwhenyoucreateaninstanceoftheclass.Everypropertyneedsavalueassigned—eitherinitsdeclaration(aswithnumberOfSides)orintheinitializer(aswithname).

Usedeinittocreateadeinitializerifyouneedtoperformsomecleanupbefore

MP
Wyróżnianie
MP
Wyróżnianie
MP
Linia
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Prostokąt
MP
Prostokąt
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie

theobjectisdeallocated.

Subclassesincludetheirsuperclassnameaftertheirclassname,separatedbyacolon.Thereisnorequirementforclassestosubclassanystandardrootclass,soyoucanincludeoromitasuperclassasneeded.

Methodsonasubclassthatoverridethesuperclass’simplementationaremarkedwithoverride—overridingamethodbyaccident,withoutoverride,isdetectedbythecompilerasanerror.Thecompileralsodetectsmethodswithoverridethatdon’tactuallyoverrideanymethodinthesuperclass.

1 classSquare:NamedShape{

2 varsideLength:Double

3

4 init(sideLength:Double,name:String){

5 self.sideLength=sideLength

6 super.init(name:name)

7 numberOfSides=4

8 }

9

10 funcarea()->Double{

11 returnsideLength*sideLength

12 }

13

14 overridefuncsimpleDescription()->String{

15 return"Asquarewithsidesoflength\(sideLength)."

16 }

17 }

18 lettest=Square(sideLength:5.2,name:"mytestsquare")

19 test.area()

20 test.simpleDescription()

MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Prostokąt
MP
Linia
MP
Wyróżnianie

EXPER IMENT

MakeanothersubclassofNamedShapecalledCirclethattakesaradiusandanameasargumentstoitsinitializer.Implementanarea()andasimpleDescription()methodontheCircleclass.

Inadditiontosimplepropertiesthatarestored,propertiescanhaveagetterandasetter.

1 classEquilateralTriangle:NamedShape{

2 varsideLength:Double=0.0

3

4 init(sideLength:Double,name:String){

5 self.sideLength=sideLength

6 super.init(name:name)

7 numberOfSides=3

8 }

9

10 varperimeter:Double{

11 get{

12 return3.0*sideLength

13 }

14 set{

15 sideLength=newValue/3.0

16 }

17 }

18

19 overridefuncsimpleDescription()->String{

20 return"Anequilateraltrianglewithsidesoflength\

(sideLength)."

21 }

22 }

MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Prostokąt
MP
Wyróżnianie

23 vartriangle=EquilateralTriangle(sideLength:3.1,name:"a

triangle")

24 print(triangle.perimeter)

25 //Prints"9.3"

26 triangle.perimeter=9.9

27 print(triangle.sideLength)

28 //Prints"3.3000000000000003"

Inthesetterforperimeter,thenewvaluehastheimplicitnamenewValue.Youcanprovideanexplicitnameinparenthesesafterset.

NoticethattheinitializerfortheEquilateralTriangleclasshasthreedifferentsteps:

1. Settingthevalueofpropertiesthatthesubclassdeclares.

2. Callingthesuperclass’sinitializer.

3. Changingthevalueofpropertiesdefinedbythesuperclass.Anyadditionalsetupworkthatusesmethods,getters,orsetterscanalsobedoneatthispoint.

Ifyoudon’tneedtocomputethepropertybutstillneedtoprovidecodethatisrunbeforeandaftersettinganewvalue,usewillSetanddidSet.Thecodeyouprovideisrunanytimethevaluechangesoutsideofaninitializer.Forexample,theclassbelowensuresthatthesidelengthofitstriangleisalwaysthesameasthesidelengthofitssquare.

1 classTriangleAndSquare{

2 vartriangle:EquilateralTriangle{

3 willSet{

4 square.sideLength=newValue.sideLength

5 }

6 }

MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie
MP
Wyróżnianie

7 varsquare:Square{

8 willSet{

9 triangle.sideLength=newValue.sideLength

10 }

11 }

12 init(size:Double,name:String){

13 square=Square(sideLength:size,name:name)

14 triangle=EquilateralTriangle(sideLength:size,name:

name)

15 }

16 }

17 vartriangleAndSquare=TriangleAndSquare(size:10,name:

"anothertestshape")

18 print(triangleAndSquare.square.sideLength)

19 //Prints"10.0"

20 print(triangleAndSquare.triangle.sideLength)

21 //Prints"10.0"

22 triangleAndSquare.square=Square(sideLength:50,name:

"largersquare")

23 print(triangleAndSquare.triangle.sideLength)

24 //Prints"50.0"

Whenworkingwithoptionalvalues,youcanwrite?beforeoperationslikemethods,properties,andsubscripting.Ifthevaluebeforethe?isnil,everythingafterthe?isignoredandthevalueofthewholeexpressionisnil.Otherwise,theoptionalvalueisunwrapped,andeverythingafterthe?actsontheunwrappedvalue.Inbothcases,thevalueofthewholeexpressionisanoptionalvalue.

1 letoptionalSquare:Square?=Square(sideLength:2.5,name:

"optionalsquare")

2 letsideLength=optionalSquare?.sideLength

MP
Wyróżnianie
MP
Wyróżnianie

EnumerationsandStructures

Useenumtocreateanenumeration.Likeclassesandallothernamedtypes,enumerationscanhavemethodsassociatedwiththem.

1 enumRank:Int{

2 caseace=1

3 casetwo,three,four,five,six,seven,eight,nine,ten

4 casejack,queen,king

5

6 funcsimpleDescription()->String{

7 switchself{

8 case.ace:

9 return"ace"

10 case.jack:

11 return"jack"

12 case.queen:

13 return"queen"

14 case.king:

15 return"king"

16 default:

17 returnString(self.rawValue)

18 }

19 }

20 }

21 letace=Rank.ace

22 letaceRawValue=ace.rawValue

EXPER IMENT

WriteafunctionthatcomparestwoRankvaluesbycomparingtheirrawvalues.

Bydefault,Swiftassignstherawvaluesstartingatzeroandincrementingbyoneeachtime,butyoucanchangethisbehaviorbyexplicitlyspecifyingvalues.Intheexampleabove,Aceisexplicitlygivenarawvalueof1,andtherestoftherawvaluesareassignedinorder.Youcanalsousestringsorfloating-pointnumbersastherawtypeofanenumeration.UsetherawValuepropertytoaccesstherawvalueofanenumerationcase.

Usetheinit?(rawValue:)initializertomakeaninstanceofanenumerationfromarawvalue.ItreturnseithertheenumerationcasematchingtherawvalueornilifthereisnomatchingRank.

1 ifletconvertedRank=Rank(rawValue:3){

2 letthreeDescription=convertedRank.simpleDescription()

3 }

Thecasevaluesofanenumerationareactualvalues,notjustanotherwayofwritingtheirrawvalues.Infact,incaseswherethereisn’tameaningfulrawvalue,youdon’thavetoprovideone.

1 enumSuit{

2 casespades,hearts,diamonds,clubs

3

4 funcsimpleDescription()->String{

5 switchself{

6 case.spades:

7 return"spades"

8 case.hearts:

9 return"hearts"

10 case.diamonds:

11 return"diamonds"

12 case.clubs:

13 return"clubs"

14 }

15 }

16 }

17 lethearts=Suit.hearts

18 letheartsDescription=hearts.simpleDescription()

EXPER IMENT

Addacolor()methodtoSuitthatreturns“black”forspadesandclubs,andreturns“red”forheartsanddiamonds.

Noticethetwowaysthattheheartscaseoftheenumerationisreferredtoabove:Whenassigningavaluetotheheartsconstant,theenumerationcaseSuit.heartsisreferredtobyitsfullnamebecausetheconstantdoesn’thaveanexplicittypespecified.Insidetheswitch,theenumerationcaseisreferredtobytheabbreviatedform.heartsbecausethevalueofselfisalreadyknowntobeasuit.Youcanusetheabbreviatedformanytimethevalue’stypeisalreadyknown.

Ifanenumerationhasrawvalues,thosevaluesaredeterminedaspartofthedeclaration,whichmeanseveryinstanceofaparticularenumerationcasealwayshasthesamerawvalue.Anotherchoiceforenumerationcasesistohavevaluesassociatedwiththecase—thesevaluesaredeterminedwhenyoumaketheinstance,andtheycanbedifferentforeachinstanceofanenumerationcase.Youcanthinkoftheassociatedvaluesasbehavinglikestoredpropertiesoftheenumerationcaseinstance.Forexample,considerthecaseofrequestingthesunriseandsunsettimesfromaserver.Theservereitherrespondswiththerequestedinformation,oritrespondswithadescriptionofwhatwentwrong.

1 enumServerResponse{

2 caseresult(String,String)

3 casefailure(String)

4 }

5

6 letsuccess=ServerResponse.result("6:00am","8:09pm")

7 letfailure=ServerResponse.failure("Outofcheese.")

8

9 switchsuccess{

10 caselet.result(sunrise,sunset):

11 print("Sunriseisat\(sunrise)andsunsetisat\

(sunset).")

12 caselet.failure(message):

13 print("Failure...\(message)")

14 }

15 //Prints"Sunriseisat6:00amandsunsetisat8:09pm."

EXPER IMENT

AddathirdcasetoServerResponseandtotheswitch.

NoticehowthesunriseandsunsettimesareextractedfromtheServerResponsevalueaspartofmatchingthevalueagainsttheswitchcases.

Usestructtocreateastructure.Structuressupportmanyofthesamebehaviorsasclasses,includingmethodsandinitializers.Oneofthemostimportantdifferencesbetweenstructuresandclassesisthatstructuresarealwayscopiedwhentheyarepassedaroundinyourcode,butclassesarepassedbyreference.

1 structCard{

2 varrank:Rank

3 varsuit:Suit

4 funcsimpleDescription()->String{

5 return"The\(rank.simpleDescription())of\

(suit.simpleDescription())"

6 }

7 }

8 letthreeOfSpades=Card(rank:.three,suit:.spades)

9 letthreeOfSpadesDescription=

threeOfSpades.simpleDescription()

EXPER IMENT

Writeafunctionthatreturnsanarraycontainingafulldeckofcards,withonecardofeachcombinationofrankandsuit.

ProtocolsandExtensions

Useprotocoltodeclareaprotocol.

1 protocolExampleProtocol{

2 varsimpleDescription:String{get}

3 mutatingfuncadjust()

4 }

Classes,enumerations,andstructscanalladoptprotocols.

1 classSimpleClass:ExampleProtocol{

2 varsimpleDescription:String="Averysimpleclass."

3 varanotherProperty:Int=69105

4 funcadjust(){

5 simpleDescription+="Now100%adjusted."

6 }

7 }

8 vara=SimpleClass()

9 a.adjust()

10 letaDescription=a.simpleDescription

11

12 structSimpleStructure:ExampleProtocol{

13 varsimpleDescription:String="Asimplestructure"

14 mutatingfuncadjust(){

15 simpleDescription+="(adjusted)"

16 }

17 }

18 varb=SimpleStructure()

19 b.adjust()

20 letbDescription=b.simpleDescription

EXPER IMENT

AddanotherrequirementtoExampleProtocol.WhatchangesdoyouneedtomaketoSimpleClassandSimpleStructuresothattheystillconformtotheprotocol?

NoticetheuseofthemutatingkeywordinthedeclarationofSimpleStructuretomarkamethodthatmodifiesthestructure.ThedeclarationofSimpleClassdoesn’tneedanyofitsmethodsmarkedasmutatingbecausemethodsonaclasscanalwaysmodifytheclass.

Useextensiontoaddfunctionalitytoanexistingtype,suchasnewmethodsandcomputedproperties.Youcanuseanextensiontoaddprotocolconformancetoatypethatisdeclaredelsewhere,oreventoatypethatyouimportedfromalibraryorframework.

1 extensionInt:ExampleProtocol{

2 varsimpleDescription:String{

3 return"Thenumber\(self)"

4 }

5 mutatingfuncadjust(){

6 self+=42

7 }

8 }

9 print(7.simpleDescription)

10 //Prints"Thenumber7"

EXPER IMENT

WriteanextensionfortheDoubletypethataddsanabsoluteValueproperty.

Youcanuseaprotocolnamejustlikeanyothernamedtype—forexample,tocreateacollectionofobjectsthathavedifferenttypesbutthatallconformtoasingleprotocol.Whenyouworkwithvalueswhosetypeisaprotocoltype,methodsoutsidetheprotocoldefinitionarenotavailable.

1 letprotocolValue:ExampleProtocol=a

2 print(protocolValue.simpleDescription)

3 //Prints"Averysimpleclass.Now100%adjusted."

4 //print(protocolValue.anotherProperty)//Uncommenttosee

theerror

EventhoughthevariableprotocolValuehasaruntimetypeofSimpleClass,thecompilertreatsitasthegiventypeofExampleProtocol.Thismeansthatyoucan’taccidentallyaccessmethodsorpropertiesthattheclassimplementsinadditiontoitsprotocolconformance.

ErrorHandling

YourepresenterrorsusinganytypethatadoptstheErrorprotocol.

1 enumPrinterError:Error{

2 caseoutOfPaper

3 casenoToner

4 caseonFire

5 }

Usethrowtothrowanerrorandthrowstomarkafunctionthatcanthrowanerror.Ifyouthrowanerrorinafunction,thefunctionreturnsimmediatelyandthecodethatcalledthefunctionhandlestheerror.

1 funcsend(job:Int,toPrinterprinterName:String)throws->

String{

2 ifprinterName=="NeverHasToner"{

3 throwPrinterError.noToner

4 }

5 return"Jobsent"

6 }

Thereareseveralwaystohandleerrors.Onewayistousedo-catch.Insidethedoblock,youmarkcodethatcanthrowanerrorbywritingtryinfrontofit.Insidethecatchblock,theerrorisautomaticallygiventhenameerrorunlessyougiveitadifferentname.

1 do{

2 letprinterResponse=trysend(job:1040,toPrinter:"Bi

Sheng")

3 print(printerResponse)

4 }catch{

5 print(error)

6 }

7 //Prints"Jobsent"

EXPER IMENT

Changetheprinternameto"NeverHasToner",sothatthesend(job:toPrinter:)functionthrowsanerror.

Youcanprovidemultiplecatchblocksthathandlespecificerrors.Youwriteapatternaftercatchjustasyoudoaftercaseinaswitch.

1 do{

2 letprinterResponse=trysend(job:1440,toPrinter:

"Gutenberg")

3 print(printerResponse)

4 }catchPrinterError.onFire{

5 print("I'lljustputthisoverhere,withtherestofthe

fire.")

6 }catchletprinterErrorasPrinterError{

7 print("Printererror:\(printerError).")

8 }catch{

9 print(error)

10 }

11 //Prints"Jobsent"

EXPER IMENT

Addcodetothrowanerrorinsidethedoblock.Whatkindoferrordoyouneedtothrowsothattheerrorishandledbythefirstcatchblock?Whataboutthesecondandthirdblocks?

Anotherwaytohandleerrorsistousetry?toconverttheresulttoanoptional.Ifthefunctionthrowsanerror,thespecificerrorisdiscardedandtheresultisnil.Otherwise,theresultisanoptionalcontainingthevaluethatthefunctionreturned.

1 letprinterSuccess=try?send(job:1884,toPrinter:

"Mergenthaler")

2 letprinterFailure=try?send(job:1885,toPrinter:"NeverHas

Toner")

Usedefertowriteablockofcodethatisexecutedafterallothercodeinthefunction,justbeforethefunctionreturns.Thecodeisexecutedregardlessofwhetherthefunctionthrowsanerror.Youcanusedefertowritesetupandcleanupcodenexttoeachother,eventhoughtheyneedtobeexecutedatdifferenttimes.

1 varfridgeIsOpen=false

2 letfridgeContent=["milk","eggs","leftovers"]

3

4 funcfridgeContains(_food:String)->Bool{

5 fridgeIsOpen=true

6 defer{

7 fridgeIsOpen=false

8 }

9

10 letresult=fridgeContent.contains(food)

11 returnresult

12 }

13 fridgeContains("banana")

14 print(fridgeIsOpen)

15 //Prints"false"

Generics

Writeanameinsideanglebracketstomakeagenericfunctionortype.

1 funcmakeArray<Item>(repeatingitem:Item,numberOfTimes:Int)

->[Item]{

2 varresult=[Item]()

3 for_in0..<numberOfTimes{

4 result.append(item)

5 }

6 returnresult

7 }

8 makeArray(repeating:"knock",numberOfTimes:4)

Youcanmakegenericformsoffunctionsandmethods,aswellasclasses,

enumerations,andstructures.

1 //ReimplementtheSwiftstandardlibrary'soptionaltype

2 enumOptionalValue<Wrapped>{

3 casenone

4 casesome(Wrapped)

5 }

6 varpossibleInteger:OptionalValue<Int>=.none

7 possibleInteger=.some(100)

Usewhererightbeforethebodytospecifyalistofrequirements—forexample,torequirethetypetoimplementaprotocol,torequiretwotypestobethesame,ortorequireaclasstohaveaparticularsuperclass.

1 funcanyCommonElements<T:Sequence,U:Sequence>(_lhs:T,_

rhs:U)->Bool

2 whereT.Element:Equatable,T.Element==U.Element

3 {

4 forlhsIteminlhs{

5 forrhsIteminrhs{

6 iflhsItem==rhsItem{

7 returntrue

8 }

9 }

10 }

11 returnfalse

12 }

13 anyCommonElements([1,2,3],[3])

EXPER IMENT

ModifytheanyCommonElements(_:_:)functiontomakeafunctionthatreturnsanarrayoftheelementsthatanytwosequenceshaveincommon.

Writing<T:Equatable>isthesameaswriting<T>...whereT:Equatable.

LanguageGuide

TheBasics

SwiftisanewprogramminglanguageforiOS,macOS,watchOS,andtvOSappdevelopment.Nonetheless,manypartsofSwiftwillbefamiliarfromyourexperienceofdevelopinginCandObjective-C.

SwiftprovidesitsownversionsofallfundamentalCandObjective-Ctypes,includingIntforintegers,DoubleandFloatforfloating-pointvalues,BoolforBooleanvalues,andStringfortextualdata.Swiftalsoprovidespowerfulversionsofthethreeprimarycollectiontypes,Array,Set,andDictionary,asdescribedinCollectionTypes.

LikeC,Swiftusesvariablestostoreandrefertovaluesbyanidentifyingname.Swiftalsomakesextensiveuseofvariableswhosevaluescan’tbechanged.Theseareknownasconstants,andaremuchmorepowerfulthanconstantsinC.ConstantsareusedthroughoutSwifttomakecodesaferandclearerinintentwhenyouworkwithvaluesthatdon’tneedtochange.

Inadditiontofamiliartypes,SwiftintroducesadvancedtypesnotfoundinObjective-C,suchastuples.Tuplesenableyoutocreateandpassaroundgroupingsofvalues.Youcanuseatupletoreturnmultiplevaluesfromafunctionasasinglecompoundvalue.

Swiftalsointroducesoptionaltypes,whichhandletheabsenceofavalue.Optionalssayeither“thereisavalue,anditequalsx”or“thereisn’tavalueatall”.UsingoptionalsissimilartousingnilwithpointersinObjective-C,buttheyworkforanytype,notjustclasses.NotonlyareoptionalssaferandmoreexpressivethannilpointersinObjective-C,they’reattheheartofmanyofSwift’smostpowerfulfeatures.

Swiftisatype-safelanguage,whichmeansthelanguagehelpsyoutobeclearaboutthetypesofvaluesyourcodecanworkwith.IfpartofyourcoderequiresaString,typesafetypreventsyoufrompassingitanIntbymistake.Likewise,typesafetypreventsyoufromaccidentallypassinganoptionalStringtoapieceofcodethatrequiresanon-optionalString.Typesafetyhelpsyoucatchandfixerrorsasearlyaspossibleinthedevelopmentprocess.

ConstantsandVariables

Constantsandvariablesassociateaname(suchasmaximumNumberOfLoginAttemptsorwelcomeMessage)withavalueofaparticulartype(suchasthenumber10orthestring"Hello").Thevalueofaconstantcan’tbechangedonceit’sset,whereasavariablecanbesettoadifferentvalueinthefuture.

DeclaringConstantsandVariablesConstantsandvariablesmustbedeclaredbeforethey’reused.Youdeclareconstantswiththeletkeywordandvariableswiththevarkeyword.Here’sanexampleofhowconstantsandvariablescanbeusedtotrackthenumberofloginattemptsauserhasmade:

1 letmaximumNumberOfLoginAttempts=10

2 varcurrentLoginAttempt=0

Thiscodecanbereadas:

“DeclareanewconstantcalledmaximumNumberOfLoginAttempts,andgiveitavalueof10.Then,declareanewvariablecalledcurrentLoginAttempt,andgiveitaninitialvalueof0.”

Inthisexample,themaximumnumberofallowedloginattemptsisdeclaredasaconstant,becausethemaximumvalueneverchanges.Thecurrentloginattemptcounterisdeclaredasavariable,becausethisvaluemustbeincrementedaftereachfailedloginattempt.

Youcandeclaremultipleconstantsormultiplevariablesonasingleline,separatedbycommas:

varx=0.0,y=0.0,z=0.0

NOTE

Ifastoredvalueinyourcodewon’tchange,alwaysdeclareitasaconstantwiththeletkeyword.

Usevariablesonlyforstoringvaluesthatneedtobeabletochange.

TypeAnnotationsYoucanprovideatypeannotationwhenyoudeclareaconstantorvariable,tobeclearaboutthekindofvaluestheconstantorvariablecanstore.Writeatypeannotationbyplacingacolonaftertheconstantorvariablename,followedbyaspace,followedbythenameofthetypetouse.

ThisexampleprovidesatypeannotationforavariablecalledwelcomeMessage,toindicatethatthevariablecanstoreStringvalues:

varwelcomeMessage:String

Thecoloninthedeclarationmeans“…oftype…,”sothecodeabovecanbereadas:

“DeclareavariablecalledwelcomeMessagethatisoftypeString.”

Thephrase“oftypeString”means“canstoreanyStringvalue.”Thinkofitasmeaning“thetypeofthing”(or“thekindofthing”)thatcanbestored.

ThewelcomeMessagevariablecannowbesettoanystringvaluewithouterror:

welcomeMessage="Hello"

Youcandefinemultiplerelatedvariablesofthesametypeonasingleline,separatedbycommas,withasingletypeannotationafterthefinalvariablename:

varred,green,blue:Double

NOTE

It’srarethatyouneedtowritetypeannotationsinpractice.Ifyouprovideaninitialvalueforaconstantorvariableatthepointthatit’sdefined,Swiftcanalmostalwaysinferthetypetobeusedforthatconstantorvariable,asdescribedinTypeSafetyandTypeInference.InthewelcomeMessageexampleabove,noinitialvalueisprovided,andsothetypeofthewelcomeMessagevariableis

specifiedwithatypeannotationratherthanbeinginferredfromaninitialvalue.

NamingConstantsandVariablesConstantandvariablenamescancontainalmostanycharacter,includingUnicodecharacters:

1 letπ=3.14159

2 let =""

3 let ="dogcow"

Constantandvariablenamescan’tcontainwhitespacecharacters,mathematicalsymbols,arrows,private-useUnicodescalarvalues,orline-andbox-drawingcharacters.Norcantheybeginwithanumber,althoughnumbersmaybeincludedelsewherewithinthename.

Onceyou’vedeclaredaconstantorvariableofacertaintype,youcan’tdeclareitagainwiththesamename,orchangeittostorevaluesofadifferenttype.Norcanyouchangeaconstantintoavariableoravariableintoaconstant.

NOTE

IfyouneedtogiveaconstantorvariablethesamenameasareservedSwiftkeyword,surroundthekeywordwithbackticks(`)whenusingitasaname.However,avoidusingkeywordsasnamesunlessyouhaveabsolutelynochoice.

Youcanchangethevalueofanexistingvariabletoanothervalueofacompatibletype.Inthisexample,thevalueoffriendlyWelcomeischangedfrom"Hello!"to"Bonjour!":

1 varfriendlyWelcome="Hello!"

2 friendlyWelcome="Bonjour!"

3 //friendlyWelcomeisnow"Bonjour!"

Unlikeavariable,thevalueofaconstantcan’tbechangedafterit’sset.

Attemptingtodosoisreportedasanerrorwhenyourcodeiscompiled:

1 letlanguageName="Swift"

2 languageName="Swift++"

3 //Thisisacompile-timeerror:languageNamecannotbe

changed.

PrintingConstantsandVariablesYoucanprintthecurrentvalueofaconstantorvariablewiththeprint(_:separator:terminator:)function:

1 print(friendlyWelcome)

2 //Prints"Bonjour!"

Theprint(_:separator:terminator:)functionisaglobalfunctionthatprintsoneormorevaluestoanappropriateoutput.InXcode,forexample,theprint(_:separator:terminator:)functionprintsitsoutputinXcode’s“console”pane.Theseparatorandterminatorparameterhavedefaultvalues,soyoucanomitthemwhenyoucallthisfunction.Bydefault,thefunctionterminatesthelineitprintsbyaddingalinebreak.Toprintavaluewithoutalinebreakafterit,passanemptystringastheterminator—forexample,print(someValue,terminator:"").Forinformationaboutparameterswithdefaultvalues,seeDefaultParameterValues.

Swiftusesstringinterpolationtoincludethenameofaconstantorvariableasaplaceholderinalongerstring,andtopromptSwifttoreplaceitwiththecurrentvalueofthatconstantorvariable.Wrapthenameinparenthesesandescapeitwithabackslashbeforetheopeningparenthesis:

1 print("ThecurrentvalueoffriendlyWelcomeis\

(friendlyWelcome)")

2 //Prints"ThecurrentvalueoffriendlyWelcomeisBonjour!"

NOTE

AlloptionsyoucanusewithstringinterpolationaredescribedinStringInterpolation.

Comments

Usecommentstoincludenonexecutabletextinyourcode,asanoteorremindertoyourself.CommentsareignoredbytheSwiftcompilerwhenyourcodeiscompiled.

CommentsinSwiftareverysimilartocommentsinC.Single-linecommentsbeginwithtwoforward-slashes(//):

//Thisisacomment.

Multilinecommentsstartwithaforward-slashfollowedbyanasterisk(/*)andendwithanasteriskfollowedbyaforward-slash(*/):

1 /*Thisisalsoacomment

2 butiswrittenovermultiplelines.*/

UnlikemultilinecommentsinC,multilinecommentsinSwiftcanbenestedinsideothermultilinecomments.Youwritenestedcommentsbystartingamultilinecommentblockandthenstartingasecondmultilinecommentwithinthefirstblock.Thesecondblockisthenclosed,followedbythefirstblock:

1 /*Thisisthestartofthefirstmultilinecomment.

2 /*Thisisthesecond,nestedmultilinecomment.*/

3 Thisistheendofthefirstmultilinecomment.*/

Nestedmultilinecommentsenableyoutocommentoutlargeblocksofcodequicklyandeasily,evenifthecodealreadycontainsmultilinecomments.

Semicolons

Unlikemanyotherlanguages,Swiftdoesn’trequireyoutowriteasemicolon(;)aftereachstatementinyourcode,althoughyoucandosoifyouwish.However,semicolonsarerequiredifyouwanttowritemultipleseparatestatementsonasingleline:

1 letcat=" ";print(cat)

2 //Prints" "

Integers

Integersarewholenumberswithnofractionalcomponent,suchas42and-23.Integersareeithersigned(positive,zero,ornegative)orunsigned(positiveorzero).

Swiftprovidessignedandunsignedintegersin8,16,32,and64bitforms.TheseintegersfollowanamingconventionsimilartoC,inthatan8-bitunsignedintegerisoftypeUInt8,anda32-bitsignedintegerisoftypeInt32.LikealltypesinSwift,theseintegertypeshavecapitalizednames.

IntegerBoundsYoucanaccesstheminimumandmaximumvaluesofeachintegertypewithitsminandmaxproperties:

1 letminValue=UInt8.min//minValueisequalto0,andisof

typeUInt8

2 letmaxValue=UInt8.max//maxValueisequalto255,andis

oftypeUInt8

Thevaluesofthesepropertiesareoftheappropriate-sizednumbertype(suchasUInt8intheexampleabove)andcanthereforebeusedinexpressionsalongside

othervaluesofthesametype.

IntInmostcases,youdon’tneedtopickaspecificsizeofintegertouseinyourcode.Swiftprovidesanadditionalintegertype,Int,whichhasthesamesizeasthecurrentplatform’snativewordsize:

Ona32-bitplatform,IntisthesamesizeasInt32.

Ona64-bitplatform,IntisthesamesizeasInt64.

Unlessyouneedtoworkwithaspecificsizeofinteger,alwaysuseIntforintegervaluesinyourcode.Thisaidscodeconsistencyandinteroperability.Evenon32-bitplatforms,Intcanstoreanyvaluebetween-2,147,483,648and2,147,483,647,andislargeenoughformanyintegerranges.

UIntSwiftalsoprovidesanunsignedintegertype,UInt,whichhasthesamesizeasthecurrentplatform’snativewordsize:

Ona32-bitplatform,UIntisthesamesizeasUInt32.

Ona64-bitplatform,UIntisthesamesizeasUInt64.

NOTE

UseUIntonlywhenyouspecificallyneedanunsignedintegertypewiththesamesizeastheplatform’snativewordsize.Ifthisisn’tthecase,Intispreferred,evenwhenthevaluestobestoredareknowntobenonnegative.AconsistentuseofIntforintegervaluesaidscodeinteroperability,avoidstheneedtoconvertbetweendifferentnumbertypes,andmatchesintegertypeinference,asdescribedinTypeSafetyandTypeInference.

Floating-PointNumbers

Floating-pointnumbersarenumberswithafractionalcomponent,suchas3.14159,0.1,and-273.15.

Floating-pointtypescanrepresentamuchwiderrangeofvaluesthanintegertypes,andcanstorenumbersthataremuchlargerorsmallerthancanbestoredinanInt.Swiftprovidestwosignedfloating-pointnumbertypes:

Doublerepresentsa64-bitfloating-pointnumber.

Floatrepresentsa32-bitfloating-pointnumber.

NOTE

Doublehasaprecisionofatleast15decimaldigits,whereastheprecisionofFloatcanbeaslittleas6decimaldigits.Theappropriatefloating-pointtypetousedependsonthenatureandrangeofvaluesyouneedtoworkwithinyourcode.Insituationswhereeithertypewouldbeappropriate,Doubleispreferred.

TypeSafetyandTypeInference

Swiftisatype-safelanguage.Atypesafelanguageencouragesyoutobeclearaboutthetypesofvaluesyourcodecanworkwith.IfpartofyourcoderequiresaString,youcan’tpassitanIntbymistake.

BecauseSwiftistypesafe,itperformstypecheckswhencompilingyourcodeandflagsanymismatchedtypesaserrors.Thisenablesyoutocatchandfixerrorsasearlyaspossibleinthedevelopmentprocess.

Type-checkinghelpsyouavoiderrorswhenyou’reworkingwithdifferenttypesofvalues.However,thisdoesn’tmeanthatyouhavetospecifythetypeofeveryconstantandvariablethatyoudeclare.Ifyoudon’tspecifythetypeofvalueyouneed,Swiftusestypeinferencetoworkouttheappropriatetype.Typeinferenceenablesacompilertodeducethetypeofaparticularexpressionautomaticallywhenitcompilesyourcode,simplybyexaminingthevaluesyouprovide.

Becauseoftypeinference,SwiftrequiresfarfewertypedeclarationsthanlanguagessuchasCorObjective-C.Constantsandvariablesarestillexplicitly

typed,butmuchoftheworkofspecifyingtheirtypeisdoneforyou.

Typeinferenceisparticularlyusefulwhenyoudeclareaconstantorvariablewithaninitialvalue.Thisisoftendonebyassigningaliteralvalue(orliteral)totheconstantorvariableatthepointthatyoudeclareit.(Aliteralvalueisavaluethatappearsdirectlyinyoursourcecode,suchas42and3.14159intheexamplesbelow.)

Forexample,ifyouassignaliteralvalueof42toanewconstantwithoutsayingwhattypeitis,SwiftinfersthatyouwanttheconstanttobeanInt,becauseyouhaveinitializeditwithanumberthatlookslikeaninteger:

1 letmeaningOfLife=42

2 //meaningOfLifeisinferredtobeoftypeInt

Likewise,ifyoudon’tspecifyatypeforafloating-pointliteral,SwiftinfersthatyouwanttocreateaDouble:

1 letpi=3.14159

2 //piisinferredtobeoftypeDouble

SwiftalwayschoosesDouble(ratherthanFloat)wheninferringthetypeoffloating-pointnumbers.

Ifyoucombineintegerandfloating-pointliteralsinanexpression,atypeofDoublewillbeinferredfromthecontext:

1 letanotherPi=3+0.14159

2 //anotherPiisalsoinferredtobeoftypeDouble

Theliteralvalueof3hasnoexplicittypeinandofitself,andsoanappropriateoutputtypeofDoubleisinferredfromthepresenceofafloating-pointliteralaspartoftheaddition.

NumericLiterals

Integerliteralscanbewrittenas:

Adecimalnumber,withnoprefix

Abinarynumber,witha0bprefix

Anoctalnumber,witha0oprefix

Ahexadecimalnumber,witha0xprefix

Alloftheseintegerliteralshaveadecimalvalueof17:

1 letdecimalInteger=17

2 letbinaryInteger=0b10001//17inbinarynotation

3 letoctalInteger=0o21//17inoctalnotation

4 lethexadecimalInteger=0x11//17inhexadecimalnotation

Floating-pointliteralscanbedecimal(withnoprefix),orhexadecimal(witha0xprefix).Theymustalwayshaveanumber(orhexadecimalnumber)onbothsidesofthedecimalpoint.Decimalfloatscanalsohaveanoptionalexponent,indicatedbyanuppercaseorlowercasee;hexadecimalfloatsmusthaveanexponent,indicatedbyanuppercaseorlowercasep.

Fordecimalnumberswithanexponentofexp,thebasenumberismultipliedby10exp:

1.25e2means1.25x102,or125.0.

1.25e-2means1.25x10-2,or0.0125.

Forhexadecimalnumberswithanexponentofexp,thebasenumberismultipliedby2exp:

0xFp2means15x22,or60.0.

0xFp-2means15x2-2,or3.75.

Allofthesefloating-pointliteralshaveadecimalvalueof12.1875:

1 letdecimalDouble=12.1875

2 letexponentDouble=1.21875e1

3 lethexadecimalDouble=0xC.3p0

Numericliteralscancontainextraformattingtomakethemeasiertoread.Bothintegersandfloatscanbepaddedwithextrazerosandcancontainunderscorestohelpwithreadability.Neithertypeofformattingaffectstheunderlyingvalueoftheliteral:

1 letpaddedDouble=000123.456

2 letoneMillion=1_000_000

3 letjustOverOneMillion=1_000_000.000_000_1

NumericTypeConversion

UsetheInttypeforallgeneral-purposeintegerconstantsandvariablesinyourcode,evenifthey’reknowntobenonnegative.Usingthedefaultintegertypeineverydaysituationsmeansthatintegerconstantsandvariablesareimmediatelyinteroperableinyourcodeandwillmatchtheinferredtypeforintegerliteralvalues.

Useotherintegertypesonlywhenthey’respecificallyneededforthetaskathand,becauseofexplicitlysizeddatafromanexternalsource,orforperformance,memoryusage,orothernecessaryoptimization.Usingexplicitlysizedtypesinthesesituationshelpstocatchanyaccidentalvalueoverflowsandimplicitlydocumentsthenatureofthedatabeingused.

IntegerConversion

Therangeofnumbersthatcanbestoredinanintegerconstantorvariableisdifferentforeachnumerictype.AnInt8constantorvariablecanstorenumbersbetween-128and127,whereasaUInt8constantorvariablecanstorenumbersbetween0and255.Anumberthatwon’tfitintoaconstantorvariableofasizedintegertypeisreportedasanerrorwhenyourcodeiscompiled:

1 letcannotBeNegative:UInt8=-1

2 //UInt8cannotstorenegativenumbers,andsothiswillreport

anerror

3 lettooBig:Int8=Int8.max+1

4 //Int8cannotstoreanumberlargerthanitsmaximumvalue,

5 //andsothiswillalsoreportanerror

Becauseeachnumerictypecanstoreadifferentrangeofvalues,youmustoptintonumerictypeconversiononacase-by-casebasis.Thisopt-inapproachpreventshiddenconversionerrorsandhelpsmaketypeconversionintentionsexplicitinyourcode.

Toconvertonespecificnumbertypetoanother,youinitializeanewnumberofthedesiredtypewiththeexistingvalue.Intheexamplebelow,theconstanttwoThousandisoftypeUInt16,whereastheconstantoneisoftypeUInt8.Theycan’tbeaddedtogetherdirectly,becausethey’renotofthesametype.Instead,thisexamplecallsUInt16(one)tocreateanewUInt16initializedwiththevalueofone,andusesthisvalueinplaceoftheoriginal:

1 lettwoThousand:UInt16=2_000

2 letone:UInt8=1

3 lettwoThousandAndOne=twoThousand+UInt16(one)

BecausebothsidesoftheadditionarenowoftypeUInt16,theadditionisallowed.Theoutputconstant(twoThousandAndOne)isinferredtobeoftypeUInt16,becauseit’sthesumoftwoUInt16values.

SomeType(ofInitialValue)isthedefaultwaytocalltheinitializerofaSwifttypeandpassinaninitialvalue.Behindthescenes,UInt16hasaninitializerthat

acceptsaUInt8value,andsothisinitializerisusedtomakeanewUInt16fromanexistingUInt8.Youcan’tpassinanytypehere,however—ithastobeatypeforwhichUInt16providesaninitializer.Extendingexistingtypestoprovideinitializersthatacceptnewtypes(includingyourowntypedefinitions)iscoveredinExtensions.

IntegerandFloating-PointConversionConversionsbetweenintegerandfloating-pointnumerictypesmustbemadeexplicit:

1 letthree=3

2 letpointOneFourOneFiveNine=0.14159

3 letpi=Double(three)+pointOneFourOneFiveNine

4 //piequals3.14159,andisinferredtobeoftypeDouble

Here,thevalueoftheconstantthreeisusedtocreateanewvalueoftypeDouble,sothatbothsidesoftheadditionareofthesametype.Withoutthisconversioninplace,theadditionwouldnotbeallowed.

Floating-pointtointegerconversionmustalsobemadeexplicit.AnintegertypecanbeinitializedwithaDoubleorFloatvalue:

1 letintegerPi=Int(pi)

2 //integerPiequals3,andisinferredtobeoftypeInt

Floating-pointvaluesarealwaystruncatedwhenusedtoinitializeanewintegervalueinthisway.Thismeansthat4.75becomes4,and-3.9becomes-3.

NOTE

Therulesforcombiningnumericconstantsandvariablesaredifferentfromtherulesfornumericliterals.Theliteralvalue3canbeaddeddirectlytotheliteralvalue0.14159,becausenumberliteralsdon’thaveanexplicittypeinandofthemselves.Theirtypeisinferredonlyatthepointthatthey’reevaluatedbythecompiler.

TypeAliases

Typealiasesdefineanalternativenameforanexistingtype.Youdefinetypealiaseswiththetypealiaskeyword.

Typealiasesareusefulwhenyouwanttorefertoanexistingtypebyanamethatiscontextuallymoreappropriate,suchaswhenworkingwithdataofaspecificsizefromanexternalsource:

typealiasAudioSample=UInt16

Onceyoudefineatypealias,youcanusethealiasanywhereyoumightusetheoriginalname:

1 varmaxAmplitudeFound=AudioSample.min

2 //maxAmplitudeFoundisnow0

Here,AudioSampleisdefinedasanaliasforUInt16.Becauseit’sanalias,thecalltoAudioSample.minactuallycallsUInt16.min,whichprovidesaninitialvalueof0forthemaxAmplitudeFoundvariable.

Booleans

SwifthasabasicBooleantype,calledBool.Booleanvaluesarereferredtoaslogical,becausetheycanonlyeverbetrueorfalse.SwiftprovidestwoBooleanconstantvalues,trueandfalse:

1 letorangesAreOrange=true

2 letturnipsAreDelicious=false

ThetypesoforangesAreOrangeandturnipsAreDelicioushavebeeninferredasBoolfromthefactthattheywereinitializedwithBooleanliteralvalues.AswithIntandDoubleabove,youdon’tneedtodeclareconstantsorvariablesasBoolifyousetthemtotrueorfalseassoonasyoucreatethem.Typeinferencehelps

makeSwiftcodemoreconciseandreadablewhenitinitializesconstantsorvariableswithothervalueswhosetypeisalreadyknown.

Booleanvaluesareparticularlyusefulwhenyouworkwithconditionalstatementssuchastheifstatement:

1 ifturnipsAreDelicious{

2 print("Mmm,tastyturnips!")

3 }else{

4 print("Eww,turnipsarehorrible.")

5 }

6 //Prints"Eww,turnipsarehorrible."

ConditionalstatementssuchastheifstatementarecoveredinmoredetailinControlFlow.

Swift’stypesafetypreventsnon-BooleanvaluesfrombeingsubstitutedforBool.Thefollowingexamplereportsacompile-timeerror:

1 leti=1

2 ifi{

3 //thisexamplewillnotcompile,andwillreportanerror

4 }

However,thealternativeexamplebelowisvalid:

1 leti=1

2 ifi==1{

3 //thisexamplewillcompilesuccessfully

4 }

Theresultofthei==1comparisonisoftypeBool,andsothissecondexamplepassesthetype-check.Comparisonslikei==1arediscussedinBasicOperators.

AswithotherexamplesoftypesafetyinSwift,thisapproachavoidsaccidentalerrorsandensuresthattheintentionofaparticularsectionofcodeisalwaysclear.

Tuples

Tuplesgroupmultiplevaluesintoasinglecompoundvalue.Thevalueswithinatuplecanbeofanytypeanddon’thavetobeofthesametypeaseachother.

Inthisexample,(404,"NotFound")isatuplethatdescribesanHTTPstatuscode.AnHTTPstatuscodeisaspecialvaluereturnedbyawebserverwheneveryourequestawebpage.Astatuscodeof404NotFoundisreturnedifyourequestawebpagethatdoesn’texist.

1 lethttp404Error=(404,"NotFound")

2 //http404Errorisoftype(Int,String),andequals(404,"Not

Found")

The(404,"NotFound")tuplegroupstogetheranIntandaStringtogivetheHTTPstatuscodetwoseparatevalues:anumberandahuman-readabledescription.Itcanbedescribedas“atupleoftype(Int,String)”.

Youcancreatetuplesfromanypermutationoftypes,andtheycancontainasmanydifferenttypesasyoulike.There’snothingstoppingyoufromhavingatupleoftype(Int,Int,Int),or(String,Bool),orindeedanyotherpermutationyourequire.

Youcandecomposeatuple’scontentsintoseparateconstantsorvariables,whichyouthenaccessasusual:

1 let(statusCode,statusMessage)=http404Error

2 print("Thestatuscodeis\(statusCode)")

3 //Prints"Thestatuscodeis404"

4 print("Thestatusmessageis\(statusMessage)")

5 //Prints"ThestatusmessageisNotFound"

Ifyouonlyneedsomeofthetuple’svalues,ignorepartsofthetuplewithanunderscore(_)whenyoudecomposethetuple:

1 let(justTheStatusCode,_)=http404Error

2 print("Thestatuscodeis\(justTheStatusCode)")

3 //Prints"Thestatuscodeis404"

Alternatively,accesstheindividualelementvaluesinatupleusingindexnumbersstartingatzero:

1 print("Thestatuscodeis\(http404Error.0)")

2 //Prints"Thestatuscodeis404"

3 print("Thestatusmessageis\(http404Error.1)")

4 //Prints"ThestatusmessageisNotFound"

Youcannametheindividualelementsinatuplewhenthetupleisdefined:

lethttp200Status=(statusCode:200,description:"OK")

Ifyounametheelementsinatuple,youcanusetheelementnamestoaccessthevaluesofthoseelements:

1 print("Thestatuscodeis\(http200Status.statusCode)")

2 //Prints"Thestatuscodeis200"

3 print("Thestatusmessageis\(http200Status.description)")

4 //Prints"ThestatusmessageisOK"

Tuplesareparticularlyusefulasthereturnvaluesoffunctions.Afunctionthattriestoretrieveawebpagemightreturnthe(Int,String)tupletypetodescribethesuccessorfailureofthepageretrieval.Byreturningatuplewithtwodistinctvalues,eachofadifferenttype,thefunctionprovidesmoreusefulinformationaboutitsoutcomethanifitcouldonlyreturnasinglevalueofasingletype.For

moreinformation,seeFunctionswithMultipleReturnValues.

NOTE

Tuplesareusefulforsimplegroupsofrelatedvalues.They’renotsuitedtothecreationofcomplexdatastructures.Ifyourdatastructureislikelytobemorecomplex,modelitasaclassorstructure,ratherthanasatuple.Formoreinformation,seeStructuresandClasses.

Optionals

Youuseoptionalsinsituationswhereavaluemaybeabsent.Anoptionalrepresentstwopossibilities:Eitherthereisavalue,andyoucanunwraptheoptionaltoaccessthatvalue,orthereisn’tavalueatall.

NOTE

Theconceptofoptionalsdoesn’texistinCorObjective-C.ThenearestthinginObjective-Cistheabilitytoreturnnilfromamethodthatwouldotherwisereturnanobject,withnilmeaning“theabsenceofavalidobject.”However,thisonlyworksforobjects—itdoesn’tworkforstructures,basicCtypes,orenumerationvalues.Forthesetypes,Objective-Cmethodstypicallyreturnaspecialvalue(suchasNSNotFound)toindicatetheabsenceofavalue.Thisapproachassumesthatthemethod’scallerknowsthere’saspecialvaluetotestagainstandrememberstocheckforit.Swift’soptionalsletyouindicatetheabsenceofavalueforanytypeatall,withouttheneedforspecialconstants.

Here’sanexampleofhowoptionalscanbeusedtocopewiththeabsenceofavalue.Swift’sInttypehasaninitializerwhichtriestoconvertaStringvalueintoanIntvalue.However,noteverystringcanbeconvertedintoaninteger.Thestring"123"canbeconvertedintothenumericvalue123,butthestring"hello,world"doesn’thaveanobviousnumericvaluetoconvertto.

TheexamplebelowusestheinitializertotrytoconvertaStringintoanInt:

1 letpossibleNumber="123"

2 letconvertedNumber=Int(possibleNumber)

3 //convertedNumberisinferredtobeoftype"Int?",or

"optionalInt"

Becausetheinitializermightfail,itreturnsanoptionalInt,ratherthananInt.AnoptionalIntiswrittenasInt?,notInt.Thequestionmarkindicatesthatthevalueitcontainsisoptional,meaningthatitmightcontainsomeIntvalue,oritmightcontainnovalueatall.(Itcan’tcontainanythingelse,suchasaBoolvalueoraStringvalue.It’seitheranInt,orit’snothingatall.)

nilYousetanoptionalvariabletoavaluelessstatebyassigningitthespecialvaluenil:

1 varserverResponseCode:Int?=404

2 //serverResponseCodecontainsanactualIntvalueof404

3 serverResponseCode=nil

4 //serverResponseCodenowcontainsnovalue

NOTE

Youcan’tusenilwithnon-optionalconstantsandvariables.Ifaconstantorvariableinyourcodeneedstoworkwiththeabsenceofavalueundercertainconditions,alwaysdeclareitasanoptionalvalueoftheappropriatetype.

Ifyoudefineanoptionalvariablewithoutprovidingadefaultvalue,thevariableisautomaticallysettonilforyou:

1 varsurveyAnswer:String?

2 //surveyAnswerisautomaticallysettonil

NOTE

Swift’snilisn’tthesameasnilinObjective-C.InObjective-C,nilisapointertoanonexistentobject.InSwift,nilisn’tapointer—it’stheabsenceofavalueofacertaintype.Optionalsofanytypecanbesettonil,notjustobjecttypes.

IfStatementsandForcedUnwrappingYoucanuseanifstatementtofindoutwhetheranoptionalcontainsavaluebycomparingtheoptionalagainstnil.Youperformthiscomparisonwiththe“equalto”operator(==)orthe“notequalto”operator(!=).

Ifanoptionalhasavalue,it’sconsideredtobe“notequalto”nil:

1 ifconvertedNumber!=nil{

2 print("convertedNumbercontainssomeintegervalue.")

3 }

4 //Prints"convertedNumbercontainssomeintegervalue."

Onceyou’resurethattheoptionaldoescontainavalue,youcanaccessitsunderlyingvaluebyaddinganexclamationmark(!)totheendoftheoptional’sname.Theexclamationmarkeffectivelysays,“Iknowthatthisoptionaldefinitelyhasavalue;pleaseuseit.”Thisisknownasforcedunwrappingoftheoptional’svalue:

1 ifconvertedNumber!=nil{

2 print("convertedNumberhasanintegervalueof\

(convertedNumber!).")

3 }

4 //Prints"convertedNumberhasanintegervalueof123."

Formoreabouttheifstatement,seeControlFlow.

NOTE

Tryingtouse!toaccessanonexistentoptionalvaluetriggersaruntimeerror.Alwaysmakesurethatanoptionalcontainsanon-nilvaluebeforeusing!toforce-unwrapitsvalue.

OptionalBindingYouuseoptionalbindingtofindoutwhetheranoptionalcontainsavalue,andif

so,tomakethatvalueavailableasatemporaryconstantorvariable.Optionalbindingcanbeusedwithifandwhilestatementstocheckforavalueinsideanoptional,andtoextractthatvalueintoaconstantorvariable,aspartofasingleaction.ifandwhilestatementsaredescribedinmoredetailinControlFlow.

Writeanoptionalbindingforanifstatementasfollows:

iflet constantName = someOptional {

statements

}

YoucanrewritethepossibleNumberexamplefromtheOptionalssectiontouseoptionalbindingratherthanforcedunwrapping:

1 ifletactualNumber=Int(possibleNumber){

2 print("Thestring\"\(possibleNumber)\"hasaninteger

valueof\(actualNumber)")

3 }else{

4 print("Thestring\"\(possibleNumber)\"couldnotbe

convertedtoaninteger")

5 }

6 //Prints"Thestring"123"hasanintegervalueof123"

Thiscodecanbereadas:

“IftheoptionalIntreturnedbyInt(possibleNumber)containsavalue,setanewconstantcalledactualNumbertothevaluecontainedintheoptional.”

Iftheconversionissuccessful,theactualNumberconstantbecomesavailableforusewithinthefirstbranchoftheifstatement.Ithasalreadybeeninitializedwiththevaluecontainedwithintheoptional,andsothere’snoneedtousethe!suffixtoaccessitsvalue.Inthisexample,actualNumberissimplyusedtoprinttheresultoftheconversion.

Youcanusebothconstantsandvariableswithoptionalbinding.Ifyouwantedto

manipulatethevalueofactualNumberwithinthefirstbranchoftheifstatement,youcouldwriteifvaractualNumberinstead,andthevaluecontainedwithintheoptionalwouldbemadeavailableasavariableratherthanaconstant.

YoucanincludeasmanyoptionalbindingsandBooleanconditionsinasingleifstatementasyouneedto,separatedbycommas.IfanyofthevaluesintheoptionalbindingsareniloranyBooleanconditionevaluatestofalse,thewholeifstatement’sconditionisconsideredtobefalse.Thefollowingifstatementsareequivalent:

1 ifletfirstNumber=Int("4"),letsecondNumber=Int("42"),

firstNumber<secondNumber&&secondNumber<100{

2 print("\(firstNumber)<\(secondNumber)<100")

3 }

4 //Prints"4<42<100"

5

6 ifletfirstNumber=Int("4"){

7 ifletsecondNumber=Int("42"){

8 iffirstNumber<secondNumber&&secondNumber<100{

9 print("\(firstNumber)<\(secondNumber)<100")

10 }

11 }

12 }

13 //Prints"4<42<100"

NOTE

Constantsandvariablescreatedwithoptionalbindinginanifstatementareavailableonlywithinthebodyoftheifstatement.Incontrast,theconstantsandvariablescreatedwithaguardstatementareavailableinthelinesofcodethatfollowtheguardstatement,asdescribedinEarlyExit.

ImplicitlyUnwrappedOptionals

Asdescribedabove,optionalsindicatethataconstantorvariableisallowedtohave“novalue”.Optionalscanbecheckedwithanifstatementtoseeifavalueexists,andcanbeconditionallyunwrappedwithoptionalbindingtoaccesstheoptional’svalueifitdoesexist.

Sometimesit’sclearfromaprogram’sstructurethatanoptionalwillalwayshaveavalue,afterthatvalueisfirstset.Inthesecases,it’susefultoremovetheneedtocheckandunwraptheoptional’svalueeverytimeit’saccessed,becauseitcanbesafelyassumedtohaveavalueallofthetime.

Thesekindsofoptionalsaredefinedasimplicitlyunwrappedoptionals.Youwriteanimplicitlyunwrappedoptionalbyplacinganexclamationmark(String!)ratherthanaquestionmark(String?)afterthetypethatyouwanttomakeoptional.

Implicitlyunwrappedoptionalsareusefulwhenanoptional’svalueisconfirmedtoexistimmediatelyaftertheoptionalisfirstdefinedandcandefinitelybeassumedtoexistateverypointthereafter.TheprimaryuseofimplicitlyunwrappedoptionalsinSwiftisduringclassinitialization,asdescribedinUnownedReferencesandImplicitlyUnwrappedOptionalProperties.

Animplicitlyunwrappedoptionalisanormaloptionalbehindthescenes,butcanalsobeusedlikeanon-optionalvalue,withouttheneedtounwraptheoptionalvalueeachtimeit’saccessed.ThefollowingexampleshowsthedifferenceinbehaviorbetweenanoptionalstringandanimplicitlyunwrappedoptionalstringwhenaccessingtheirwrappedvalueasanexplicitString:

1 letpossibleString:String?="Anoptionalstring."

2 letforcedString:String=possibleString!//requiresan

exclamationmark

3

4 letassumedString:String!="Animplicitlyunwrappedoptional

string."

5 letimplicitString:String=assumedString//noneedforan

exclamationmark

Youcanthinkofanimplicitlyunwrappedoptionalasgivingpermissionfortheoptionaltobeunwrappedautomaticallywheneverit’sused.Ratherthanplacinganexclamationmarkaftertheoptional’snameeachtimeyouuseit,youplaceanexclamationmarkaftertheoptional’stypewhenyoudeclareit.

NOTE

Ifanimplicitlyunwrappedoptionalisnilandyoutrytoaccessitswrappedvalue,you’lltriggeraruntimeerror.Theresultisexactlythesameasifyouplaceanexclamationmarkafteranormaloptionalthatdoesn’tcontainavalue.

Youcanstilltreatanimplicitlyunwrappedoptionallikeanormaloptional,tocheckifitcontainsavalue:

1 ifassumedString!=nil{

2 print(assumedString!)

3 }

4 //Prints"Animplicitlyunwrappedoptionalstring."

Youcanalsouseanimplicitlyunwrappedoptionalwithoptionalbinding,tocheckandunwrapitsvalueinasinglestatement:

1 ifletdefiniteString=assumedString{

2 print(definiteString)

3 }

4 //Prints"Animplicitlyunwrappedoptionalstring."

NOTE

Don’tuseanimplicitlyunwrappedoptionalwhenthere’sapossibilityofavariablebecomingnilatalaterpoint.Alwaysuseanormaloptionaltypeifyouneedtocheckforanilvalueduringthelifetimeofavariable.

ErrorHandling

Youuseerrorhandlingtorespondtoerrorconditionsyourprogrammayencounterduringexecution.

Incontrasttooptionals,whichcanusethepresenceorabsenceofavaluetocommunicatesuccessorfailureofafunction,errorhandlingallowsyoutodeterminetheunderlyingcauseoffailure,and,ifnecessary,propagatetheerrortoanotherpartofyourprogram.

Whenafunctionencountersanerrorcondition,itthrowsanerror.Thatfunction’scallercanthencatchtheerrorandrespondappropriately.

1 funccanThrowAnError()throws{

2 //thisfunctionmayormaynotthrowanerror

3 }

Afunctionindicatesthatitcanthrowanerrorbyincludingthethrowskeywordinitsdeclaration.Whenyoucallafunctionthatcanthrowanerror,youprependthetrykeywordtotheexpression.

Swiftautomaticallypropagateserrorsoutoftheircurrentscopeuntilthey’rehandledbyacatchclause.

1 do{

2 trycanThrowAnError()

3 //noerrorwasthrown

4 }catch{

5 //anerrorwasthrown

6 }

Adostatementcreatesanewcontainingscope,whichallowserrorstobepropagatedtooneormorecatchclauses.

Here’sanexampleofhowerrorhandlingcanbeusedtorespondtodifferenterrorconditions:

1 funcmakeASandwich()throws{

2 //...

3 }

4

5 do{

6 trymakeASandwich()

7 eatASandwich()

8 }catchSandwichError.outOfCleanDishes{

9 washDishes()

10 }catchSandwichError.missingIngredients(letingredients){

11 buyGroceries(ingredients)

12 }

Inthisexample,themakeASandwich()functionwillthrowanerrorifnocleandishesareavailableorifanyingredientsaremissing.BecausemakeASandwich()canthrowanerror,thefunctioncalliswrappedinatryexpression.Bywrappingthefunctioncallinadostatement,anyerrorsthatarethrownwillbepropagatedtotheprovidedcatchclauses.

Ifnoerroristhrown,theeatASandwich()functioniscalled.IfanerroristhrownanditmatchestheSandwichError.outOfCleanDishescase,thenthewashDishes()functionwillbecalled.IfanerroristhrownanditmatchestheSandwichError.missingIngredientscase,thenthebuyGroceries(_:)functioniscalledwiththeassociated[String]valuecapturedbythecatchpattern.

Throwing,catching,andpropagatingerrorsiscoveredingreaterdetailinErrorHandling.

AssertionsandPreconditions

Assertionsandpreconditionsarechecksthathappenatruntime.Youusethemtomakesureanessentialconditionissatisfiedbeforeexecutinganyfurthercode.If

theBooleanconditionintheassertionorpreconditionevaluatestotrue,codeexecutioncontinuesasusual.Iftheconditionevaluatestofalse,thecurrentstateoftheprogramisinvalid;codeexecutionends,andyourappisterminated.

Youuseassertionsandpreconditionstoexpresstheassumptionsyoumakeandtheexpectationsyouhavewhilecoding,soyoucanincludethemaspartofyourcode.Assertionshelpyoufindmistakesandincorrectassumptionsduringdevelopment,andpreconditionshelpyoudetectissuesinproduction.

Inadditiontoverifyingyourexpectationsatruntime,assertionsandpreconditionsalsobecomeausefulformofdocumentationwithinthecode.UnliketheerrorconditionsdiscussedinErrorHandlingabove,assertionsandpreconditionsaren’tusedforrecoverableorexpectederrors.Becauseafailedassertionorpreconditionindicatesaninvalidprogramstate,there’snowaytocatchafailedassertion.

Usingassertionsandpreconditionsisn’tasubstitutefordesigningyourcodeinsuchawaythatinvalidconditionsareunlikelytoarise.However,usingthemtoenforcevaliddataandstatecausesyourapptoterminatemorepredictablyifaninvalidstateoccurs,andhelpsmaketheproblemeasiertodebug.Stoppingexecutionassoonasaninvalidstateisdetectedalsohelpslimitthedamagecausedbythatinvalidstate.

Thedifferencebetweenassertionsandpreconditionsisinwhenthey’rechecked:Assertionsarecheckedonlyindebugbuilds,butpreconditionsarecheckedinbothdebugandproductionbuilds.Inproductionbuilds,theconditioninsideanassertionisn’tevaluated.Thismeansyoucanuseasmanyassertionsasyouwantduringyourdevelopmentprocess,withoutimpactingperformanceinproduction.

DebuggingwithAssertionsYouwriteanassertionbycallingtheassert(_:_:file:line:)functionfromtheSwiftstandardlibrary.Youpassthisfunctionanexpressionthatevaluatestotrueorfalseandamessagetodisplayiftheresultoftheconditionisfalse.Forexample:

1 letage=-3

2 assert(age>=0,"Aperson'sagecan'tbelessthanzero.")

3 //Thisassertionfailsbecause-3isnot>=0.

Inthisexample,codeexecutioncontinuesifage>=0evaluatestotrue,thatis,ifthevalueofageisnonnegative.Ifthevalueofageisnegative,asinthecodeabove,thenage>=0evaluatestofalse,andtheassertionfails,terminatingtheapplication.

Youcanomittheassertionmessage—forexample,whenitwouldjustrepeattheconditionasprose.

assert(age>=0)

Ifthecodealreadychecksthecondition,youusetheassertionFailure(_:file:line:)functiontoindicatethatanassertionhasfailed.Forexample:

1 ifage>10{

2 print("Youcanridetheroller-coasterortheferris

wheel.")

3 }elseifage>=0{

4 print("Youcanridetheferriswheel.")

5 }else{

6 assertionFailure("Aperson'sagecan'tbelessthanzero.")

7 }

EnforcingPreconditionsUseapreconditionwheneveraconditionhasthepotentialtobefalse,butmustdefinitelybetrueforyourcodetocontinueexecution.Forexample,useapreconditiontocheckthatasubscriptisnotoutofbounds,ortocheckthatafunctionhasbeenpassedavalidvalue.

Youwriteapreconditionbycallingtheprecondition(_:_:file:line:)function.

Youpassthisfunctionanexpressionthatevaluatestotrueorfalseandamessagetodisplayiftheresultoftheconditionisfalse.Forexample:

1 //Intheimplementationofasubscript...

2 precondition(index>0,"Indexmustbegreaterthanzero.")

YoucanalsocallthepreconditionFailure(_:file:line:)functiontoindicatethatafailurehasoccurred—forexample,ifthedefaultcaseofaswitchwastaken,butallvalidinputdatashouldhavebeenhandledbyoneoftheswitch’sothercases.

NOTE

Ifyoucompileinuncheckedmode(-Ounchecked),preconditionsaren’tchecked.Thecompilerassumesthatpreconditionsarealwaystrue,anditoptimizesyourcodeaccordingly.However,thefatalError(_:file:line:)functionalwayshaltsexecution,regardlessofoptimizationsettings.

YoucanusethefatalError(_:file:line:)functionduringprototypingandearlydevelopmenttocreatestubsforfunctionalitythathasn’tbeenimplementedyet,bywritingfatalError("Unimplemented")asthestubimplementation.Becausefatalerrorsareneveroptimizedout,unlikeassertionsorpreconditions,youcanbesurethatexecutionalwayshaltsifitencountersastubimplementation.

BasicOperators

Anoperatorisaspecialsymbolorphrasethatyouusetocheck,change,orcombinevalues.Forexample,theadditionoperator(+)addstwonumbers,asinleti=1+2,andthelogicalANDoperator(&&)combinestwoBooleanvalues,asinifenteredDoorCode&&passedRetinaScan.

SwiftsupportsmoststandardCoperatorsandimprovesseveralcapabilitiestoeliminatecommoncodingerrors.Theassignmentoperator(=)doesn’treturnavalue,topreventitfrombeingmistakenlyusedwhentheequaltooperator(==)isintended.Arithmeticoperators(+,-,*,/,%andsoforth)detectanddisallowvalueoverflow,toavoidunexpectedresultswhenworkingwithnumbersthatbecomelargerorsmallerthantheallowedvaluerangeofthetypethatstoresthem.YoucanoptintovalueoverflowbehaviorbyusingSwift’soverflowoperators,asdescribedinOverflowOperators.

Swiftalsoprovidesrangeoperatorsthataren’tfoundinC,suchasa..<banda...b,asashortcutforexpressingarangeofvalues.

ThischapterdescribesthecommonoperatorsinSwift.AdvancedOperatorscoversSwift’sadvancedoperators,anddescribeshowtodefineyourowncustomoperatorsandimplementthestandardoperatorsforyourowncustomtypes.

Terminology

Operatorsareunary,binary,orternary:

Unaryoperatorsoperateonasingletarget(suchas-a).Unaryprefixoperatorsappearimmediatelybeforetheirtarget(suchas!b),andunarypostfixoperatorsappearimmediatelyaftertheirtarget(suchasc!).

Binaryoperatorsoperateontwotargets(suchas2+3)andareinfixbecausetheyappearinbetweentheirtwotargets.

Ternaryoperatorsoperateonthreetargets.LikeC,Swifthasonlyoneternaryoperator,theternaryconditionaloperator(a?b:c).

Thevaluesthatoperatorsaffectareoperands.Intheexpression1+2,the+symbolisabinaryoperatoranditstwooperandsarethevalues1and2.

AssignmentOperator

Theassignmentoperator(a=b)initializesorupdatesthevalueofawiththevalueofb:

1 letb=10

2 vara=5

3 a=b

4 //aisnowequalto10

Iftherightsideoftheassignmentisatuplewithmultiplevalues,itselementscanbedecomposedintomultipleconstantsorvariablesatonce:

1 let(x,y)=(1,2)

2 //xisequalto1,andyisequalto2

UnliketheassignmentoperatorinCandObjective-C,theassignmentoperatorinSwiftdoesnotitselfreturnavalue.Thefollowingstatementisnotvalid:

1 ifx=y{

2 //Thisisnotvalid,becausex=ydoesnotreturna

value.

3 }

Thisfeaturepreventstheassignmentoperator(=)frombeingusedbyaccidentwhentheequaltooperator(==)isactuallyintended.Bymakingifx=yinvalid,Swifthelpsyoutoavoidthesekindsoferrorsinyourcode.

ArithmeticOperators

Swiftsupportsthefourstandardarithmeticoperatorsforallnumbertypes:

Addition(+)

Subtraction(-)

Multiplication(*)

Division(/)

1 1+2//equals3

2 5-3//equals2

3 2*3//equals6

4 10.0/2.5//equals4.0

UnlikethearithmeticoperatorsinCandObjective-C,theSwiftarithmeticoperatorsdon’tallowvaluestooverflowbydefault.YoucanoptintovalueoverflowbehaviorbyusingSwift’soverflowoperators(suchasa&+b).SeeOverflowOperators.

TheadditionoperatorisalsosupportedforStringconcatenation:

"hello,"+"world"//equals"hello,world"

RemainderOperatorTheremainderoperator(a%b)worksouthowmanymultiplesofbwillfitinsideaandreturnsthevaluethatisleftover(knownastheremainder).

NOTE

Theremainderoperator(%)isalsoknownasamodulooperatorinotherlanguages.However,itsbehaviorinSwiftfornegativenumbersmeansthat,strictlyspeaking,it’saremainderratherthanamodulooperation.

Here’showtheremainderoperatorworks.Tocalculate9%4,youfirstworkouthowmany4swillfitinside9:

Youcanfittwo4sinside9,andtheremainderis1(showninorange).

InSwift,thiswouldbewrittenas:

9%4//equals1

Todeterminetheanswerfora%b,the%operatorcalculatesthefollowingequationandreturnsremainderasitsoutput:

a=(bxsomemultiplier)+remainder

wheresomemultiplieristhelargestnumberofmultiplesofbthatwillfitinsidea.

Inserting9and4intothisequationyields:

9=(4x2)+1

Thesamemethodisappliedwhencalculatingtheremainderforanegativevalueofa:

-9%4//equals-1

Inserting-9and4intotheequationyields:

-9=(4x-2)+-1

givingaremaindervalueof-1.

Thesignofbisignoredfornegativevaluesofb.Thismeansthata%banda%-balwaysgivethesameanswer.

UnaryMinusOperatorThesignofanumericvaluecanbetoggledusingaprefixed-,knownastheunaryminusoperator:

1 letthree=3

2 letminusThree=-three//minusThreeequals-3

3 letplusThree=-minusThree//plusThreeequals3,or"minus

minusthree"

Theunaryminusoperator(-)isprependeddirectlybeforethevalueitoperateson,withoutanywhitespace.

UnaryPlusOperatorTheunaryplusoperator(+)simplyreturnsthevalueitoperateson,withoutanychange:

1 letminusSix=-6

2 letalsoMinusSix=+minusSix//alsoMinusSixequals-6

Althoughtheunaryplusoperatordoesn’tactuallydoanything,youcanuseittoprovidesymmetryinyourcodeforpositivenumberswhenalsousingtheunaryminusoperatorfornegativenumbers.

CompoundAssignmentOperators

LikeC,Swiftprovidescompoundassignmentoperatorsthatcombineassignment(=)withanotheroperation.Oneexampleistheadditionassignmentoperator(+=):

1 vara=1

2 a+=2

3 //aisnowequalto3

Theexpressiona+=2isshorthandfora=a+2.Effectively,theadditionandtheassignmentarecombinedintooneoperatorthatperformsbothtasksatthesametime.

NOTE

Thecompoundassignmentoperatorsdon’treturnavalue.Forexample,youcan’twriteletb=a+=2.

ForinformationabouttheoperatorsprovidedbytheSwiftstandardlibrary,seeOperatorDeclarations.

ComparisonOperators

SwiftsupportsallstandardCcomparisonoperators:

Equalto(a==b)

Notequalto(a!=b)

Greaterthan(a>b)

Lessthan(a<b)

Greaterthanorequalto(a>=b)

Lessthanorequalto(a<=b)

NOTE

Swiftalsoprovidestwoidentityoperators(===and!==),whichyouusetotestwhethertwoobjectreferencesbothrefertothesameobjectinstance.Formoreinformation,seeIdentityOperators.

EachofthecomparisonoperatorsreturnsaBoolvaluetoindicatewhetherornotthestatementistrue:

1 1==1//truebecause1isequalto1

2 2!=1//truebecause2isnotequalto1

3 2>1//truebecause2isgreaterthan1

4 1<2//truebecause1islessthan2

5 1>=1//truebecause1isgreaterthanorequalto1

6 2<=1//falsebecause2isnotlessthanorequalto1

Comparisonoperatorsareoftenusedinconditionalstatements,suchastheifstatement:

1 letname="world"

2 ifname=="world"{

3 print("hello,world")

4 }else{

5 print("I'msorry\(name),butIdon'trecognizeyou")

6 }

7 //Prints"hello,world",becausenameisindeedequalto

"world".

Formoreabouttheifstatement,seeControlFlow.

Youcancomparetwotuplesiftheyhavethesametypeandthesamenumberofvalues.Tuplesarecomparedfromlefttoright,onevalueatatime,untilthecomparisonfindstwovaluesthataren’tequal.Thosetwovaluesarecompared,andtheresultofthatcomparisondeterminestheoverallresultofthetuplecomparison.Ifalltheelementsareequal,thenthetuplesthemselvesareequal.Forexample:

1 (1,"zebra")<(2,"apple")//truebecause1islessthan2;

"zebra"and"apple"arenotcompared

2 (3,"apple")<(3,"bird")//truebecause3isequalto3,

and"apple"islessthan"bird"

3 (4,"dog")==(4,"dog")//truebecause4isequalto4,

and"dog"isequalto"dog"

Intheexampleabove,youcanseetheleft-to-rightcomparisonbehavioronthefirstline.Because1islessthan2,(1,"zebra")isconsideredlessthan(2,"apple"),regardlessofanyothervaluesinthetuples.Itdoesn’tmatterthat"zebra"isn’tlessthan"apple",becausethecomparisonisalreadydeterminedbythetuples’firstelements.However,whenthetuples’firstelementsarethesame,theirsecondelementsarecompared—thisiswhathappensonthesecondandthirdline.

Tuplescanbecomparedwithagivenoperatoronlyiftheoperatorcanbeappliedtoeachvalueintherespectivetuples.Forexample,asdemonstratedinthecodebelow,youcancomparetwotuplesoftype(String,Int)becausebothStringandIntvaluescanbecomparedusingthe<operator.Incontrast,twotuplesoftype(String,Bool)can’tbecomparedwiththe<operatorbecausethe<operatorcan’tbeappliedtoBoolvalues.

1 ("blue",-1)<("purple",1)//OK,evaluatestotrue

2 ("blue",false)<("purple",true)//Errorbecause<can't

compareBooleanvalues

NOTE

TheSwiftstandardlibraryincludestuplecomparisonoperatorsfortupleswithfewerthansevenelements.Tocomparetupleswithsevenormoreelements,youmustimplementthecomparisonoperatorsyourself.

TernaryConditionalOperator

Theternaryconditionaloperatorisaspecialoperatorwiththreeparts,whichtakestheformquestion?answer1:answer2.It’sashortcutforevaluatingoneoftwoexpressionsbasedonwhetherquestionistrueorfalse.Ifquestionistrue,itevaluatesanswer1andreturnsitsvalue;otherwise,itevaluatesanswer2

andreturnsitsvalue.

Theternaryconditionaloperatorisshorthandforthecodebelow:

1 ifquestion{

2 answer1

3 }else{

4 answer2

5 }

Here’sanexample,whichcalculatestheheightforatablerow.Therowheightshouldbe50pointstallerthanthecontentheightiftherowhasaheader,and20pointstalleriftherowdoesn’thaveaheader:

1 letcontentHeight=40

2 lethasHeader=true

3 letrowHeight=contentHeight+(hasHeader?50:20)

4 //rowHeightisequalto90

Theexampleaboveisshorthandforthecodebelow:

1 letcontentHeight=40

2 lethasHeader=true

3 letrowHeight:Int

4 ifhasHeader{

5 rowHeight=contentHeight+50

6 }else{

7 rowHeight=contentHeight+20

8 }

9 //rowHeightisequalto90

Thefirstexample’suseoftheternaryconditionaloperatormeansthatrowHeightcanbesettothecorrectvalueonasinglelineofcode,whichismoreconcise

thanthecodeusedinthesecondexample.

Theternaryconditionaloperatorprovidesanefficientshorthandfordecidingwhichoftwoexpressionstoconsider.Usetheternaryconditionaloperatorwithcare,however.Itsconcisenesscanleadtohard-to-readcodeifoverused.Avoidcombiningmultipleinstancesoftheternaryconditionaloperatorintoonecompoundstatement.

Nil-CoalescingOperator

Thenil-coalescingoperator(a??b)unwrapsanoptionalaifitcontainsavalue,orreturnsadefaultvaluebifaisnil.Theexpressionaisalwaysofanoptionaltype.Theexpressionbmustmatchthetypethatisstoredinsidea.

Thenil-coalescingoperatorisshorthandforthecodebelow:

a!=nil?a!:b

Thecodeaboveusestheternaryconditionaloperatorandforcedunwrapping(a!)toaccessthevaluewrappedinsideawhenaisnotnil,andtoreturnbotherwise.Thenil-coalescingoperatorprovidesamoreelegantwaytoencapsulatethisconditionalcheckingandunwrappinginaconciseandreadableform.

NOTE

Ifthevalueofaisnon-nil,thevalueofbisnotevaluated.Thisisknownasshort-circuitevaluation.

Theexamplebelowusesthenil-coalescingoperatortochoosebetweenadefaultcolornameandanoptionaluser-definedcolorname:

1 letdefaultColorName="red"

2 varuserDefinedColorName:String?//defaultstonil

3

4 varcolorNameToUse=userDefinedColorName??defaultColorName

5 //userDefinedColorNameisnil,socolorNameToUseissettothe

defaultof"red"

TheuserDefinedColorNamevariableisdefinedasanoptionalString,withadefaultvalueofnil.BecauseuserDefinedColorNameisofanoptionaltype,youcanusethenil-coalescingoperatortoconsideritsvalue.Intheexampleabove,theoperatorisusedtodetermineaninitialvalueforaStringvariablecalledcolorNameToUse.BecauseuserDefinedColorNameisnil,theexpressionuserDefinedColorName??defaultColorNamereturnsthevalueofdefaultColorName,or"red".

Ifyouassignanon-nilvaluetouserDefinedColorNameandperformthenil-coalescingoperatorcheckagain,thevaluewrappedinsideuserDefinedColorNameisusedinsteadofthedefault:

1 userDefinedColorName="green"

2 colorNameToUse=userDefinedColorName??defaultColorName

3 //userDefinedColorNameisnotnil,socolorNameToUseissetto

"green"

RangeOperators

Swiftincludesseveralrangeoperators,whichareshortcutsforexpressingarangeofvalues.

ClosedRangeOperatorTheclosedrangeoperator(a...b)definesarangethatrunsfromatob,andincludesthevaluesaandb.Thevalueofamustnotbegreaterthanb.

Theclosedrangeoperatorisusefulwheniteratingoverarangeinwhichyou

wantallofthevaluestobeused,suchaswithafor-inloop:

1 forindexin1...5{

2 print("\(index)times5is\(index*5)")

3 }

4 //1times5is5

5 //2times5is10

6 //3times5is15

7 //4times5is20

8 //5times5is25

Formoreaboutfor-inloops,seeControlFlow.

Half-OpenRangeOperatorThehalf-openrangeoperator(a..<b)definesarangethatrunsfromatob,butdoesn’tincludeb.It’ssaidtobehalf-openbecauseitcontainsitsfirstvalue,butnotitsfinalvalue.Aswiththeclosedrangeoperator,thevalueofamustnotbegreaterthanb.Ifthevalueofaisequaltob,thentheresultingrangewillbeempty.

Half-openrangesareparticularlyusefulwhenyouworkwithzero-basedlistssuchasarrays,whereit’susefultocountupto(butnotincluding)thelengthofthelist:

1 letnames=["Anna","Alex","Brian","Jack"]

2 letcount=names.count

3 foriin0..<count{

4 print("Person\(i+1)iscalled\(names[i])")

5 }

6 //Person1iscalledAnna

7 //Person2iscalledAlex

8 //Person3iscalledBrian

9 //Person4iscalledJack

Notethatthearraycontainsfouritems,but0..<countonlycountsasfaras3(theindexofthelastiteminthearray),becauseit’sahalf-openrange.Formoreaboutarrays,seeArrays.

One-SidedRangesTheclosedrangeoperatorhasanalternativeformforrangesthatcontinueasfaraspossibleinonedirection—forexample,arangethatincludesalltheelementsofanarrayfromindex2totheendofthearray.Inthesecases,youcanomitthevaluefromonesideoftherangeoperator.Thiskindofrangeiscalledaone-sidedrangebecausetheoperatorhasavalueononlyoneside.Forexample:

1 fornameinnames[2...]{

2 print(name)

3 }

4 //Brian

5 //Jack

6

7 fornameinnames[...2]{

8 print(name)

9 }

10 //Anna

11 //Alex

12 //Brian

Thehalf-openrangeoperatoralsohasaone-sidedformthat’swrittenwithonlyitsfinalvalue.Justlikewhenyouincludeavalueonbothsides,thefinalvalueisn’tpartoftherange.Forexample:

1 fornameinnames[..<2]{

2 print(name)

3 }

4 //Anna

5 //Alex

One-sidedrangescanbeusedinothercontexts,notjustinsubscripts.Youcan’titerateoveraone-sidedrangethatomitsafirstvalue,becauseitisn’tclearwhereiterationshouldbegin.Youcaniterateoveraone-sidedrangethatomitsitsfinalvalue;however,becausetherangecontinuesindefinitely,makesureyouaddanexplicitendconditionfortheloop.Youcanalsocheckwhetheraone-sidedrangecontainsaparticularvalue,asshowninthecodebelow.

1 letrange=...5

2 range.contains(7)//false

3 range.contains(4)//true

4 range.contains(-1)//true

LogicalOperators

LogicaloperatorsmodifyorcombinetheBooleanlogicvaluestrueandfalse.SwiftsupportsthethreestandardlogicaloperatorsfoundinC-basedlanguages:

LogicalNOT(!a)

LogicalAND(a&&b)

LogicalOR(a||b)

LogicalNOTOperatorThelogicalNOToperator(!a)invertsaBooleanvaluesothattruebecomes

false,andfalsebecomestrue.

ThelogicalNOToperatorisaprefixoperator,andappearsimmediatelybeforethevalueitoperateson,withoutanywhitespace.Itcanbereadas“nota”,asseeninthefollowingexample:

1 letallowedEntry=false

2 if!allowedEntry{

3 print("ACCESSDENIED")

4 }

5 //Prints"ACCESSDENIED"

Thephraseif!allowedEntrycanbereadas“ifnotallowedentry.”Thesubsequentlineisonlyexecutedif“notallowedentry”istrue;thatis,ifallowedEntryisfalse.

Asinthisexample,carefulchoiceofBooleanconstantandvariablenamescanhelptokeepcodereadableandconcise,whileavoidingdoublenegativesorconfusinglogicstatements.

LogicalANDOperatorThelogicalANDoperator(a&&b)createslogicalexpressionswherebothvaluesmustbetruefortheoverallexpressiontoalsobetrue.

Ifeithervalueisfalse,theoverallexpressionwillalsobefalse.Infact,ifthefirstvalueisfalse,thesecondvaluewon’tevenbeevaluated,becauseitcan’tpossiblymaketheoverallexpressionequatetotrue.Thisisknownasshort-circuitevaluation.

ThisexampleconsiderstwoBoolvaluesandonlyallowsaccessifbothvaluesaretrue:

1 letenteredDoorCode=true

2 letpassedRetinaScan=false

3 ifenteredDoorCode&&passedRetinaScan{

4 print("Welcome!")

5 }else{

6 print("ACCESSDENIED")

7 }

8 //Prints"ACCESSDENIED"

LogicalOROperatorThelogicalORoperator(a||b)isaninfixoperatormadefromtwoadjacentpipecharacters.Youuseittocreatelogicalexpressionsinwhichonlyoneofthetwovalueshastobetruefortheoverallexpressiontobetrue.

LiketheLogicalANDoperatorabove,theLogicalORoperatorusesshort-circuitevaluationtoconsideritsexpressions.IftheleftsideofaLogicalORexpressionistrue,therightsideisnotevaluated,becauseitcan’tchangetheoutcomeoftheoverallexpression.

Intheexamplebelow,thefirstBoolvalue(hasDoorKey)isfalse,butthesecondvalue(knowsOverridePassword)istrue.Becauseonevalueistrue,theoverallexpressionalsoevaluatestotrue,andaccessisallowed:

1 lethasDoorKey=false

2 letknowsOverridePassword=true

3 ifhasDoorKey||knowsOverridePassword{

4 print("Welcome!")

5 }else{

6 print("ACCESSDENIED")

7 }

8 //Prints"Welcome!"

CombiningLogicalOperatorsYoucancombinemultiplelogicaloperatorstocreatelongercompoundexpressions:

1 ifenteredDoorCode&&passedRetinaScan||hasDoorKey||

knowsOverridePassword{

2 print("Welcome!")

3 }else{

4 print("ACCESSDENIED")

5 }

6 //Prints"Welcome!"

Thisexampleusesmultiple&&and||operatorstocreatealongercompoundexpression.However,the&&and||operatorsstilloperateononlytwovalues,sothisisactuallythreesmallerexpressionschainedtogether.Theexamplecanbereadas:

Ifwe’veenteredthecorrectdoorcodeandpassedtheretinascan,orifwehaveavaliddoorkey,orifweknowtheemergencyoverridepassword,thenallowaccess.

BasedonthevaluesofenteredDoorCode,passedRetinaScan,andhasDoorKey,thefirsttwosubexpressionsarefalse.However,theemergencyoverridepasswordisknown,sotheoverallcompoundexpressionstillevaluatestotrue.

NOTE

TheSwiftlogicaloperators&&and||areleft-associative,meaningthatcompoundexpressionswithmultiplelogicaloperatorsevaluatetheleftmostsubexpressionfirst.

ExplicitParenthesesIt’ssometimesusefultoincludeparentheseswhenthey’renotstrictlyneeded,tomaketheintentionofacomplexexpressioneasiertoread.Inthedooraccessexampleabove,it’susefultoaddparenthesesaroundthefirstpartofthe

compoundexpressiontomakeitsintentexplicit:

1 if(enteredDoorCode&&passedRetinaScan)||hasDoorKey||

knowsOverridePassword{

2 print("Welcome!")

3 }else{

4 print("ACCESSDENIED")

5 }

6 //Prints"Welcome!"

Theparenthesesmakeitclearthatthefirsttwovaluesareconsideredaspartofaseparatepossiblestateintheoveralllogic.Theoutputofthecompoundexpressiondoesn’tchange,buttheoverallintentionisclearertothereader.Readabilityisalwayspreferredoverbrevity;useparentheseswheretheyhelptomakeyourintentionsclear.

StringsandCharacters

Astringisaseriesofcharacters,suchas"hello,world"or"albatross".SwiftstringsarerepresentedbytheStringtype.ThecontentsofaStringcanbeaccessedinvariousways,includingasacollectionofCharactervalues.

Swift’sStringandCharactertypesprovideafast,Unicode-compliantwaytoworkwithtextinyourcode.Thesyntaxforstringcreationandmanipulationislightweightandreadable,withastringliteralsyntaxthatissimilartoC.Stringconcatenationisassimpleascombiningtwostringswiththe+operator,andstringmutabilityismanagedbychoosingbetweenaconstantoravariable,justlikeanyothervalueinSwift.Youcanalsousestringstoinsertconstants,variables,literals,andexpressionsintolongerstrings,inaprocessknownasstringinterpolation.Thismakesiteasytocreatecustomstringvaluesfordisplay,storage,andprinting.

Despitethissimplicityofsyntax,Swift’sStringtypeisafast,modernstringimplementation.Everystringiscomposedofencoding-independentUnicodecharacters,andprovidessupportforaccessingthosecharactersinvariousUnicoderepresentations.

NOTE

Swift’sStringtypeisbridgedwithFoundation’sNSStringclass.FoundationalsoextendsStringtoexposemethodsdefinedbyNSString.Thismeans,ifyouimportFoundation,youcanaccessthoseNSStringmethodsonStringwithoutcasting.

FormoreinformationaboutusingStringwithFoundationandCocoa,seeBridgingBetweenStringandNSString.

StringLiterals

YoucanincludepredefinedStringvalueswithinyourcodeasstringliterals.Astringliteralisasequenceofcharacterssurroundedbydoublequotationmarks(").

Useastringliteralasaninitialvalueforaconstantorvariable:

letsomeString="Somestringliteralvalue"

NotethatSwiftinfersatypeofStringforthesomeStringconstantbecauseit’sinitializedwithastringliteralvalue.

MultilineStringLiteralsIfyouneedastringthatspansseverallines,useamultilinestringliteral—asequenceofcharacterssurroundedbythreedoublequotationmarks:

1 letquotation="""

2 TheWhiteRabbitputonhisspectacles."WhereshallIbegin,

3 pleaseyourMajesty?"heasked.

4

5 "Beginatthebeginning,"theKingsaidgravely,"andgoon

6 tillyoucometotheend;thenstop."

7 """

Amultilinestringliteralincludesallofthelinesbetweenitsopeningandclosingquotationmarks.Thestringbeginsonthefirstlineaftertheopeningquotationmarks(""")andendsonthelinebeforetheclosingquotationmarks,whichmeansthatneitherofthestringsbelowstartorendwithalinebreak:

1 letsingleLineString="Thesearethesame."

2 letmultilineString="""

3 Thesearethesame.

4 """

Whenyoursourcecodeincludesalinebreakinsideofamultilinestringliteral,thatlinebreakalsoappearsinthestring’svalue.Ifyouwanttouselinebreakstomakeyoursourcecodeeasiertoread,butyoudon’twantthelinebreakstobe

partofthestring’svalue,writeabackslash(\)attheendofthoselines:

1 letsoftWrappedQuotation="""

2 TheWhiteRabbitputonhisspectacles."WhereshallIbegin,

\

3 pleaseyourMajesty?"heasked.

4

5 "Beginatthebeginning,"theKingsaidgravely,"andgoon\

6 tillyoucometotheend;thenstop."

7 """

Tomakeamultilinestringliteralthatbeginsorendswithalinefeed,writeablanklineasthefirstorlastline.Forexample:

1 letlineBreaks="""

2

3 Thisstringstartswithalinebreak.

4 Italsoendswithalinebreak.

5

6 """

Amultilinestringcanbeindentedtomatchthesurroundingcode.Thewhitespacebeforetheclosingquotationmarks(""")tellsSwiftwhatwhitespacetoignorebeforealloftheotherlines.However,ifyouwritewhitespaceatthebeginningofalineinadditiontowhat’sbeforetheclosingquotationmarks,thatwhitespaceisincluded.

Intheexampleabove,eventhoughtheentiremultilinestringliteralisindented,

thefirstandlastlinesinthestringdon’tbeginwithanywhitespace.Themiddlelinehasmoreindentationthantheclosingquotationmarks,soitstartswiththatextrafour-spaceindentation.

SpecialCharactersinStringLiteralsStringliteralscanincludethefollowingspecialcharacters:

Theescapedspecialcharacters\0(nullcharacter),\\(backslash),\t(horizontaltab),\n(linefeed),\r(carriagereturn),\"(doublequotationmark)and\'(singlequotationmark)

AnarbitraryUnicodescalarvalue,writtenas\u{n},wherenisa1–8digithexadecimalnumber(UnicodeisdiscussedinUnicodebelow)

Thecodebelowshowsfourexamplesofthesespecialcharacters.ThewiseWordsconstantcontainstwoescapeddoublequotationmarks.ThedollarSign,blackHeart,andsparklingHeartconstantsdemonstratetheUnicodescalarformat:

1 letwiseWords="\"Imaginationismoreimportantthan

knowledge\"-Einstein"

2 //"Imaginationismoreimportantthanknowledge"-Einstein

3 letdollarSign="\u{24}"//$,UnicodescalarU+0024

4 letblackHeart="\u{2665}"//♥,UnicodescalarU+2665

5 letsparklingHeart="\u{1F496}"//,UnicodescalarU+1F496

Becausemultilinestringliteralsusethreedoublequotationmarksinsteadofjustone,youcanincludeadoublequotationmark(")insideofamultilinestringliteralwithoutescapingit.Toincludethetext"""inamultilinestring,escapeatleastoneofthequotationmarks.Forexample:

1 letthreeDoubleQuotationMarks="""

2 Escapingthefirstquotationmark\"""

3 Escapingallthreequotationmarks\"\"\"

4 """

ExtendedStringDelimitersYoucanplaceastringliteralwithinextendeddelimiterstoincludespecialcharactersinastringwithoutinvokingtheireffect.Youplaceyourstringwithinquotationmarks(")andsurroundthatwithnumbersigns(#).Forexample,printingthestringliteral#"Line1\nLine2"#printsthelinefeedescapesequence(\n)ratherthanprintingthestringacrosstwolines.

Ifyouneedthespecialeffectsofacharacterinastringliteral,matchthenumberofnumbersignswithinthestringfollowingtheescapecharacter(\).Forexample,ifyourstringis#"Line1\nLine2"#andyouwanttobreaktheline,youcanuse#"Line1\#nLine2"#instead.Similarly,###"Line1\###nLine2"###alsobreakstheline.

Stringliteralscreatedusingextendeddelimiterscanalsobemultilinestringliterals.Youcanuseextendeddelimiterstoincludethetext"""inamultilinestring,overridingthedefaultbehaviorthatendstheliteral.Forexample:

1 letthreeMoreDoubleQuotationMarks=#"""

2 Herearethreemoredoublequotes:"""

3 """#

InitializinganEmptyString

TocreateanemptyStringvalueasthestartingpointforbuildingalongerstring,eitherassignanemptystringliteraltoavariable,orinitializeanewStringinstancewithinitializersyntax:

1 varemptyString=""//emptystringliteral

2 varanotherEmptyString=String()//initializersyntax

3 //thesetwostringsarebothempty,andareequivalenttoeach

other

FindoutwhetheraStringvalueisemptybycheckingitsBooleanisEmptyproperty:

1 ifemptyString.isEmpty{

2 print("Nothingtoseehere")

3 }

4 //Prints"Nothingtoseehere"

StringMutability

YouindicatewhetheraparticularStringcanbemodified(ormutated)byassigningittoavariable(inwhichcaseitcanbemodified),ortoaconstant(inwhichcaseitcan’tbemodified):

1 varvariableString="Horse"

2 variableString+="andcarriage"

3 //variableStringisnow"Horseandcarriage"

4

5 letconstantString="Highlander"

6 constantString+="andanotherHighlander"

7 //thisreportsacompile-timeerror-aconstantstringcannot

bemodified

NOTE

ThisapproachisdifferentfromstringmutationinObjective-CandCocoa,whereyouchoosebetweentwoclasses(NSStringandNSMutableString)toindicatewhetherastringcanbemutated.

StringsAreValueTypes

Swift’sStringtypeisavaluetype.IfyoucreateanewStringvalue,thatStringvalueiscopiedwhenit’spassedtoafunctionormethod,orwhenit’sassignedtoaconstantorvariable.Ineachcase,anewcopyoftheexistingStringvalueiscreated,andthenewcopyispassedorassigned,nottheoriginalversion.ValuetypesaredescribedinStructuresandEnumerationsAreValueTypes.

Swift’scopy-by-defaultStringbehaviorensuresthatwhenafunctionormethodpassesyouaStringvalue,it’sclearthatyouownthatexactStringvalue,regardlessofwhereitcamefrom.Youcanbeconfidentthatthestringyouarepassedwon’tbemodifiedunlessyoumodifyityourself.

Behindthescenes,Swift’scompileroptimizesstringusagesothatactualcopyingtakesplaceonlywhenabsolutelynecessary.Thismeansyoualwaysgetgreatperformancewhenworkingwithstringsasvaluetypes.

WorkingwithCharacters

YoucanaccesstheindividualCharactervaluesforaStringbyiteratingoverthestringwithafor-inloop:

1 forcharacterin"Dog!" {

2 print(character)

3 }

4 //D

5 //o

6 //g

7 //!

8 //

Thefor-inloopisdescribedinFor-InLoops.

Alternatively,youcancreateastand-aloneCharacterconstantorvariablefromasingle-characterstringliteralbyprovidingaCharactertypeannotation:

letexclamationMark:Character="!"

StringvaluescanbeconstructedbypassinganarrayofCharactervaluesasanargumenttoitsinitializer:

1 letcatCharacters:[Character]=["C","a","t","!"," "]

2 letcatString=String(catCharacters)

3 print(catString)

4 //Prints"Cat! "

ConcatenatingStringsandCharacters

Stringvaluescanbeaddedtogether(orconcatenated)withtheadditionoperator(+)tocreateanewStringvalue:

1 letstring1="hello"

2 letstring2="there"

3 varwelcome=string1+string2

4 //welcomenowequals"hellothere"

YoucanalsoappendaStringvaluetoanexistingStringvariablewiththeadditionassignmentoperator(+=):

1 varinstruction="lookover"

2 instruction+=string2

3 //instructionnowequals"lookoverthere"

YoucanappendaCharactervaluetoaStringvariablewiththeStringtype’sappend()method:

1 letexclamationMark:Character="!"

2 welcome.append(exclamationMark)

3 //welcomenowequals"hellothere!"

NOTE

Youcan’tappendaStringorCharactertoanexistingCharactervariable,becauseaCharactervaluemustcontainasinglecharacteronly.

Ifyou’reusingmultilinestringliteralstobuildupthelinesofalongerstring,youwanteverylineinthestringtoendwithalinebreak,includingthelastline.Forexample:

1 letbadStart="""

2 one

3 two

4 """

5 letend="""

6 three

7 """

8 print(badStart+end)

9 //Printstwolines:

10 //one

11 //twothree

12

13 letgoodStart="""

14 one

15 two

16

17 """

18 print(goodStart+end)

19 //Printsthreelines:

20 //one

21 //two

22 //three

Inthecodeabove,concatenatingbadStartwithendproducesatwo-linestring,whichisn’tthedesiredresult.BecausethelastlineofbadStartdoesn’tendwithalinebreak,thatlinegetscombinedwiththefirstlineofend.Incontrast,bothlinesofgoodStartendwithalinebreak,sowhenit’scombinedwithendtheresulthasthreelines,asexpected.

StringInterpolation

StringinterpolationisawaytoconstructanewStringvaluefromamixofconstants,variables,literals,andexpressionsbyincludingtheirvaluesinsideastringliteral.Youcanusestringinterpolationinbothsingle-lineandmultilinestringliterals.Eachitemthatyouinsertintothestringliteraliswrappedinapairofparentheses,prefixedbyabackslash(\):

1 letmultiplier=3

2 letmessage="\(multiplier)times2.5is\(Double(multiplier)

*2.5)"

3 //messageis"3times2.5is7.5"

Intheexampleabove,thevalueofmultiplierisinsertedintoastringliteralas\(multiplier).Thisplaceholderisreplacedwiththeactualvalueofmultiplierwhenthestringinterpolationisevaluatedtocreateanactualstring.

Thevalueofmultiplierisalsopartofalargerexpressionlaterinthestring.ThisexpressioncalculatesthevalueofDouble(multiplier)*2.5andinsertstheresult(7.5)intothestring.Inthiscase,theexpressioniswrittenas\(Double(multiplier)*2.5)whenit’sincludedinsidethestringliteral.

Youcanuseextendedstringdelimiterstocreatestringscontainingcharacters

thatwouldotherwisebetreatedasastringinterpolation.Forexample:

1 print(#"WriteaninterpolatedstringinSwiftusing\

(multiplier)."#)

2 //Prints"WriteaninterpolatedstringinSwiftusing\

(multiplier)."

Tousestringinterpolationinsideastringthatusesextendeddelimiters,matchthenumberofnumbersignsbeforethebackslashtothenumberofnumbersignsatthebeginningandendofthestring.Forexample:

1 print(#"6times7is\#(6*7)."#)

2 //Prints"6times7is42."

NOTE

Theexpressionsyouwriteinsideparentheseswithinaninterpolatedstringcan’tcontainanunescapedbackslash(\),acarriagereturn,oralinefeed.However,theycancontainotherstringliterals.

Unicode

Unicodeisaninternationalstandardforencoding,representing,andprocessingtextindifferentwritingsystems.Itenablesyoutorepresentalmostanycharacterfromanylanguageinastandardizedform,andtoreadandwritethosecharacterstoandfromanexternalsourcesuchasatextfileorwebpage.Swift’sStringandCharactertypesarefullyUnicode-compliant,asdescribedinthissection.

UnicodeScalarValuesBehindthescenes,Swift’snativeStringtypeisbuiltfromUnicodescalarvalues.AUnicodescalarvalueisaunique21-bitnumberforacharacterormodifier,suchasU+0061forLATINSMALLLETTERA("a"),orU+1F425forFRONT-FACINGBABYCHICK("" ).

Notethatnotall21-bitUnicodescalarvaluesareassignedtoacharacter—somescalarsarereservedforfutureassignmentorforuseinUTF-16encoding.Scalarvaluesthathavebeenassignedtoacharactertypicallyalsohaveaname,suchasLATINSMALLLETTERAandFRONT-FACINGBABYCHICKintheexamplesabove.

ExtendedGraphemeClustersEveryinstanceofSwift’sCharactertyperepresentsasingleextendedgraphemecluster.AnextendedgraphemeclusterisasequenceofoneormoreUnicodescalarsthat(whencombined)produceasinglehuman-readablecharacter.

Here’sanexample.TheletterécanberepresentedasthesingleUnicodescalaré(LATINSMALLLETTEREWITHACUTE,orU+00E9).However,thesamelettercanalsoberepresentedasapairofscalars—astandardlettere(LATINSMALLLETTERE,orU+0065),followedbytheCOMBININGACUTEACCENTscalar(U+0301).TheCOMBININGACUTEACCENTscalarisgraphicallyappliedtothescalarthatprecedesit,turninganeintoanéwhenit’srenderedbyaUnicode-awaretext-renderingsystem.

Inbothcases,theletteréisrepresentedasasingleSwiftCharactervaluethatrepresentsanextendedgraphemecluster.Inthefirstcase,theclustercontainsasinglescalar;inthesecondcase,it’saclusteroftwoscalars:

1 leteAcute:Character="\u{E9}"//é

2 letcombinedEAcute:Character="\u{65}\u{301}"//e

followedby

3 //eAcuteisé,combinedEAcuteisé

ExtendedgraphemeclustersareaflexiblewaytorepresentmanycomplexscriptcharactersasasingleCharactervalue.Forexample,HangulsyllablesfromtheKoreanalphabetcanberepresentedaseitheraprecomposedordecomposedsequence.BothoftheserepresentationsqualifyasasingleCharactervalueinSwift:

1 letprecomposed:Character="\u{D55C}"//

2 letdecomposed:Character="\u{1112}\u{1161}\u{11AB}"//�,

�,�

3 //precomposedis�,decomposedis�

Extendedgraphemeclustersenablescalarsforenclosingmarks(suchasCOMBININGENCLOSINGCIRCLE,orU+20DD)toencloseotherUnicodescalarsaspartofasingleCharactervalue:

1 letenclosedEAcute:Character="\u{E9}\u{20DD}"

2 //enclosedEAcuteisé�

UnicodescalarsforregionalindicatorsymbolscanbecombinedinpairstomakeasingleCharactervalue,suchasthiscombinationofREGIONALINDICATORSYMBOLLETTERU(U+1F1FA)andREGIONALINDICATORSYMBOLLETTERS(U+1F1F8):

1 letregionalIndicatorForUS:Character="\u{1F1FA}\u{1F1F8}"

2 //regionalIndicatorForUSis��

CountingCharacters

ToretrieveacountoftheCharactervaluesinastring,usethecountpropertyofthestring:

1 letunusualMenagerie="Koala,Snail,Penguin,Dromedary

"

2 print("unusualMenageriehas\(unusualMenagerie.count)

characters")

3 //Prints"unusualMenageriehas40characters"

NotethatSwift’suseofextendedgraphemeclustersforCharactervaluesmeansthatstringconcatenationandmodificationmaynotalwaysaffectastring’scharactercount.

Forexample,ifyouinitializeanewstringwiththefour-characterwordcafe,andthenappendaCOMBININGACUTEACCENT(U+0301)totheendofthestring,theresultingstringwillstillhaveacharactercountof4,withafourthcharacterofé,note:

1 varword="cafe"

2 print("thenumberofcharactersin\(word)is\(word.count)")

3 //Prints"thenumberofcharactersincafeis4"

4

5 word+="\u{301}"//COMBININGACUTEACCENT,U+0301

6

7 print("thenumberofcharactersin\(word)is\(word.count)")

8 //Prints"thenumberofcharactersincaféis4"

NOTE

ExtendedgraphemeclusterscanbecomposedofmultipleUnicodescalars.Thismeansthatdifferentcharacters—anddifferentrepresentationsofthesamecharacter—canrequiredifferentamountsofmemorytostore.Becauseofthis,charactersinSwiftdon’teachtakeupthesameamountofmemorywithinastring’srepresentation.Asaresult,thenumberofcharactersinastringcan’tbecalculatedwithoutiteratingthroughthestringtodetermineitsextendedgraphemeclusterboundaries.Ifyouareworkingwithparticularlylongstringvalues,beawarethatthecountpropertymustiterateovertheUnicodescalarsintheentirestringinordertodeterminethecharactersforthatstring.

Thecountofthecharactersreturnedbythecountpropertyisn’talwaysthesameasthelengthpropertyofanNSStringthatcontainsthesamecharacters.ThelengthofanNSStringisbasedonthenumberof16-bitcodeunitswithinthestring’sUTF-16representationandnotthenumberofUnicodeextendedgraphemeclusterswithinthestring.

AccessingandModifyingaString

Youaccessandmodifyastringthroughitsmethodsandproperties,orbyusingsubscriptsyntax.

StringIndicesEachStringvaluehasanassociatedindextype,String.Index,whichcorrespondstothepositionofeachCharacterinthestring.

Asmentionedabove,differentcharacterscanrequiredifferentamountsofmemorytostore,soinordertodeterminewhichCharacterisataparticularposition,youmustiterateovereachUnicodescalarfromthestartorendofthatString.Forthisreason,Swiftstringscan’tbeindexedbyintegervalues.

UsethestartIndexpropertytoaccessthepositionofthefirstCharacterofaString.TheendIndexpropertyisthepositionafterthelastcharacterinaString.Asaresult,theendIndexpropertyisn’tavalidargumenttoastring’ssubscript.IfaStringisempty,startIndexandendIndexareequal.

Youaccesstheindicesbeforeandafteragivenindexusingtheindex(before:)andindex(after:)methodsofString.Toaccessanindexfartherawayfromthegivenindex,youcanusetheindex(_:offsetBy:)methodinsteadofcallingoneofthesemethodsmultipletimes.

YoucanusesubscriptsyntaxtoaccesstheCharacterataparticularStringindex.

1 letgreeting="GutenTag!"

2 greeting[greeting.startIndex]

3 //G

4 greeting[greeting.index(before:greeting.endIndex)]

5 //!

6 greeting[greeting.index(after:greeting.startIndex)]

7 //u

8 letindex=greeting.index(greeting.startIndex,offsetBy:7)

9 greeting[index]

10 //a

Attemptingtoaccessanindexoutsideofastring’srangeoraCharacteratanindexoutsideofastring’srangewilltriggeraruntimeerror.

1 greeting[greeting.endIndex]//Error

2 greeting.index(after:greeting.endIndex)//Error

Usetheindicespropertytoaccessalloftheindicesofindividualcharactersinastring.

1 forindexingreeting.indices{

2 print("\(greeting[index])",terminator:"")

3 }

4 //Prints"GutenTag!"

NOTE

YoucanusethestartIndexandendIndexpropertiesandtheindex(before:),index(after:),andindex(_:offsetBy:)methodsonanytypethatconformstotheCollectionprotocol.ThisincludesString,asshownhere,aswellascollectiontypessuchasArray,Dictionary,andSet.

InsertingandRemovingToinsertasinglecharacterintoastringataspecifiedindex,usetheinsert(_:at:)method,andtoinsertthecontentsofanotherstringataspecifiedindex,usetheinsert(contentsOf:at:)method.

1 varwelcome="hello"

2 welcome.insert("!",at:welcome.endIndex)

3 //welcomenowequals"hello!"

4

5 welcome.insert(contentsOf:"there",at:welcome.index(before:

welcome.endIndex))

6 //welcomenowequals"hellothere!"

Toremoveasinglecharacterfromastringataspecifiedindex,usetheremove(at:)method,andtoremoveasubstringataspecifiedrange,usethe

removeSubrange(_:)method:

1 welcome.remove(at:welcome.index(before:welcome.endIndex))

2 //welcomenowequals"hellothere"

3

4 letrange=welcome.index(welcome.endIndex,offsetBy:-6)..

<welcome.endIndex

5 welcome.removeSubrange(range)

6 //welcomenowequals"hello"

NOTE

Youcanusetheinsert(_:at:),insert(contentsOf:at:),remove(at:),andremoveSubrange(_:)methodsonanytypethatconformstotheRangeReplaceableCollectionprotocol.ThisincludesString,asshownhere,aswellascollectiontypessuchasArray,Dictionary,andSet.

Substrings

Whenyougetasubstringfromastring—forexample,usingasubscriptoramethodlikeprefix(_:)—theresultisaninstanceofSubstring,notanotherstring.SubstringsinSwifthavemostofthesamemethodsasstrings,whichmeansyoucanworkwithsubstringsthesamewayyouworkwithstrings.However,unlikestrings,youusesubstringsforonlyashortamountoftimewhileperformingactionsonastring.Whenyou’rereadytostoretheresultforalongertime,youconvertthesubstringtoaninstanceofString.Forexample:

1 letgreeting="Hello,world!"

2 letindex=greeting.firstIndex(of:",")??greeting.endIndex

3 letbeginning=greeting[..<index]

4 //beginningis"Hello"

5

6 //ConverttheresulttoaStringforlong-termstorage.

7 letnewString=String(beginning)

Likestrings,eachsubstringhasaregionofmemorywherethecharactersthatmakeupthesubstringarestored.Thedifferencebetweenstringsandsubstringsisthat,asaperformanceoptimization,asubstringcanreusepartofthememorythat’susedtostoretheoriginalstring,orpartofthememorythat’susedtostoreanothersubstring.(Stringshaveasimilaroptimization,butiftwostringssharememory,theyareequal.)Thisperformanceoptimizationmeansyoudon’thavetopaytheperformancecostofcopyingmemoryuntilyoumodifyeitherthestringorsubstring.Asmentionedabove,substringsaren’tsuitableforlong-termstorage—becausetheyreusethestorageoftheoriginalstring,theentireoriginalstringmustbekeptinmemoryaslongasanyofitssubstringsarebeingused.

Intheexampleabove,greetingisastring,whichmeansithasaregionofmemorywherethecharactersthatmakeupthestringarestored.Becausebeginningisasubstringofgreeting,itreusesthememorythatgreetinguses.Incontrast,newStringisastring—whenit’screatedfromthesubstring,ithasitsownstorage.Thefigurebelowshowstheserelationships:

NOTE

BothStringandSubstringconformtotheStringProtocolprotocol,whichmeansit’softenconvenientforstring-manipulationfunctionstoacceptaStringProtocolvalue.YoucancallsuchfunctionswitheitheraStringorSubstringvalue.

ComparingStrings

Swiftprovidesthreewaystocomparetextualvalues:stringandcharacterequality,prefixequality,andsuffixequality.

StringandCharacterEqualityStringandcharacterequalityischeckedwiththe“equalto”operator(==)andthe“notequalto”operator(!=),asdescribedinComparisonOperators:

1 letquotation="We'realotalike,youandI."

2 letsameQuotation="We'realotalike,youandI."

3 ifquotation==sameQuotation{

4 print("Thesetwostringsareconsideredequal")

5 }

6 //Prints"Thesetwostringsareconsideredequal"

TwoStringvalues(ortwoCharactervalues)areconsideredequaliftheirextendedgraphemeclustersarecanonicallyequivalent.Extendedgraphemeclustersarecanonicallyequivalentiftheyhavethesamelinguisticmeaningandappearance,evenifthey’recomposedfromdifferentUnicodescalarsbehindthescenes.

Forexample,LATINSMALLLETTEREWITHACUTE(U+00E9)iscanonicallyequivalenttoLATINSMALLLETTERE(U+0065)followedbyCOMBININGACUTEACCENT(U+0301).Bothoftheseextendedgraphemeclustersarevalidwaystorepresentthecharacteré,andsothey’reconsideredtobecanonicallyequivalent:

1 //"Voulez-vousuncafé?"usingLATINSMALLLETTEREWITHACUTE

2 leteAcuteQuestion="Voulez-vousuncaf\u{E9}?"

3

4 //"Voulez-vousuncafé?"usingLATINSMALLLETTEREand

COMBININGACUTEACCENT

5 letcombinedEAcuteQuestion="Voulez-vousuncaf\u{65}\u{301}?"

6

7 ifeAcuteQuestion==combinedEAcuteQuestion{

8 print("Thesetwostringsareconsideredequal")

9 }

10 //Prints"Thesetwostringsareconsideredequal"

Conversely,LATINCAPITALLETTERA(U+0041,or"A"),asusedinEnglish,isnotequivalenttoCYRILLICCAPITALLETTERA(U+0410,or"А"),asusedinRussian.Thecharactersarevisuallysimilar,butdon’thavethesamelinguisticmeaning:

1 letlatinCapitalLetterA:Character="\u{41}"

2

3 letcyrillicCapitalLetterA:Character="\u{0410}"

4

5 iflatinCapitalLetterA!=cyrillicCapitalLetterA{

6 print("Thesetwocharactersarenotequivalent.")

7 }

8 //Prints"Thesetwocharactersarenotequivalent."

NOTE

StringandcharactercomparisonsinSwiftarenotlocale-sensitive.

PrefixandSuffixEqualityTocheckwhetherastringhasaparticularstringprefixorsuffix,callthestring’shasPrefix(_:)andhasSuffix(_:)methods,bothofwhichtakeasingleargumentoftypeStringandreturnaBooleanvalue.

TheexamplesbelowconsideranarrayofstringsrepresentingthescenelocationsfromthefirsttwoactsofShakespeare’sRomeoandJuliet:

1 letromeoAndJuliet=[

2 "Act1Scene1:Verona,Apublicplace",

3 "Act1Scene2:Capulet'smansion",

4 "Act1Scene3:AroominCapulet'smansion",

5 "Act1Scene4:AstreetoutsideCapulet'smansion",

6 "Act1Scene5:TheGreatHallinCapulet'smansion",

7 "Act2Scene1:OutsideCapulet'smansion",

8 "Act2Scene2:Capulet'sorchard",

9 "Act2Scene3:OutsideFriarLawrence'scell",

10 "Act2Scene4:AstreetinVerona",

11 "Act2Scene5:Capulet'smansion",

12 "Act2Scene6:FriarLawrence'scell"

13 ]

YoucanusethehasPrefix(_:)methodwiththeromeoAndJulietarraytocountthenumberofscenesinAct1oftheplay:

1 varact1SceneCount=0

2 forsceneinromeoAndJuliet{

3 ifscene.hasPrefix("Act1"){

4 act1SceneCount+=1

5 }

6 }

7 print("Thereare\(act1SceneCount)scenesinAct1")

8 //Prints"Thereare5scenesinAct1"

Similarly,usethehasSuffix(_:)methodtocountthenumberofscenesthattakeplaceinoraroundCapulet’smansionandFriarLawrence’scell:

1 varmansionCount=0

2 varcellCount=0

3 forsceneinromeoAndJuliet{

4 ifscene.hasSuffix("Capulet'smansion"){

5 mansionCount+=1

6 }elseifscene.hasSuffix("FriarLawrence'scell"){

7 cellCount+=1

8 }

9 }

10 print("\(mansionCount)mansionscenes;\(cellCount)cell

scenes")

11 //Prints"6mansionscenes;2cellscenes"

NOTE

ThehasPrefix(_:)andhasSuffix(_:)methodsperformacharacter-by-charactercanonicalequivalencecomparisonbetweentheextendedgraphemeclustersineachstring,asdescribedinStringandCharacterEquality.

UnicodeRepresentationsofStrings

WhenaUnicodestringiswrittentoatextfileorsomeotherstorage,theUnicodescalarsinthatstringareencodedinoneofseveralUnicode-definedencodingforms.Eachformencodesthestringinsmallchunksknownascodeunits.TheseincludetheUTF-8encodingform(whichencodesastringas8-bitcodeunits),theUTF-16encodingform(whichencodesastringas16-bitcodeunits),andtheUTF-32encodingform(whichencodesastringas32-bitcodeunits).

SwiftprovidesseveraldifferentwaystoaccessUnicoderepresentationsofstrings.Youcaniterateoverthestringwithafor-instatement,toaccessitsindividualCharactervaluesasUnicodeextendedgraphemeclusters.ThisprocessisdescribedinWorkingwithCharacters.

Alternatively,accessaStringvalueinoneofthreeotherUnicode-compliant

representations:

AcollectionofUTF-8codeunits(accessedwiththestring’sutf8property)

AcollectionofUTF-16codeunits(accessedwiththestring’sutf16property)

Acollectionof21-bitUnicodescalarvalues,equivalenttothestring’sUTF-32encodingform(accessedwiththestring’sunicodeScalarsproperty)

Eachexamplebelowshowsadifferentrepresentationofthefollowingstring,whichismadeupofthecharactersD,o,g,‼(DOUBLEEXCLAMATIONMARK,orUnicodescalarU+203C),andthecharacter( DOGFACE,orUnicodescalarU+1F436):

letdogString="Dog‼"

UTF-8RepresentationYoucanaccessaUTF-8representationofaStringbyiteratingoveritsutf8property.ThispropertyisoftypeString.UTF8View,whichisacollectionofunsigned8-bit(UInt8)values,oneforeachbyteinthestring’sUTF-8representation:

1 forcodeUnitindogString.utf8{

2 print("\(codeUnit)",terminator:"")

3 }

4 print("")

5 //Prints"68111103226128188240159144182"

Intheexampleabove,thefirstthreedecimalcodeUnitvalues(68,111,103)representthecharactersD,o,andg,whoseUTF-8representationisthesameastheirASCIIrepresentation.ThenextthreedecimalcodeUnitvalues(226,128,188)areathree-byteUTF-8representationoftheDOUBLEEXCLAMATIONMARKcharacter.ThelastfourcodeUnitvalues(240,159,144,182)areafour-byteUTF-8representationoftheDOGFACEcharacter.

UTF-16RepresentationYoucanaccessaUTF-16representationofaStringbyiteratingoveritsutf16property.ThispropertyisoftypeString.UTF16View,whichisacollectionofunsigned16-bit(UInt16)values,oneforeach16-bitcodeunitinthestring’sUTF-16representation:

1 forcodeUnitindogString.utf16{

2 print("\(codeUnit)",terminator:"")

3 }

4 print("")

5 //Prints"6811110382525535756374"

Again,thefirstthreecodeUnitvalues(68,111,103)representthecharactersD,o,andg,whoseUTF-16codeunitshavethesamevaluesasinthestring’sUTF-8representation(becausetheseUnicodescalarsrepresentASCIIcharacters).

ThefourthcodeUnitvalue(8252)isadecimalequivalentofthehexadecimalvalue203C,whichrepresentstheUnicodescalarU+203CfortheDOUBLEEXCLAMATIONMARKcharacter.ThischaractercanberepresentedasasinglecodeunitinUTF-16.

ThefifthandsixthcodeUnitvalues(55357and56374)areaUTF-16surrogatepairrepresentationoftheDOGFACEcharacter.Thesevaluesareahigh-surrogatevalueofU+D83D(decimalvalue55357)andalow-surrogatevalueofU+DC36(decimalvalue56374).

UnicodeScalarRepresentationYoucanaccessaUnicodescalarrepresentationofaStringvaluebyiteratingoveritsunicodeScalarsproperty.ThispropertyisoftypeUnicodeScalarView,whichisacollectionofvaluesoftypeUnicodeScalar.

EachUnicodeScalarhasavaluepropertythatreturnsthescalar’s21-bitvalue,representedwithinaUInt32value:

1 forscalarindogString.unicodeScalars{

2 print("\(scalar.value)",terminator:"")

3 }

4 print("")

5 //Prints"681111038252128054"

ThevaluepropertiesforthefirstthreeUnicodeScalarvalues(68,111,103)onceagainrepresentthecharactersD,o,andg.

ThefourthcodeUnitvalue(8252)isagainadecimalequivalentofthehexadecimalvalue203C,whichrepresentstheUnicodescalarU+203CfortheDOUBLEEXCLAMATIONMARKcharacter.

ThevaluepropertyofthefifthandfinalUnicodeScalar,128054,isadecimalequivalentofthehexadecimalvalue1F436,whichrepresentstheUnicodescalarU+1F436fortheDOGFACEcharacter.

Asanalternativetoqueryingtheirvalueproperties,eachUnicodeScalarvaluecanalsobeusedtoconstructanewStringvalue,suchaswithstringinterpolation:

1 forscalarindogString.unicodeScalars{

2 print("\(scalar)")

3 }

4 //D

5 //o

6 //g

7 //‼

8 //

CollectionTypes

Swiftprovidesthreeprimarycollectiontypes,knownasarrays,sets,anddictionaries,forstoringcollectionsofvalues.Arraysareorderedcollectionsofvalues.Setsareunorderedcollectionsofuniquevalues.Dictionariesareunorderedcollectionsofkey-valueassociations.

Arrays,sets,anddictionariesinSwiftarealwaysclearaboutthetypesofvaluesandkeysthattheycanstore.Thismeansthatyoucannotinsertavalueofthewrongtypeintoacollectionbymistake.Italsomeansyoucanbeconfidentaboutthetypeofvaluesyouwillretrievefromacollection.

NOTE

Swift’sarray,set,anddictionarytypesareimplementedasgenericcollections.Formoreaboutgenerictypesandcollections,seeGenerics.

MutabilityofCollections

Ifyoucreateanarray,aset,oradictionary,andassignittoavariable,thecollectionthatiscreatedwillbemutable.Thismeansthatyoucanchange(ormutate)thecollectionafterit’screatedbyadding,removing,orchangingitemsinthecollection.Ifyouassignanarray,aset,oradictionarytoaconstant,that

collectionisimmutable,anditssizeandcontentscannotbechanged.

NOTE

Itisgoodpracticetocreateimmutablecollectionsinallcaseswherethecollectiondoesnotneedtochange.DoingsomakesiteasierforyoutoreasonaboutyourcodeandenablestheSwiftcompilertooptimizetheperformanceofthecollectionsyoucreate.

Arrays

Anarraystoresvaluesofthesametypeinanorderedlist.Thesamevaluecanappearinanarraymultipletimesatdifferentpositions.

NOTE

Swift’sArraytypeisbridgedtoFoundation’sNSArrayclass.

FormoreinformationaboutusingArraywithFoundationandCocoa,seeBridgingBetweenArrayandNSArray.

ArrayTypeShorthandSyntaxThetypeofaSwiftarrayiswritteninfullasArray<Element>,whereElementisthetypeofvaluesthearrayisallowedtostore.Youcanalsowritethetypeofanarrayinshorthandformas[Element].Althoughthetwoformsarefunctionallyidentical,theshorthandformispreferredandisusedthroughoutthisguidewhenreferringtothetypeofanarray.

CreatinganEmptyArrayYoucancreateanemptyarrayofacertaintypeusinginitializersyntax:

1 varsomeInts=[Int]()

2 print("someIntsisoftype[Int]with\(someInts.count)

items.")

3 //Prints"someIntsisoftype[Int]with0items."

NotethatthetypeofthesomeIntsvariableisinferredtobe[Int]fromthetypeoftheinitializer.

Alternatively,ifthecontextalreadyprovidestypeinformation,suchasafunctionargumentoranalreadytypedvariableorconstant,youcancreateanemptyarraywithanemptyarrayliteral,whichiswrittenas[](anemptypairofsquarebrackets):

1 someInts.append(3)

2 //someIntsnowcontains1valueoftypeInt

3 someInts=[]

4 //someIntsisnowanemptyarray,butisstilloftype[Int]

CreatinganArraywithaDefaultValueSwift’sArraytypealsoprovidesaninitializerforcreatinganarrayofacertainsizewithallofitsvaluessettothesamedefaultvalue.Youpassthisinitializeradefaultvalueoftheappropriatetype(calledrepeating):andthenumberoftimesthatvalueisrepeatedinthenewarray(calledcount):

1 varthreeDoubles=Array(repeating:0.0,count:3)

2 //threeDoublesisoftype[Double],andequals[0.0,0.0,0.0]

CreatinganArraybyAddingTwoArraysTogetherYoucancreateanewarraybyaddingtogethertwoexistingarrayswithcompatibletypeswiththeadditionoperator(+).Thenewarray’stypeisinferredfromthetypeofthetwoarraysyouaddtogether:

1 varanotherThreeDoubles=Array(repeating:2.5,count:3)

2 //anotherThreeDoublesisoftype[Double],andequals[2.5,

2.5,2.5]

3

4 varsixDoubles=threeDoubles+anotherThreeDoubles

5 //sixDoublesisinferredas[Double],andequals[0.0,0.0,

0.0,2.5,2.5,2.5]

CreatinganArraywithanArrayLiteralYoucanalsoinitializeanarraywithanarrayliteral,whichisashorthandwaytowriteoneormorevaluesasanarraycollection.Anarrayliteraliswrittenasalistofvalues,separatedbycommas,surroundedbyapairofsquarebrackets:

[ value1 , value2 , value3 ]

TheexamplebelowcreatesanarraycalledshoppingListtostoreStringvalues:

1 varshoppingList:[String]=["Eggs","Milk"]

2 //shoppingListhasbeeninitializedwithtwoinitialitems

TheshoppingListvariableisdeclaredas“anarrayofstringvalues”,writtenas[String].BecausethisparticulararrayhasspecifiedavaluetypeofString,itisallowedtostoreStringvaluesonly.Here,theshoppingListarrayisinitializedwithtwoStringvalues("Eggs"and"Milk"),writtenwithinanarrayliteral.

NOTE

TheshoppingListarrayisdeclaredasavariable(withthevarintroducer)andnotaconstant(withtheletintroducer)becausemoreitemsareaddedtotheshoppinglistintheexamplesbelow.

Inthiscase,thearrayliteralcontainstwoStringvaluesandnothingelse.ThismatchesthetypeoftheshoppingListvariable’sdeclaration(anarraythatcanonlycontainStringvalues),andsotheassignmentofthearrayliteralispermittedasawaytoinitializeshoppingListwithtwoinitialitems.

ThankstoSwift’stypeinference,youdon’thavetowritethetypeofthearrayifyou’reinitializingitwithanarrayliteralcontainingvaluesofthesametype.TheinitializationofshoppingListcouldhavebeenwritteninashorterforminstead:

varshoppingList=["Eggs","Milk"]

Becauseallvaluesinthearrayliteralareofthesametype,Swiftcaninferthat[String]isthecorrecttypetousefortheshoppingListvariable.

AccessingandModifyinganArrayYouaccessandmodifyanarraythroughitsmethodsandproperties,orbyusingsubscriptsyntax.

Tofindoutthenumberofitemsinanarray,checkitsread-onlycountproperty:

1 print("Theshoppinglistcontains\(shoppingList.count)

items.")

2 //Prints"Theshoppinglistcontains2items."

UsetheBooleanisEmptypropertyasashortcutforcheckingwhetherthecountpropertyisequalto0:

1 ifshoppingList.isEmpty{

2 print("Theshoppinglistisempty.")

3 }else{

4 print("Theshoppinglistisnotempty.")

5 }

6 //Prints"Theshoppinglistisnotempty."

Youcanaddanewitemtotheendofanarraybycallingthearray’sappend(_:)method:

1 shoppingList.append("Flour")

2 //shoppingListnowcontains3items,andsomeoneismaking

pancakes

Alternatively,appendanarrayofoneormorecompatibleitemswiththeadditionassignmentoperator(+=):

1 shoppingList+=["BakingPowder"]

2 //shoppingListnowcontains4items

3 shoppingList+=["ChocolateSpread","Cheese","Butter"]

4 //shoppingListnowcontains7items

Retrieveavaluefromthearraybyusingsubscriptsyntax,passingtheindexofthevalueyouwanttoretrievewithinsquarebracketsimmediatelyafterthenameofthearray:

1 varfirstItem=shoppingList[0]

2 //firstItemisequalto"Eggs"

NOTE

Thefirstiteminthearrayhasanindexof0,not1.ArraysinSwiftarealwayszero-indexed.

Youcanusesubscriptsyntaxtochangeanexistingvalueatagivenindex:

1 shoppingList[0]="Sixeggs"

2 //thefirstiteminthelistisnowequalto"Sixeggs"rather

than"Eggs"

Whenyouusesubscriptsyntax,theindexyouspecifyneedstobevalid.Forexample,writingshoppingList[shoppingList.count]="Salt"totrytoappendanitemtotheendofthearrayresultsinaruntimeerror.

Youcanalsousesubscriptsyntaxtochangearangeofvaluesatonce,evenifthereplacementsetofvalueshasadifferentlengththantherangeyouarereplacing.Thefollowingexamplereplaces"ChocolateSpread","Cheese",and"Butter"

with"Bananas"and"Apples":

1 shoppingList[4...6]=["Bananas","Apples"]

2 //shoppingListnowcontains6items

Toinsertanitemintothearrayataspecifiedindex,callthearray’sinsert(_:at:)method:

1 shoppingList.insert("MapleSyrup",at:0)

2 //shoppingListnowcontains7items

3 //"MapleSyrup"isnowthefirstiteminthelist

Thiscalltotheinsert(_:at:)methodinsertsanewitemwithavalueof"MapleSyrup"attheverybeginningoftheshoppinglist,indicatedbyanindexof0.

Similarly,youremoveanitemfromthearraywiththeremove(at:)method.Thismethodremovestheitematthespecifiedindexandreturnstheremoveditem(althoughyoucanignorethereturnedvalueifyoudonotneedit):

1 letmapleSyrup=shoppingList.remove(at:0)

2 //theitemthatwasatindex0hasjustbeenremoved

3 //shoppingListnowcontains6items,andnoMapleSyrup

4 //themapleSyrupconstantisnowequaltotheremoved"Maple

Syrup"string

NOTE

Ifyoutrytoaccessormodifyavalueforanindexthatisoutsideofanarray’sexistingbounds,youwilltriggeraruntimeerror.Youcancheckthatanindexisvalidbeforeusingitbycomparingittothearray’scountproperty.Thelargestvalidindexinanarrayiscount-1becausearraysareindexedfromzero—however,whencountis0(meaningthearrayisempty),therearenovalidindexes.

Anygapsinanarrayareclosedwhenanitemisremoved,andsothevalueatindex0isonceagainequalto"Sixeggs":

1 firstItem=shoppingList[0]

2 //firstItemisnowequalto"Sixeggs"

Ifyouwanttoremovethefinalitemfromanarray,usetheremoveLast()methodratherthantheremove(at:)methodtoavoidtheneedtoquerythearray’scountproperty.Liketheremove(at:)method,removeLast()returnstheremoveditem:

1 letapples=shoppingList.removeLast()

2 //thelastiteminthearrayhasjustbeenremoved

3 //shoppingListnowcontains5items,andnoapples

4 //theapplesconstantisnowequaltotheremoved"Apples"

string

IteratingOveranArrayYoucaniterateovertheentiresetofvaluesinanarraywiththefor-inloop:

1 foriteminshoppingList{

2 print(item)

3 }

4 //Sixeggs

5 //Milk

6 //Flour

7 //BakingPowder

8 //Bananas

Ifyouneedtheintegerindexofeachitemaswellasitsvalue,usetheenumerated()methodtoiterateoverthearrayinstead.Foreachiteminthearray,theenumerated()methodreturnsatuplecomposedofanintegerandtheitem.Theintegersstartatzeroandcountupbyoneforeachitem;ifyouenumerateoverawholearray,theseintegersmatchtheitems’indices.Youcandecomposethetupleintotemporaryconstantsorvariablesaspartoftheiteration:

1 for(index,value)inshoppingList.enumerated(){

2 print("Item\(index+1):\(value)")

3 }

4 //Item1:Sixeggs

5 //Item2:Milk

6 //Item3:Flour

7 //Item4:BakingPowder

8 //Item5:Bananas

Formoreaboutthefor-inloop,seeFor-InLoops.

Sets

Asetstoresdistinctvaluesofthesametypeinacollectionwithnodefinedordering.Youcanuseasetinsteadofanarraywhentheorderofitemsisnotimportant,orwhenyouneedtoensurethatanitemonlyappearsonce.

NOTE

Swift’sSettypeisbridgedtoFoundation’sNSSetclass.

FormoreinformationaboutusingSetwithFoundationandCocoa,seeBridgingBetweenSetandNSSet.

HashValuesforSetTypesAtypemustbehashableinordertobestoredinaset—thatis,thetypemustprovideawaytocomputeahashvalueforitself.AhashvalueisanIntvaluethatisthesameforallobjectsthatcompareequally,suchthatifa==b,itfollowsthata.hashValue==b.hashValue.

AllofSwift’sbasictypes(suchasString,Int,Double,andBool)arehashablebydefault,andcanbeusedassetvaluetypesordictionarykeytypes.Enumeration

casevalueswithoutassociatedvalues(asdescribedinEnumerations)arealsohashablebydefault.

NOTE

YoucanuseyourowncustomtypesassetvaluetypesordictionarykeytypesbymakingthemconformtotheHashableprotocolfromSwift’sstandardlibrary.TypesthatconformtotheHashableprotocolmustprovideagettableIntpropertycalledhashValue.Thevaluereturnedbyatype’shashValuepropertyisnotrequiredtobethesameacrossdifferentexecutionsofthesameprogram,orindifferentprograms.

BecausetheHashableprotocolconformstoEquatable,conformingtypesmustalsoprovideanimplementationoftheequalsoperator(==).TheEquatableprotocolrequiresanyconformingimplementationof==tobeanequivalencerelation.Thatis,animplementationof==mustsatisfythefollowingthreeconditions,forallvaluesa,b,andc:

a==a(Reflexivity)

a==bimpliesb==a(Symmetry)

a==b&&b==cimpliesa==c(Transitivity)

Formoreinformationaboutconformingtoprotocols,seeProtocols.

SetTypeSyntaxThetypeofaSwiftsetiswrittenasSet<Element>,whereElementisthetypethatthesetisallowedtostore.Unlikearrays,setsdonothaveanequivalentshorthandform.

CreatingandInitializinganEmptySetYoucancreateanemptysetofacertaintypeusinginitializersyntax:

1 varletters=Set<Character>()

2 print("lettersisoftypeSet<Character>with\(letters.count)

items.")

3 //Prints"lettersisoftypeSet<Character>with0items."

NOTE

ThetypeofthelettersvariableisinferredtobeSet<Character>,fromthetypeoftheinitializer.

Alternatively,ifthecontextalreadyprovidestypeinformation,suchasafunctionargumentoranalreadytypedvariableorconstant,youcancreateanemptysetwithanemptyarrayliteral:

1 letters.insert("a")

2 //lettersnowcontains1valueoftypeCharacter

3 letters=[]

4 //lettersisnowanemptyset,butisstilloftype

Set<Character>

CreatingaSetwithanArrayLiteralYoucanalsoinitializeasetwithanarrayliteral,asashorthandwaytowriteoneormorevaluesasasetcollection.

TheexamplebelowcreatesasetcalledfavoriteGenrestostoreStringvalues:

1 varfavoriteGenres:Set<String>=["Rock","Classical","Hip

hop"]

2 //favoriteGenreshasbeeninitializedwiththreeinitialitems

ThefavoriteGenresvariableisdeclaredas“asetofStringvalues”,writtenasSet<String>.BecausethisparticularsethasspecifiedavaluetypeofString,itisonlyallowedtostoreStringvalues.Here,thefavoriteGenressetisinitializedwiththreeStringvalues("Rock","Classical",and"Hiphop"),writtenwithinanarrayliteral.

NOTE

ThefavoriteGenressetisdeclaredasavariable(withthevarintroducer)andnotaconstant(withtheletintroducer)becauseitemsareaddedandremovedintheexamplesbelow.

Asettypecannotbeinferredfromanarrayliteralalone,sothetypeSetmustbeexplicitlydeclared.However,becauseofSwift’stypeinference,youdon’thavetowritethetypeoftheset’selementsifyou’reinitializingitwithanarrayliteralthatcontainsvaluesofjustonetype.TheinitializationoffavoriteGenrescouldhavebeenwritteninashorterforminstead:

varfavoriteGenres:Set=["Rock","Classical","Hiphop"]

Becauseallvaluesinthearrayliteralareofthesametype,SwiftcaninferthatSet<String>isthecorrecttypetouseforthefavoriteGenresvariable.

AccessingandModifyingaSetYouaccessandmodifyasetthroughitsmethodsandproperties.

Tofindoutthenumberofitemsinaset,checkitsread-onlycountproperty:

1 print("Ihave\(favoriteGenres.count)favoritemusicgenres.")

2 //Prints"Ihave3favoritemusicgenres."

UsetheBooleanisEmptypropertyasashortcutforcheckingwhetherthecountpropertyisequalto0:

1 iffavoriteGenres.isEmpty{

2 print("Asfarasmusicgoes,I'mnotpicky.")

3 }else{

4 print("Ihaveparticularmusicpreferences.")

5 }

6 //Prints"Ihaveparticularmusicpreferences."

Youcanaddanewitemintoasetbycallingtheset’sinsert(_:)method:

1 favoriteGenres.insert("Jazz")

2 //favoriteGenresnowcontains4items

Youcanremoveanitemfromasetbycallingtheset’sremove(_:)method,whichremovestheitemifit’samemberoftheset,andreturnstheremovedvalue,orreturnsnilifthesetdidnotcontainit.Alternatively,allitemsinasetcanberemovedwithitsremoveAll()method.

1 ifletremovedGenre=favoriteGenres.remove("Rock"){

2 print("\(removedGenre)?I'moverit.")

3 }else{

4 print("Inevermuchcaredforthat.")

5 }

6 //Prints"Rock?I'moverit."

Tocheckwhetherasetcontainsaparticularitem,usethecontains(_:)method.

1 iffavoriteGenres.contains("Funk"){

2 print("Igetuponthegoodfoot.")

3 }else{

4 print("It'stoofunkyinhere.")

5 }

6 //Prints"It'stoofunkyinhere."

IteratingOveraSetYoucaniterateoverthevaluesinasetwithafor-inloop.

1 forgenreinfavoriteGenres{

2 print("\(genre)")

3 }

4 //Classical

5 //Jazz

6 //Hiphop

Formoreaboutthefor-inloop,seeFor-InLoops.

Swift’sSettypedoesnothaveadefinedordering.Toiterateoverthevaluesofasetinaspecificorder,usethesorted()method,whichreturnstheset’selementsasanarraysortedusingthe<operator.

1 forgenreinfavoriteGenres.sorted(){

2 print("\(genre)")

3 }

4 //Classical

5 //Hiphop

6 //Jazz

PerformingSetOperations

Youcanefficientlyperformfundamentalsetoperations,suchascombiningtwosetstogether,determiningwhichvaluestwosetshaveincommon,ordeterminingwhethertwosetscontainall,some,ornoneofthesamevalues.

FundamentalSetOperationsTheillustrationbelowdepictstwosets—aandb—withtheresultsofvarioussetoperationsrepresentedbytheshadedregions.

Usetheintersection(_:)methodtocreateanewsetwithonlythevaluescommontobothsets.

UsethesymmetricDifference(_:)methodtocreateanewsetwithvaluesineitherset,butnotboth.

Usetheunion(_:)methodtocreateanewsetwithallofthevaluesinbothsets.

Usethesubtracting(_:)methodtocreateanewsetwithvaluesnotinthespecifiedset.

1 letoddDigits:Set=[1,3,5,7,9]

2 letevenDigits:Set=[0,2,4,6,8]

3 letsingleDigitPrimeNumbers:Set=[2,3,5,7]

4

5 oddDigits.union(evenDigits).sorted()

6 //[0,1,2,3,4,5,6,7,8,9]

7 oddDigits.intersection(evenDigits).sorted()

8 //[]

9 oddDigits.subtracting(singleDigitPrimeNumbers).sorted()

10 //[1,9]

11 oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()

12 //[1,2,9]

SetMembershipandEqualityTheillustrationbelowdepictsthreesets—a,bandc—withoverlappingregionsrepresentingelementssharedamongsets.Setaisasupersetofsetb,becauseacontainsallelementsinb.Conversely,setbisasubsetofseta,becauseallelementsinbarealsocontainedbya.Setbandsetcaredisjointwithoneanother,becausetheysharenoelementsincommon.

Usethe“isequal”operator(==)todeterminewhethertwosetscontainallofthesamevalues.

UsetheisSubset(of:)methodtodeterminewhetherallofthevaluesofasetarecontainedinthespecifiedset.

UsetheisSuperset(of:)methodtodeterminewhetherasetcontainsallofthevaluesinaspecifiedset.

UsetheisStrictSubset(of:)orisStrictSuperset(of:)methodstodeterminewhetherasetisasubsetorsuperset,butnotequalto,aspecifiedset.

UsetheisDisjoint(with:)methodtodeterminewhethertwosetshavenovaluesincommon.

1 lethouseAnimals:Set=["" ," "]

2 letfarmAnimals:Set=[" ","" ,"" ,"" ," "]

3 letcityAnimals:Set=["" ," "]

4

5 houseAnimals.isSubset(of:farmAnimals)

6 //true

7 farmAnimals.isSuperset(of:houseAnimals)

8 //true

9 farmAnimals.isDisjoint(with:cityAnimals)

10 //true

Dictionaries

Adictionarystoresassociationsbetweenkeysofthesametypeandvaluesofthesametypeinacollectionwithnodefinedordering.Eachvalueisassociatedwithauniquekey,whichactsasanidentifierforthatvaluewithinthedictionary.Unlikeitemsinanarray,itemsinadictionarydonothaveaspecifiedorder.Youuseadictionarywhenyouneedtolookupvaluesbasedontheiridentifier,inmuchthesamewaythatareal-worlddictionaryisusedtolookupthedefinitionforaparticularword.

NOTE

Swift’sDictionarytypeisbridgedtoFoundation’sNSDictionaryclass.

FormoreinformationaboutusingDictionarywithFoundationandCocoa,seeBridgingBetweenDictionaryandNSDictionary.

DictionaryTypeShorthandSyntaxThetypeofaSwiftdictionaryiswritteninfullasDictionary<Key,Value>,whereKeyisthetypeofvaluethatcanbeusedasadictionarykey,andValueisthetypeofvaluethatthedictionarystoresforthosekeys.

NOTE

AdictionaryKeytypemustconformtotheHashableprotocol,likeaset’svaluetype.

Youcanalsowritethetypeofadictionaryinshorthandformas[Key:Value].Althoughthetwoformsarefunctionallyidentical,theshorthandformispreferredandisusedthroughoutthisguidewhenreferringtothetypeofadictionary.

CreatinganEmptyDictionaryAswitharrays,youcancreateanemptyDictionaryofacertaintypebyusinginitializersyntax:

1 varnamesOfIntegers=[Int:String]()

2 //namesOfIntegersisanempty[Int:String]dictionary

Thisexamplecreatesanemptydictionaryoftype[Int:String]tostorehuman-readablenamesofintegervalues.ItskeysareoftypeInt,anditsvaluesareoftypeString.

Ifthecontextalreadyprovidestypeinformation,youcancreateanemptydictionarywithanemptydictionaryliteral,whichiswrittenas[:](acoloninsideapairofsquarebrackets):

1 namesOfIntegers[16]="sixteen"

2 //namesOfIntegersnowcontains1key-valuepair

3 namesOfIntegers=[:]

4 //namesOfIntegersisonceagainanemptydictionaryoftype

[Int:String]

CreatingaDictionarywithaDictionaryLiteralYoucanalsoinitializeadictionarywithadictionaryliteral,whichhasasimilarsyntaxtothearrayliteralseenearlier.Adictionaryliteralisashorthandwaytowriteoneormorekey-valuepairsasaDictionarycollection.

Akey-valuepairisacombinationofakeyandavalue.Inadictionaryliteral,thekeyandvalueineachkey-valuepairareseparatedbyacolon.Thekey-valuepairsarewrittenasalist,separatedbycommas,surroundedbyapairofsquarebrackets:

[ key1 : value1 , key2 : value2 , key3 : value3 ]

Theexamplebelowcreatesadictionarytostorethenamesofinternationalairports.Inthisdictionary,thekeysarethree-letterInternationalAirTransportAssociationcodes,andthevaluesareairportnames:

varairports:[String:String]=["YYZ":"TorontoPearson",

"DUB":"Dublin"]

Theairportsdictionaryisdeclaredashavingatypeof[String:String],whichmeans“aDictionarywhosekeysareoftypeString,andwhosevaluesarealsooftypeString”.

NOTE

Theairportsdictionaryisdeclaredasavariable(withthevarintroducer),andnotaconstant(withtheletintroducer),becausemoreairportsareaddedtothedictionaryintheexamplesbelow.

Theairportsdictionaryisinitializedwithadictionaryliteralcontainingtwokey-valuepairs.Thefirstpairhasakeyof"YYZ"andavalueof"TorontoPearson".Thesecondpairhasakeyof"DUB"andavalueof"Dublin".

ThisdictionaryliteralcontainstwoString:Stringpairs.Thiskey-valuetypematchesthetypeoftheairportsvariabledeclaration(adictionarywithonlyStringkeys,andonlyStringvalues),andsotheassignmentofthedictionaryliteralispermittedasawaytoinitializetheairportsdictionarywithtwoinitialitems.

Aswitharrays,youdon’thavetowritethetypeofthedictionaryifyou’reinitializingitwithadictionaryliteralwhosekeysandvalueshaveconsistenttypes.Theinitializationofairportscouldhavebeenwritteninashorterforminstead:

varairports=["YYZ":"TorontoPearson","DUB":"Dublin"]

Becauseallkeysintheliteralareofthesametypeaseachother,andlikewiseallvaluesareofthesametypeaseachother,Swiftcaninferthat[String:String]isthecorrecttypetousefortheairportsdictionary.

AccessingandModifyingaDictionaryYouaccessandmodifyadictionarythroughitsmethodsandproperties,orbyusingsubscriptsyntax.

Aswithanarray,youfindoutthenumberofitemsinaDictionarybycheckingitsread-onlycountproperty:

1 print("Theairportsdictionarycontains\(airports.count)

items.")

2 //Prints"Theairportsdictionarycontains2items."

UsetheBooleanisEmptypropertyasashortcutforcheckingwhetherthecountpropertyisequalto0:

1 ifairports.isEmpty{

2 print("Theairportsdictionaryisempty.")

3 }else{

4 print("Theairportsdictionaryisnotempty.")

5 }

6 //Prints"Theairportsdictionaryisnotempty."

Youcanaddanewitemtoadictionarywithsubscriptsyntax.Useanewkeyoftheappropriatetypeasthesubscriptindex,andassignanewvalueoftheappropriatetype:

1 airports["LHR"]="London"

2 //theairportsdictionarynowcontains3items

Youcanalsousesubscriptsyntaxtochangethevalueassociatedwithaparticularkey:

1 airports["LHR"]="LondonHeathrow"

2 //thevaluefor"LHR"hasbeenchangedto"LondonHeathrow"

Asanalternativetosubscripting,useadictionary’supdateValue(_:forKey:)methodtosetorupdatethevalueforaparticularkey.Likethesubscriptexamplesabove,theupdateValue(_:forKey:)methodsetsavalueforakeyifnoneexists,orupdatesthevalueifthatkeyalreadyexists.Unlikeasubscript,however,theupdateValue(_:forKey:)methodreturnstheoldvalueafterperforminganupdate.Thisenablesyoutocheckwhetherornotanupdatetookplace.

TheupdateValue(_:forKey:)methodreturnsanoptionalvalueofthedictionary’svaluetype.ForadictionarythatstoresStringvalues,forexample,themethodreturnsavalueoftypeString?,or“optionalString”.Thisoptionalvaluecontainstheoldvalueforthatkeyifoneexistedbeforetheupdate,ornilifnovalueexisted:

1 ifletoldValue=airports.updateValue("DublinAirport",

forKey:"DUB"){

2 print("TheoldvalueforDUBwas\(oldValue).")

3 }

4 //Prints"TheoldvalueforDUBwasDublin."

Youcanalsousesubscriptsyntaxtoretrieveavaluefromthedictionaryforaparticularkey.Becauseitispossibletorequestakeyforwhichnovalueexists,adictionary’ssubscriptreturnsanoptionalvalueofthedictionary’svaluetype.Ifthedictionarycontainsavaluefortherequestedkey,thesubscriptreturnsanoptionalvaluecontainingtheexistingvalueforthatkey.Otherwise,thesubscriptreturnsnil:

1 ifletairportName=airports["DUB"]{

2 print("Thenameoftheairportis\(airportName).")

3 }else{

4 print("Thatairportisnotintheairportsdictionary.")

5 }

6 //Prints"ThenameoftheairportisDublinAirport."

Youcanusesubscriptsyntaxtoremoveakey-valuepairfromadictionarybyassigningavalueofnilforthatkey:

1 airports["APL"]="AppleInternational"

2 //"AppleInternational"isnottherealairportforAPL,so

deleteit

3 airports["APL"]=nil

4 //APLhasnowbeenremovedfromthedictionary

Alternatively,removeakey-valuepairfromadictionarywiththeremoveValue(forKey:)method.Thismethodremovesthekey-valuepairifitexistsandreturnstheremovedvalue,orreturnsnilifnovalueexisted:

1 ifletremovedValue=airports.removeValue(forKey:"DUB"){

2 print("Theremovedairport'snameis\(removedValue).")

3 }else{

4 print("Theairportsdictionarydoesnotcontainavaluefor

DUB.")

5 }

6 //Prints"Theremovedairport'snameisDublinAirport."

IteratingOveraDictionaryYoucaniterateoverthekey-valuepairsinadictionarywithafor-inloop.Eachiteminthedictionaryisreturnedasa(key,value)tuple,andyoucandecomposethetuple’smembersintotemporaryconstantsorvariablesaspartoftheiteration:

1 for(airportCode,airportName)inairports{

2 print("\(airportCode):\(airportName)")

3 }

4 //LHR:LondonHeathrow

5 //YYZ:TorontoPearson

Formoreaboutthefor-inloop,seeFor-InLoops.

Youcanalsoretrieveaniterablecollectionofadictionary’skeysorvaluesbyaccessingitskeysandvaluesproperties:

1 forairportCodeinairports.keys{

2 print("Airportcode:\(airportCode)")

3 }

4 //Airportcode:LHR

5 //Airportcode:YYZ

6

7 forairportNameinairports.values{

8 print("Airportname:\(airportName)")

9 }

10 //Airportname:LondonHeathrow

11 //Airportname:TorontoPearson

Ifyouneedtouseadictionary’skeysorvalueswithanAPIthattakesanArrayinstance,initializeanewarraywiththekeysorvaluesproperty:

1 letairportCodes=[String](airports.keys)

2 //airportCodesis["LHR","YYZ"]

3

4 letairportNames=[String](airports.values)

5 //airportNamesis["LondonHeathrow","TorontoPearson"]

Swift’sDictionarytypedoesnothaveadefinedordering.Toiterateoverthekeysorvaluesofadictionaryinaspecificorder,usethesorted()methodonitskeysorvaluesproperty.

ControlFlow

Swiftprovidesavarietyofcontrolflowstatements.Theseincludewhileloopstoperformataskmultipletimes;if,guard,andswitchstatementstoexecutedifferentbranchesofcodebasedoncertainconditions;andstatementssuchasbreakandcontinuetotransfertheflowofexecutiontoanotherpointinyourcode.

Swiftalsoprovidesafor-inloopthatmakesiteasytoiterateoverarrays,dictionaries,ranges,strings,andothersequences.

Swift’sswitchstatementisconsiderablymorepowerfulthanitscounterpartinmanyC-likelanguages.Casescanmatchmanydifferentpatterns,includingintervalmatches,tuples,andcaststoaspecifictype.Matchedvaluesinaswitchcasecanbeboundtotemporaryconstantsorvariablesforusewithinthecase’sbody,andcomplexmatchingconditionscanbeexpressedwithawhereclauseforeachcase.

For-InLoops

Youusethefor-inlooptoiterateoverasequence,suchasitemsinanarray,rangesofnumbers,orcharactersinastring.

Thisexampleusesafor-inlooptoiterateovertheitemsinanarray:

1 letnames=["Anna","Alex","Brian","Jack"]

2 fornameinnames{

3 print("Hello,\(name)!")

4 }

5 //Hello,Anna!

6 //Hello,Alex!

7 //Hello,Brian!

8 //Hello,Jack!

Youcanalsoiterateoveradictionarytoaccessitskey-valuepairs.Eachiteminthedictionaryisreturnedasa(key,value)tuplewhenthedictionaryisiterated,andyoucandecomposethe(key,value)tuple’smembersasexplicitlynamedconstantsforusewithinthebodyofthefor-inloop.Inthecodeexamplebelow,thedictionary’skeysaredecomposedintoaconstantcalledanimalName,andthedictionary’svaluesaredecomposedintoaconstantcalledlegCount.

1 letnumberOfLegs=["spider":8,"ant":6,"cat":4]

2 for(animalName,legCount)innumberOfLegs{

3 print("\(animalName)shave\(legCount)legs")

4 }

5 //catshave4legs

6 //antshave6legs

7 //spidershave8legs

ThecontentsofaDictionaryareinherentlyunordered,anditeratingoverthemdoesnotguaranteetheorderinwhichtheywillberetrieved.Inparticular,theorderyouinsertitemsintoaDictionarydoesn’tdefinetheordertheyareiterated.Formoreaboutarraysanddictionaries,seeCollectionTypes.

Youcanalsousefor-inloopswithnumericranges.Thisexampleprintsthefirstfewentriesinafive-timestable:

1 forindexin1...5{

2 print("\(index)times5is\(index*5)")

3 }

4 //1times5is5

5 //2times5is10

6 //3times5is15

7 //4times5is20

8 //5times5is25

Thesequencebeingiteratedoverisarangeofnumbersfrom1to5,inclusive,asindicatedbytheuseoftheclosedrangeoperator(...).Thevalueofindexissettothefirstnumberintherange(1),andthestatementsinsidetheloopareexecuted.Inthiscase,theloopcontainsonlyonestatement,whichprintsanentryfromthefive-timestableforthecurrentvalueofindex.Afterthestatementisexecuted,thevalueofindexisupdatedtocontainthesecondvalueintherange(2),andtheprint(_:separator:terminator:)functioniscalledagain.Thisprocesscontinuesuntiltheendoftherangeisreached.

Intheexampleabove,indexisaconstantwhosevalueisautomaticallysetatthestartofeachiterationoftheloop.Assuch,indexdoesnothavetobedeclaredbeforeitisused.Itisimplicitlydeclaredsimplybyitsinclusionintheloopdeclaration,withouttheneedforaletdeclarationkeyword.

Ifyoudon’tneedeachvaluefromasequence,youcanignorethevaluesbyusinganunderscoreinplaceofavariablename.

1 letbase=3

2 letpower=10

3 varanswer=1

4 for_in1...power{

5 answer*=base

6 }

7 print("\(base)tothepowerof\(power)is\(answer)")

8 //Prints"3tothepowerof10is59049"

Theexampleabovecalculatesthevalueofonenumbertothepowerofanother(inthiscase,3tothepowerof10).Itmultipliesastartingvalueof1(thatis,3tothepowerof0)by3,tentimes,usingaclosedrangethatstartswith1andendswith10.Forthiscalculation,theindividualcountervalueseachtimethroughtheloopareunnecessary—thecodesimplyexecutestheloopthecorrectnumberoftimes.Theunderscorecharacter(_)usedinplaceofaloopvariablecausestheindividualvaluestobeignoredanddoesnotprovideaccesstothecurrentvalueduringeachiterationoftheloop.

Insomesituations,youmightnotwanttouseclosedranges,whichincludeboth

endpoints.Considerdrawingthetickmarksforeveryminuteonawatchface.Youwanttodraw60tickmarks,startingwiththe0minute.Usethehalf-openrangeoperator(..<)toincludethelowerboundbutnottheupperbound.Formoreaboutranges,seeRangeOperators.

1 letminutes=60

2 fortickMarkin0..<minutes{

3 //renderthetickmarkeachminute(60times)

4 }

SomeusersmightwantfewertickmarksintheirUI.Theycouldpreferonemarkevery5minutesinstead.Usethestride(from:to:by:)functiontoskiptheunwantedmarks.

1 letminuteInterval=5

2 fortickMarkinstride(from:0,to:minutes,by:

minuteInterval){

3 //renderthetickmarkevery5minutes(0,5,10,15...

45,50,55)

4 }

Closedrangesarealsoavailable,byusingstride(from:through:by:)instead:

1 lethours=12

2 lethourInterval=3

3 fortickMarkinstride(from:3,through:hours,by:

hourInterval){

4 //renderthetickmarkevery3hours(3,6,9,12)

5 }

WhileLoops

Awhileloopperformsasetofstatementsuntilaconditionbecomesfalse.Thesekindsofloopsarebestusedwhenthenumberofiterationsisnotknownbeforethefirstiterationbegins.Swiftprovidestwokindsofwhileloops:

whileevaluatesitsconditionatthestartofeachpassthroughtheloop.

repeat-whileevaluatesitsconditionattheendofeachpassthroughtheloop.

WhileAwhileloopstartsbyevaluatingasinglecondition.Iftheconditionistrue,asetofstatementsisrepeateduntiltheconditionbecomesfalse.

Here’sthegeneralformofawhileloop:

while condition {

statements

}

ThisexampleplaysasimplegameofSnakesandLadders(alsoknownasChutesandLadders):

Therulesofthegameareasfollows:

Theboardhas25squares,andtheaimistolandonorbeyondsquare25.

Theplayer’sstartingsquareis“squarezero”,whichisjustoffthebottom-leftcorneroftheboard.

Eachturn,yourollasix-sideddiceandmovebythatnumberofsquares,followingthehorizontalpathindicatedbythedottedarrowabove.

Ifyourturnendsatthebottomofaladder,youmoveupthatladder.

Ifyourturnendsattheheadofasnake,youmovedownthatsnake.

ThegameboardisrepresentedbyanarrayofIntvalues.ItssizeisbasedonaconstantcalledfinalSquare,whichisusedtoinitializethearrayandalsotocheckforawinconditionlaterintheexample.Becausetheplayersstartofftheboard,on“squarezero”,theboardisinitializedwith26zeroIntvalues,not25.

1 letfinalSquare=25

2 varboard=[Int](repeating:0,count:finalSquare+1)

Somesquaresarethensettohavemorespecificvaluesforthesnakesandladders.Squareswithaladderbasehaveapositivenumbertomoveyouuptheboard,whereassquareswithasnakeheadhaveanegativenumbertomoveyoubackdowntheboard.

1 board[03]=+08;board[06]=+11;board[09]=+09;board[10]=

+02

2 board[14]=-10;board[19]=-11;board[22]=-02;board[24]=

-08

Square3containsthebottomofaladderthatmovesyouuptosquare11.Torepresentthis,board[03]isequalto+08,whichisequivalenttoanintegervalueof8(thedifferencebetween3and11).Toalignthevaluesandstatements,theunaryplusoperator(+i)isexplicitlyusedwiththeunaryminusoperator(-i)andnumberslowerthan10arepaddedwithzeros.(Neitherstylistictechniqueis

strictlynecessary,buttheyleadtoneatercode.)

1 varsquare=0

2 vardiceRoll=0

3 whilesquare<finalSquare{

4 //rollthedice

5 diceRoll+=1

6 ifdiceRoll==7{diceRoll=1}

7 //movebytherolledamount

8 square+=diceRoll

9 ifsquare<board.count{

10 //ifwe'restillontheboard,moveupordownfora

snakeoraladder

11 square+=board[square]

12 }

13 }

14 print("Gameover!")

Theexampleaboveusesaverysimpleapproachtodicerolling.Insteadofgeneratingarandomnumber,itstartswithadiceRollvalueof0.Eachtimethroughthewhileloop,diceRollisincrementedbyoneandisthencheckedtoseewhetherithasbecometoolarge.Wheneverthisreturnvalueequals7,thedicerollhasbecometoolargeandisresettoavalueof1.TheresultisasequenceofdiceRollvaluesthatisalways1,2,3,4,5,6,1,2andsoon.

Afterrollingthedice,theplayermovesforwardbydiceRollsquares.It’spossiblethatthedicerollmayhavemovedtheplayerbeyondsquare25,inwhichcasethegameisover.Tocopewiththisscenario,thecodechecksthatsquareislessthantheboardarray’scountproperty.Ifsquareisvalid,thevaluestoredinboard[square]isaddedtothecurrentsquarevaluetomovetheplayerupordownanyladdersorsnakes.

NOTE

Ifthischeckisnotperformed,board[square]mighttrytoaccessavalueoutsidetheboundsoftheboardarray,whichwouldtriggeraruntimeerror.

Thecurrentwhileloopexecutionthenends,andtheloop’sconditionischeckedtoseeiftheloopshouldbeexecutedagain.Iftheplayerhasmovedonorbeyondsquarenumber25,theloop’sconditionevaluatestofalseandthegameends.

Awhileloopisappropriateinthiscase,becausethelengthofthegameisnotclearatthestartofthewhileloop.Instead,theloopisexecuteduntilaparticularconditionissatisfied.

Repeat-WhileTheothervariationofthewhileloop,knownastherepeat-whileloop,performsasinglepassthroughtheloopblockfirst,beforeconsideringtheloop’scondition.Itthencontinuestorepeattheloopuntiltheconditionisfalse.

NOTE

Therepeat-whileloopinSwiftisanalogoustoado-whileloopinotherlanguages.

Here’sthegeneralformofarepeat-whileloop:

repeat{

statements

}while condition

Here’stheSnakesandLaddersexampleagain,writtenasarepeat-whileloopratherthanawhileloop.ThevaluesoffinalSquare,board,square,anddiceRollareinitializedinexactlythesamewayaswithawhileloop.

1 letfinalSquare=25

2 varboard=[Int](repeating:0,count:finalSquare+1)

3 board[03]=+08;board[06]=+11;board[09]=+09;board[10]=

+02

4 board[14]=-10;board[19]=-11;board[22]=-02;board[24]=

-08

5 varsquare=0

6 vardiceRoll=0

Inthisversionofthegame,thefirstactionintheloopistocheckforaladderorasnake.Noladderontheboardtakestheplayerstraighttosquare25,andsoitisn’tpossibletowinthegamebymovingupaladder.Therefore,it’ssafetocheckforasnakeoraladderasthefirstactionintheloop.

Atthestartofthegame,theplayerison“squarezero”.board[0]alwaysequals0andhasnoeffect.

1 repeat{

2 //moveupordownforasnakeorladder

3 square+=board[square]

4 //rollthedice

5 diceRoll+=1

6 ifdiceRoll==7{diceRoll=1}

7 //movebytherolledamount

8 square+=diceRoll

9 }whilesquare<finalSquare

10 print("Gameover!")

Afterthecodechecksforsnakesandladders,thediceisrolledandtheplayerismovedforwardbydiceRollsquares.Thecurrentloopexecutionthenends.

Theloop’scondition(whilesquare<finalSquare)isthesameasbefore,butthistimeit’snotevaluateduntiltheendofthefirstrunthroughtheloop.Thestructureoftherepeat-whileloopisbettersuitedtothisgamethanthewhileloopinthepreviousexample.Intherepeat-whileloopabove,square+=board[square]isalwaysexecutedimmediatelyaftertheloop’swhileconditionconfirmsthatsquareisstillontheboard.Thisbehaviorremovestheneedforthearrayboundscheckseeninthewhileloopversionofthegamedescribedearlier.

ConditionalStatements

Itisoftenusefultoexecutedifferentpiecesofcodebasedoncertainconditions.Youmightwanttorunanextrapieceofcodewhenanerroroccurs,ortodisplayamessagewhenavaluebecomestoohighortoolow.Todothis,youmakepartsofyourcodeconditional.

Swiftprovidestwowaystoaddconditionalbranchestoyourcode:theifstatementandtheswitchstatement.Typically,youusetheifstatementtoevaluatesimpleconditionswithonlyafewpossibleoutcomes.Theswitchstatementisbettersuitedtomorecomplexconditionswithmultiplepossiblepermutationsandisusefulinsituationswherepatternmatchingcanhelpselectanappropriatecodebranchtoexecute.

IfInitssimplestform,theifstatementhasasingleifcondition.Itexecutesasetofstatementsonlyifthatconditionistrue.

1 vartemperatureInFahrenheit=30

2 iftemperatureInFahrenheit<=32{

3 print("It'sverycold.Considerwearingascarf.")

4 }

5 //Prints"It'sverycold.Considerwearingascarf."

Theexampleabovecheckswhetherthetemperatureislessthanorequalto32degreesFahrenheit(thefreezingpointofwater).Ifitis,amessageisprinted.Otherwise,nomessageisprinted,andcodeexecutioncontinuesaftertheifstatement’sclosingbrace.

Theifstatementcanprovideanalternativesetofstatements,knownasanelseclause,forsituationswhentheifconditionisfalse.Thesestatementsareindicatedbytheelsekeyword.

1 temperatureInFahrenheit=40

2 iftemperatureInFahrenheit<=32{

3 print("It'sverycold.Considerwearingascarf.")

4 }else{

5 print("It'snotthatcold.Wearat-shirt.")

6 }

7 //Prints"It'snotthatcold.Wearat-shirt."

Oneofthesetwobranchesisalwaysexecuted.Becausethetemperaturehasincreasedto40degreesFahrenheit,itisnolongercoldenoughtoadvisewearingascarfandsotheelsebranchistriggeredinstead.

Youcanchainmultipleifstatementstogethertoconsideradditionalclauses.

1 temperatureInFahrenheit=90

2 iftemperatureInFahrenheit<=32{

3 print("It'sverycold.Considerwearingascarf.")

4 }elseiftemperatureInFahrenheit>=86{

5 print("It'sreallywarm.Don'tforgettowearsunscreen.")

6 }else{

7 print("It'snotthatcold.Wearat-shirt.")

8 }

9 //Prints"It'sreallywarm.Don'tforgettowearsunscreen."

Here,anadditionalifstatementwasaddedtorespondtoparticularlywarmtemperatures.Thefinalelseclauseremains,anditprintsaresponseforanytemperaturesthatareneithertoowarmnortoocold.

Thefinalelseclauseisoptional,however,andcanbeexcludedifthesetofconditionsdoesnotneedtobecomplete.

1 temperatureInFahrenheit=72

2 iftemperatureInFahrenheit<=32{

3 print("It'sverycold.Considerwearingascarf.")

4 }elseiftemperatureInFahrenheit>=86{

5 print("It'sreallywarm.Don'tforgettowearsunscreen.")

6 }

Becausethetemperatureisneithertoocoldnortoowarmtotriggertheiforelseifconditions,nomessageisprinted.

SwitchAswitchstatementconsidersavalueandcomparesitagainstseveralpossiblematchingpatterns.Itthenexecutesanappropriateblockofcode,basedonthefirstpatternthatmatchessuccessfully.Aswitchstatementprovidesanalternativetotheifstatementforrespondingtomultiplepotentialstates.

Initssimplestform,aswitchstatementcomparesavalueagainstoneormorevaluesofthesametype.

switch somevaluetoconsider {

case value1 :

respondtovalue1

case value2 ,

value3 :

respondtovalue2or3

default:

otherwise,dosomethingelse

}

Everyswitchstatementconsistsofmultiplepossiblecases,eachofwhichbeginswiththecasekeyword.Inadditiontocomparingagainstspecificvalues,Swiftprovidesseveralwaysforeachcasetospecifymorecomplexmatchingpatterns.Theseoptionsaredescribedlaterinthischapter.

Likethebodyofanifstatement,eachcaseisaseparatebranchofcodeexecution.Theswitchstatementdetermineswhichbranchshouldbeselected.

Thisprocedureisknownasswitchingonthevaluethatisbeingconsidered.

Everyswitchstatementmustbeexhaustive.Thatis,everypossiblevalueofthetypebeingconsideredmustbematchedbyoneoftheswitchcases.Ifit’snotappropriatetoprovideacaseforeverypossiblevalue,youcandefineadefaultcasetocoveranyvaluesthatarenotaddressedexplicitly.Thisdefaultcaseisindicatedbythedefaultkeyword,andmustalwaysappearlast.

ThisexampleusesaswitchstatementtoconsiderasinglelowercasecharactercalledsomeCharacter:

1 letsomeCharacter:Character="z"

2 switchsomeCharacter{

3 case"a":

4 print("Thefirstletterofthealphabet")

5 case"z":

6 print("Thelastletterofthealphabet")

7 default:

8 print("Someothercharacter")

9 }

10 //Prints"Thelastletterofthealphabet"

Theswitchstatement’sfirstcasematchesthefirstletteroftheEnglishalphabet,a,anditssecondcasematchesthelastletter,z.Becausetheswitchmusthaveacaseforeverypossiblecharacter,notjusteveryalphabeticcharacter,thisswitchstatementusesadefaultcasetomatchallcharactersotherthanaandz.Thisprovisionensuresthattheswitchstatementisexhaustive.

NoImplicitFallthrough

IncontrastwithswitchstatementsinCandObjective-C,switchstatementsinSwiftdonotfallthroughthebottomofeachcaseandintothenextonebydefault.Instead,theentireswitchstatementfinishesitsexecutionassoonasthefirstmatchingswitchcaseiscompleted,withoutrequiringanexplicitbreakstatement.Thismakestheswitchstatementsaferandeasiertousethantheone

inCandavoidsexecutingmorethanoneswitchcasebymistake.

NOTE

AlthoughbreakisnotrequiredinSwift,youcanuseabreakstatementtomatchandignoreaparticularcaseortobreakoutofamatchedcasebeforethatcasehascompleteditsexecution.Fordetails,seeBreakinaSwitchStatement.

Thebodyofeachcasemustcontainatleastoneexecutablestatement.Itisnotvalidtowritethefollowingcode,becausethefirstcaseisempty:

1 letanotherCharacter:Character="a"

2 switchanotherCharacter{

3 case"a"://Invalid,thecasehasanemptybody

4 case"A":

5 print("TheletterA")

6 default:

7 print("NottheletterA")

8 }

9 //Thiswillreportacompile-timeerror.

UnlikeaswitchstatementinC,thisswitchstatementdoesnotmatchboth"a"and"A".Rather,itreportsacompile-timeerrorthatcase"a":doesnotcontainanyexecutablestatements.Thisapproachavoidsaccidentalfallthroughfromonecasetoanotherandmakesforsafercodethatisclearerinitsintent.

Tomakeaswitchwithasinglecasethatmatchesboth"a"and"A",combinethetwovaluesintoacompoundcase,separatingthevalueswithcommas.

1 letanotherCharacter:Character="a"

2 switchanotherCharacter{

3 case"a","A":

4 print("TheletterA")

5 default:

6 print("NottheletterA")

7 }

8 //Prints"TheletterA"

Forreadability,acompoundcasecanalsobewrittenovermultiplelines.Formoreinformationaboutcompoundcases,seeCompoundCases.

NOTE

Toexplicitlyfallthroughattheendofaparticularswitchcase,usethefallthroughkeyword,asdescribedinFallthrough.

IntervalMatching

Valuesinswitchcasescanbecheckedfortheirinclusioninaninterval.Thisexampleusesnumberintervalstoprovideanatural-languagecountfornumbersofanysize:

1 letapproximateCount=62

2 letcountedThings="moonsorbitingSaturn"

3 letnaturalCount:String

4 switchapproximateCount{

5 case0:

6 naturalCount="no"

7 case1..<5:

8 naturalCount="afew"

9 case5..<12:

10 naturalCount="several"

11 case12..<100:

12 naturalCount="dozensof"

13 case100..<1000:

14 naturalCount="hundredsof"

15 default:

16 naturalCount="many"

17 }

18 print("Thereare\(naturalCount)\(countedThings).")

19 //Prints"TherearedozensofmoonsorbitingSaturn."

Intheaboveexample,approximateCountisevaluatedinaswitchstatement.Eachcasecomparesthatvaluetoanumberorinterval.BecausethevalueofapproximateCountfallsbetween12and100,naturalCountisassignedthevalue"dozensof",andexecutionistransferredoutoftheswitchstatement.

Tuples

Youcanusetuplestotestmultiplevaluesinthesameswitchstatement.Eachelementofthetuplecanbetestedagainstadifferentvalueorintervalofvalues.Alternatively,usetheunderscorecharacter(_),alsoknownasthewildcardpattern,tomatchanypossiblevalue.

Theexamplebelowtakesan(x,y)point,expressedasasimpletupleoftype(Int,Int),andcategorizesitonthegraphthatfollowstheexample.

1 letsomePoint=(1,1)

2 switchsomePoint{

3 case(0,0):

4 print("\(somePoint)isattheorigin")

5 case(_,0):

6 print("\(somePoint)isonthex-axis")

7 case(0,_):

8 print("\(somePoint)isonthey-axis")

9 case(-2...2,-2...2):

10 print("\(somePoint)isinsidethebox")

11 default:

12 print("\(somePoint)isoutsideofthebox")

13 }

14 //Prints"(1,1)isinsidethebox"

Theswitchstatementdetermineswhetherthepointisattheorigin(0,0),ontheredx-axis,ontheorangey-axis,insidetheblue4-by-4boxcenteredontheorigin,oroutsideofthebox.

UnlikeC,Swiftallowsmultipleswitchcasestoconsiderthesamevalueorvalues.Infact,thepoint(0,0)couldmatchallfourofthecasesinthisexample.However,ifmultiplematchesarepossible,thefirstmatchingcaseisalwaysused.Thepoint(0,0)wouldmatchcase(0,0)first,andsoallothermatchingcaseswouldbeignored.

ValueBindings

Aswitchcasecannamethevalueorvaluesitmatchestotemporaryconstantsorvariables,foruseinthebodyofthecase.Thisbehaviorisknownasvaluebinding,becausethevaluesareboundtotemporaryconstantsorvariableswithinthecase’sbody.

Theexamplebelowtakesan(x,y)point,expressedasatupleoftype(Int,Int),andcategorizesitonthegraphthatfollows:

1 letanotherPoint=(2,0)

2 switchanotherPoint{

3 case(letx,0):

4 print("onthex-axiswithanxvalueof\(x)")

5 case(0,lety):

6 print("onthey-axiswithayvalueof\(y)")

7 caselet(x,y):

8 print("somewhereelseat(\(x),\(y))")

9 }

10 //Prints"onthex-axiswithanxvalueof2"

Theswitchstatementdetermineswhetherthepointisontheredx-axis,ontheorangey-axis,orelsewhere(onneitheraxis).

Thethreeswitchcasesdeclareplaceholderconstantsxandy,whichtemporarilytakeononeorbothtuplevaluesfromanotherPoint.Thefirstcase,case(letx,0),matchesanypointwithayvalueof0andassignsthepoint’sxvaluetothetemporaryconstantx.Similarly,thesecondcase,case(0,lety),matchesanypointwithanxvalueof0andassignsthepoint’syvaluetothetemporaryconstanty.

Afterthetemporaryconstantsaredeclared,theycanbeusedwithinthecase’scodeblock.Here,theyareusedtoprintthecategorizationofthepoint.

Thisswitchstatementdoesnothaveadefaultcase.Thefinalcase,caselet

(x,y),declaresatupleoftwoplaceholderconstantsthatcanmatchanyvalue.BecauseanotherPointisalwaysatupleoftwovalues,thiscasematchesallpossibleremainingvalues,andadefaultcaseisnotneededtomaketheswitchstatementexhaustive.

Where

Aswitchcasecanuseawhereclausetocheckforadditionalconditions.

Theexamplebelowcategorizesan(x,y)pointonthefollowinggraph:

1 letyetAnotherPoint=(1,-1)

2 switchyetAnotherPoint{

3 caselet(x,y)wherex==y:

4 print("(\(x),\(y))isonthelinex==y")

5 caselet(x,y)wherex==-y:

6 print("(\(x),\(y))isonthelinex==-y")

7 caselet(x,y):

8 print("(\(x),\(y))isjustsomearbitrarypoint")

9 }

10 //Prints"(1,-1)isonthelinex==-y"

Theswitchstatementdetermineswhetherthepointisonthegreendiagonallinewherex==y,onthepurplediagonallinewherex==-y,orneither.

Thethreeswitchcasesdeclareplaceholderconstantsxandy,whichtemporarilytakeonthetwotuplevaluesfromyetAnotherPoint.Theseconstantsareusedaspartofawhereclause,tocreateadynamicfilter.Theswitchcasematchesthecurrentvalueofpointonlyifthewhereclause’sconditionevaluatestotrueforthatvalue.

Asinthepreviousexample,thefinalcasematchesallpossibleremainingvalues,andsoadefaultcaseisnotneededtomaketheswitchstatementexhaustive.

CompoundCases

Multipleswitchcasesthatsharethesamebodycanbecombinedbywritingseveralpatternsaftercase,withacommabetweeneachofthepatterns.Ifanyofthepatternsmatch,thenthecaseisconsideredtomatch.Thepatternscanbewrittenovermultiplelinesifthelistislong.Forexample:

1 letsomeCharacter:Character="e"

2 switchsomeCharacter{

3 case"a","e","i","o","u":

4 print("\(someCharacter)isavowel")

5 case"b","c","d","f","g","h","j","k","l","m",

6 "n","p","q","r","s","t","v","w","x","y","z":

7 print("\(someCharacter)isaconsonant")

8 default:

9 print("\(someCharacter)isnotavoweloraconsonant")

10 }

11 //Prints"eisavowel"

Theswitchstatement’sfirstcasematchesallfivelowercasevowelsintheEnglishlanguage.Similarly,itssecondcasematchesalllowercaseEnglishconsonants.Finally,thedefaultcasematchesanyothercharacter.

Compoundcasescanalsoincludevaluebindings.Allofthepatternsofacompoundcasehavetoincludethesamesetofvaluebindings,andeachbindinghastogetavalueofthesametypefromallofthepatternsinthecompoundcase.Thisensuresthat,nomatterwhichpartofthecompoundcasematched,thecodeinthebodyofthecasecanalwaysaccessavalueforthebindingsandthatthevaluealwayshasthesametype.

1 letstillAnotherPoint=(9,0)

2 switchstillAnotherPoint{

3 case(letdistance,0),(0,letdistance):

4 print("Onanaxis,\(distance)fromtheorigin")

5 default:

6 print("Notonanaxis")

7 }

8 //Prints"Onanaxis,9fromtheorigin"

Thecaseabovehastwopatterns:(letdistance,0)matchespointsonthex-axisand(0,letdistance)matchespointsonthey-axis.Bothpatternsincludeabindingfordistanceanddistanceisanintegerinbothpatterns—whichmeansthatthecodeinthebodyofthecasecanalwaysaccessavaluefordistance.

ControlTransferStatements

Controltransferstatementschangetheorderinwhichyourcodeisexecuted,bytransferringcontrolfromonepieceofcodetoanother.Swifthasfivecontroltransferstatements:

continue

break

fallthrough

return

throw

Thecontinue,break,andfallthroughstatementsaredescribedbelow.ThereturnstatementisdescribedinFunctions,andthethrowstatementisdescribedinPropagatingErrorsUsingThrowingFunctions.

ContinueThecontinuestatementtellsalooptostopwhatitisdoingandstartagainatthebeginningofthenextiterationthroughtheloop.Itsays“Iamdonewiththecurrentloopiteration”withoutleavingtheloopaltogether.

Thefollowingexampleremovesallvowelsandspacesfromalowercasestringtocreateacrypticpuzzlephrase:

1 letpuzzleInput="greatmindsthinkalike"

2 varpuzzleOutput=""

3 letcharactersToRemove:[Character]=["a","e","i","o","u",

""]

4 forcharacterinpuzzleInput{

5 ifcharactersToRemove.contains(character){

6 continue

7 }

8 puzzleOutput.append(character)

9 }

10 print(puzzleOutput)

11 //Prints"grtmndsthnklk"

Thecodeabovecallsthecontinuekeywordwheneveritmatchesavoweloraspace,causingthecurrentiterationofthelooptoendimmediatelyandtojumpstraighttothestartofthenextiteration.

BreakThebreakstatementendsexecutionofanentirecontrolflowstatementimmediately.Thebreakstatementcanbeusedinsideaswitchorloopstatementwhenyouwanttoterminatetheexecutionoftheswitchorloopstatementearlierthanwouldotherwisebethecase.

BreakinaLoopStatement

Whenusedinsidealoopstatement,breakendstheloop’sexecutionimmediatelyandtransferscontroltothecodeaftertheloop’sclosingbrace(}).Nofurthercodefromthecurrentiterationoftheloopisexecuted,andnofurtheriterationsofthelooparestarted.

BreakinaSwitchStatement

Whenusedinsideaswitchstatement,breakcausestheswitchstatementtoenditsexecutionimmediatelyandtotransfercontroltothecodeaftertheswitchstatement’sclosingbrace(}).

Thisbehaviorcanbeusedtomatchandignoreoneormorecasesinaswitchstatement.BecauseSwift’sswitchstatementisexhaustiveanddoesnotallowemptycases,itissometimesnecessarytodeliberatelymatchandignoreacaseinordertomakeyourintentionsexplicit.Youdothisbywritingthebreakstatementastheentirebodyofthecaseyouwanttoignore.Whenthatcaseismatchedbytheswitchstatement,thebreakstatementinsidethecaseendstheswitchstatement’sexecutionimmediately.

NOTE

Aswitchcasethatcontainsonlyacommentisreportedasacompile-timeerror.Commentsarenotstatementsanddonotcauseaswitchcasetobeignored.Alwaysuseabreakstatementtoignoreaswitchcase.

ThefollowingexampleswitchesonaCharactervalueanddetermineswhetheritrepresentsanumbersymbolinoneoffourlanguages.Forbrevity,multiplevaluesarecoveredinasingleswitchcase.

1 letnumberSymbol:Character="" //Chinesesymbolforthe

number3

2 varpossibleIntegerValue:Int?

3 switchnumberSymbol{

4 case"1","١","" ,"�":

5 possibleIntegerValue=1

6 case"2","٢","" ,"�":

7 possibleIntegerValue=2

8 case"3","٣","" ,"�":

9 possibleIntegerValue=3

10 case"4","٤","" ,"�":

11 possibleIntegerValue=4

12 default:

13 break

14 }

15 ifletintegerValue=possibleIntegerValue{

16 print("Theintegervalueof\(numberSymbol)is\

(integerValue).")

17 }else{

18 print("Anintegervaluecouldnotbefoundfor\

(numberSymbol).")

19 }

20 //Prints"Theintegervalueofis3."

ThisexamplechecksnumberSymboltodeterminewhetheritisaLatin,Arabic,Chinese,orThaisymbolforthenumbers1to4.Ifamatchisfound,oneoftheswitchstatement’scasessetsanoptionalInt?variablecalledpossibleIntegerValuetoanappropriateintegervalue.

Aftertheswitchstatementcompletesitsexecution,theexampleusesoptionalbindingtodeterminewhetheravaluewasfound.ThepossibleIntegerValue

variablehasanimplicitinitialvalueofnilbyvirtueofbeinganoptionaltype,andsotheoptionalbindingwillsucceedonlyifpossibleIntegerValuewassettoanactualvaluebyoneoftheswitchstatement’sfirstfourcases.

Becauseit’snotpracticaltolisteverypossibleCharactervalueintheexampleabove,adefaultcasehandlesanycharactersthatarenotmatched.Thisdefaultcasedoesnotneedtoperformanyaction,andsoitiswrittenwithasinglebreakstatementasitsbody.Assoonasthedefaultcaseismatched,thebreakstatementendstheswitchstatement’sexecution,andcodeexecutioncontinuesfromtheifletstatement.

FallthroughInSwift,switchstatementsdon’tfallthroughthebottomofeachcaseandintothenextone.Thatis,theentireswitchstatementcompletesitsexecutionassoonasthefirstmatchingcaseiscompleted.Bycontrast,Crequiresyoutoinsertanexplicitbreakstatementattheendofeveryswitchcasetopreventfallthrough.AvoidingdefaultfallthroughmeansthatSwiftswitchstatementsaremuchmoreconciseandpredictablethantheircounterpartsinC,andthustheyavoidexecutingmultipleswitchcasesbymistake.

IfyouneedC-stylefallthroughbehavior,youcanoptintothisbehavioronacase-by-casebasiswiththefallthroughkeyword.Theexamplebelowusesfallthroughtocreateatextualdescriptionofanumber.

1 letintegerToDescribe=5

2 vardescription="Thenumber\(integerToDescribe)is"

3 switchintegerToDescribe{

4 case2,3,5,7,11,13,17,19:

5 description+="aprimenumber,andalso"

6 fallthrough

7 default:

8 description+="aninteger."

9 }

10 print(description)

11 //Prints"Thenumber5isaprimenumber,andalsoan

integer."

ThisexampledeclaresanewStringvariablecalleddescriptionandassignsitaninitialvalue.ThefunctionthenconsidersthevalueofintegerToDescribeusingaswitchstatement.IfthevalueofintegerToDescribeisoneoftheprimenumbersinthelist,thefunctionappendstexttotheendofdescription,tonotethatthenumberisprime.Itthenusesthefallthroughkeywordto“fallinto”thedefaultcaseaswell.Thedefaultcaseaddssomeextratexttotheendofthedescription,andtheswitchstatementiscomplete.

UnlessthevalueofintegerToDescribeisinthelistofknownprimenumbers,itisnotmatchedbythefirstswitchcaseatall.Becausetherearenootherspecificcases,integerToDescribeismatchedbythedefaultcase.

Aftertheswitchstatementhasfinishedexecuting,thenumber’sdescriptionisprintedusingtheprint(_:separator:terminator:)function.Inthisexample,thenumber5iscorrectlyidentifiedasaprimenumber.

NOTE

Thefallthroughkeyworddoesnotcheckthecaseconditionsfortheswitchcasethatitcausesexecutiontofallinto.Thefallthroughkeywordsimplycausescodeexecutiontomovedirectlytothestatementsinsidethenextcase(ordefaultcase)block,asinC’sstandardswitchstatementbehavior.

LabeledStatementsInSwift,youcannestloopsandconditionalstatementsinsideotherloopsandconditionalstatementstocreatecomplexcontrolflowstructures.However,loopsandconditionalstatementscanbothusethebreakstatementtoendtheirexecutionprematurely.Therefore,itissometimesusefultobeexplicitaboutwhichlooporconditionalstatementyouwantabreakstatementtoterminate.Similarly,ifyouhavemultiplenestedloops,itcanbeusefultobeexplicitaboutwhichloopthecontinuestatementshouldaffect.

Toachievetheseaims,youcanmarkaloopstatementorconditionalstatementwithastatementlabel.Withaconditionalstatement,youcanuseastatementlabelwiththebreakstatementtoendtheexecutionofthelabeledstatement.Withaloopstatement,youcanuseastatementlabelwiththebreakorcontinuestatementtoendorcontinuetheexecutionofthelabeledstatement.

Alabeledstatementisindicatedbyplacingalabelonthesamelineasthestatement’sintroducerkeyword,followedbyacolon.Here’sanexampleofthissyntaxforawhileloop,althoughtheprincipleisthesameforallloopsandswitchstatements:

labelname :while condition {

statements

}

ThefollowingexampleusesthebreakandcontinuestatementswithalabeledwhileloopforanadaptedversionoftheSnakesandLaddersgamethatyousawearlierinthischapter.Thistimearound,thegamehasanextrarule:

Towin,youmustlandexactlyonsquare25.

Ifaparticulardicerollwouldtakeyoubeyondsquare25,youmustrollagainuntilyourolltheexactnumberneededtolandonsquare25.

Thegameboardisthesameasbefore.

ThevaluesoffinalSquare,board,square,anddiceRollareinitializedinthesamewayasbefore:

1 letfinalSquare=25

2 varboard=[Int](repeating:0,count:finalSquare+1)

3 board[03]=+08;board[06]=+11;board[09]=+09;board[10]=

+02

4 board[14]=-10;board[19]=-11;board[22]=-02;board[24]=

-08

5 varsquare=0

6 vardiceRoll=0

Thisversionofthegameusesawhileloopandaswitchstatementtoimplementthegame’slogic.ThewhileloophasastatementlabelcalledgameLooptoindicatethatitisthemaingameloopfortheSnakesandLaddersgame.

Thewhileloop’sconditioniswhilesquare!=finalSquare,toreflectthatyoumustlandexactlyonsquare25.

1 gameLoop:whilesquare!=finalSquare{

2 diceRoll+=1

3 ifdiceRoll==7{diceRoll=1}

4 switchsquare+diceRoll{

5 casefinalSquare:

6 //diceRollwillmoveustothefinalsquare,sothe

gameisover

7 breakgameLoop

8 caseletnewSquarewherenewSquare>finalSquare:

9 //diceRollwillmoveusbeyondthefinalsquare,so

rollagain

10 continuegameLoop

11 default:

12 //thisisavalidmove,sofindoutitseffect

13 square+=diceRoll

14 square+=board[square]

15 }

16 }

17 print("Gameover!")

Thediceisrolledatthestartofeachloop.Ratherthanmovingtheplayerimmediately,theloopusesaswitchstatementtoconsidertheresultofthemoveandtodeterminewhetherthemoveisallowed:

Ifthedicerollwillmovetheplayerontothefinalsquare,thegameisover.ThebreakgameLoopstatementtransferscontroltothefirstlineofcodeoutsideofthewhileloop,whichendsthegame.

Ifthedicerollwillmovetheplayerbeyondthefinalsquare,themoveisinvalidandtheplayerneedstorollagain.ThecontinuegameLoopstatementendsthecurrentwhileloopiterationandbeginsthenextiterationoftheloop.

Inallothercases,thedicerollisavalidmove.TheplayermovesforwardbydiceRollsquares,andthegamelogicchecksforanysnakesandladders.

Theloopthenends,andcontrolreturnstothewhileconditiontodecidewhetheranotherturnisrequired.

NOTE

IfthebreakstatementabovedidnotusethegameLooplabel,itwouldbreakoutoftheswitchstatement,notthewhilestatement.UsingthegameLooplabelmakesitclearwhichcontrolstatementshouldbeterminated.

ItisnotstrictlynecessarytousethegameLooplabelwhencallingcontinuegameLooptojumptothenextiterationoftheloop.Thereisonlyoneloopinthegame,andthereforenoambiguityastowhichloopthecontinuestatementwillaffect.However,thereisnoharminusingthegameLooplabelwiththecontinuestatement.Doingsoisconsistentwiththelabel’susealongsidethebreakstatementandhelpsmakethegame’slogicclearertoreadandunderstand.

EarlyExit

Aguardstatement,likeanifstatement,executesstatementsdependingontheBooleanvalueofanexpression.Youuseaguardstatementtorequirethataconditionmustbetrueinorderforthecodeaftertheguardstatementtobeexecuted.Unlikeanifstatement,aguardstatementalwayshasanelseclause—thecodeinsidetheelseclauseisexecutediftheconditionisnottrue.

1 funcgreet(person:[String:String]){

2 guardletname=person["name"]else{

3 return

4 }

5

6 print("Hello\(name)!")

7

8 guardletlocation=person["location"]else{

9 print("Ihopetheweatherisnicenearyou.")

10 return

11 }

12

13 print("Ihopetheweatherisnicein\(location).")

14 }

15

16 greet(person:["name":"John"])

17 //Prints"HelloJohn!"

18 //Prints"Ihopetheweatherisnicenearyou."

19 greet(person:["name":"Jane","location":"Cupertino"])

20 //Prints"HelloJane!"

21 //Prints"IhopetheweatherisniceinCupertino."

Iftheguardstatement’sconditionismet,codeexecutioncontinuesaftertheguardstatement’sclosingbrace.Anyvariablesorconstantsthatwereassignedvaluesusinganoptionalbindingaspartoftheconditionareavailablefortherestofthecodeblockthattheguardstatementappearsin.

Ifthatconditionisnotmet,thecodeinsidetheelsebranchisexecuted.Thatbranchmusttransfercontroltoexitthecodeblockinwhichtheguardstatementappears.Itcandothiswithacontroltransferstatementsuchasreturn,break,continue,orthrow,oritcancallafunctionormethodthatdoesn’treturn,suchasfatalError(_:file:line:).

Usingaguardstatementforrequirementsimprovesthereadabilityofyourcode,comparedtodoingthesamecheckwithanifstatement.Itletsyouwritethecodethat’stypicallyexecutedwithoutwrappingitinanelseblock,anditletsyoukeepthecodethathandlesaviolatedrequirementnexttotherequirement.

CheckingAPIAvailability

Swifthasbuilt-insupportforcheckingAPIavailability,whichensuresthatyoudon’taccidentallyuseAPIsthatareunavailableonagivendeploymenttarget.

ThecompilerusesavailabilityinformationintheSDKtoverifythatallofthe

APIsusedinyourcodeareavailableonthedeploymenttargetspecifiedbyyourproject.SwiftreportsanerroratcompiletimeifyoutrytouseanAPIthatisn’tavailable.

Youuseanavailabilityconditioninaniforguardstatementtoconditionallyexecuteablockofcode,dependingonwhethertheAPIsyouwanttouseareavailableatruntime.ThecompilerusestheinformationfromtheavailabilityconditionwhenitverifiesthattheAPIsinthatblockofcodeareavailable.

1 if#available(iOS10,macOS10.12,*){

2 //UseiOS10APIsoniOS,andusemacOS10.12APIson

macOS

3 }else{

4 //FallbacktoearlieriOSandmacOSAPIs

5 }

TheavailabilityconditionabovespecifiesthatiniOS,thebodyoftheifstatementexecutesonlyiniOS10andlater;inmacOS,onlyinmacOS10.12andlater.Thelastargument,*,isrequiredandspecifiesthatonanyotherplatform,thebodyoftheifexecutesontheminimumdeploymenttargetspecifiedbyyourtarget.

Initsgeneralform,theavailabilityconditiontakesalistofplatformnamesandversions.YouuseplatformnamessuchasiOS,macOS,watchOS,andtvOS—forthefulllist,seeDeclarationAttributes.InadditiontospecifyingmajorversionnumberslikeiOS8ormacOS10.10,youcanspecifyminorversionsnumberslikeiOS11.2.6andmacOS10.13.3.

if#available( platformname version , ... ,*){

statementstoexecuteiftheAPIsareavailable

}else{

fallbackstatementstoexecuteiftheAPIsareunavailable

}

Functions

Functionsareself-containedchunksofcodethatperformaspecifictask.Yougiveafunctionanamethatidentifieswhatitdoes,andthisnameisusedto“call”thefunctiontoperformitstaskwhenneeded.

Swift’sunifiedfunctionsyntaxisflexibleenoughtoexpressanythingfromasimpleC-stylefunctionwithnoparameternamestoacomplexObjective-C-stylemethodwithnamesandargumentlabelsforeachparameter.Parameterscanprovidedefaultvaluestosimplifyfunctioncallsandcanbepassedasin-outparameters,whichmodifyapassedvariableoncethefunctionhascompleteditsexecution.

EveryfunctioninSwifthasatype,consistingofthefunction’sparametertypesandreturntype.YoucanusethistypelikeanyothertypeinSwift,whichmakesiteasytopassfunctionsasparameterstootherfunctions,andtoreturnfunctionsfromfunctions.Functionscanalsobewrittenwithinotherfunctionstoencapsulateusefulfunctionalitywithinanestedfunctionscope.

DefiningandCallingFunctions

Whenyoudefineafunction,youcanoptionallydefineoneormorenamed,typedvaluesthatthefunctiontakesasinput,knownasparameters.Youcanalsooptionallydefineatypeofvaluethatthefunctionwillpassbackasoutputwhenitisdone,knownasitsreturntype.

Everyfunctionhasafunctionname,whichdescribesthetaskthatthefunctionperforms.Touseafunction,you“call”thatfunctionwithitsnameandpassitinputvalues(knownasarguments)thatmatchthetypesofthefunction’sparameters.Afunction’sargumentsmustalwaysbeprovidedinthesameorderasthefunction’sparameterlist.

Thefunctionintheexamplebelowiscalledgreet(person:),becausethat’swhatitdoes—ittakesaperson’snameasinputandreturnsagreetingforthatperson.

Toaccomplishthis,youdefineoneinputparameter—aStringvaluecalledperson—andareturntypeofString,whichwillcontainagreetingforthatperson:

1 funcgreet(person:String)->String{

2 letgreeting="Hello,"+person+"!"

3 returngreeting

4 }

Allofthisinformationisrolledupintothefunction’sdefinition,whichisprefixedwiththefunckeyword.Youindicatethefunction’sreturntypewiththereturnarrow->(ahyphenfollowedbyarightanglebracket),whichisfollowedbythenameofthetypetoreturn.

Thedefinitiondescribeswhatthefunctiondoes,whatitexpectstoreceive,andwhatitreturnswhenitisdone.Thedefinitionmakesiteasyforthefunctiontobecalledunambiguouslyfromelsewhereinyourcode:

1 print(greet(person:"Anna"))

2 //Prints"Hello,Anna!"

3 print(greet(person:"Brian"))

4 //Prints"Hello,Brian!"

Youcallthegreet(person:)functionbypassingitaStringvalueafterthepersonargumentlabel,suchasgreet(person:"Anna").BecausethefunctionreturnsaStringvalue,greet(person:)canbewrappedinacalltotheprint(_:separator:terminator:)functiontoprintthatstringandseeitsreturnvalue,asshownabove.

NOTE

Theprint(_:separator:terminator:)functiondoesn’thavealabelforitsfirstargument,anditsotherargumentsareoptionalbecausetheyhaveadefaultvalue.ThesevariationsonfunctionsyntaxarediscussedbelowinFunctionArgumentLabelsandParameterNamesandDefaultParameterValues.

Thebodyofthegreet(person:)functionstartsbydefininganewStringconstantcalledgreetingandsettingittoasimplegreetingmessage.Thisgreetingisthenpassedbackoutofthefunctionusingthereturnkeyword.Inthelineofcodethatsaysreturngreeting,thefunctionfinishesitsexecutionandreturnsthecurrentvalueofgreeting.

Youcancallthegreet(person:)functionmultipletimeswithdifferentinputvalues.Theexampleaboveshowswhathappensifitiscalledwithaninputvalueof"Anna",andaninputvalueof"Brian".Thefunctionreturnsatailoredgreetingineachcase.

Tomakethebodyofthisfunctionshorter,youcancombinethemessagecreationandthereturnstatementintooneline:

1 funcgreetAgain(person:String)->String{

2 return"Helloagain,"+person+"!"

3 }

4 print(greetAgain(person:"Anna"))

5 //Prints"Helloagain,Anna!"

FunctionParametersandReturnValues

FunctionparametersandreturnvaluesareextremelyflexibleinSwift.Youcandefineanythingfromasimpleutilityfunctionwithasingleunnamedparametertoacomplexfunctionwithexpressiveparameternamesanddifferentparameteroptions.

FunctionsWithoutParametersFunctionsarenotrequiredtodefineinputparameters.Here’safunctionwithnoinputparameters,whichalwaysreturnsthesameStringmessagewheneveritiscalled:

1 funcsayHelloWorld()->String{

2 return"hello,world"

3 }

4 print(sayHelloWorld())

5 //Prints"hello,world"

Thefunctiondefinitionstillneedsparenthesesafterthefunction’sname,eventhoughitdoesnottakeanyparameters.Thefunctionnameisalsofollowedbyanemptypairofparentheseswhenthefunctioniscalled.

FunctionsWithMultipleParametersFunctionscanhavemultipleinputparameters,whicharewrittenwithinthefunction’sparentheses,separatedbycommas.

Thisfunctiontakesaperson’snameandwhethertheyhavealreadybeengreetedasinput,andreturnsanappropriategreetingforthatperson:

1 funcgreet(person:String,alreadyGreeted:Bool)->String{

2 ifalreadyGreeted{

3 returngreetAgain(person:person)

4 }else{

5 returngreet(person:person)

6 }

7 }

8 print(greet(person:"Tim",alreadyGreeted:true))

9 //Prints"Helloagain,Tim!"

Youcallthegreet(person:alreadyGreeted:)functionbypassingitbothaStringargumentvaluelabeledpersonandaBoolargumentvaluelabeledalreadyGreetedinparentheses,separatedbycommas.Notethatthisfunctionisdistinctfromthegreet(person:)functionshowninanearliersection.Althoughbothfunctionshavenamesthatbeginwithgreet,the

greet(person:alreadyGreeted:)functiontakestwoargumentsbutthegreet(person:)functiontakesonlyone.

FunctionsWithoutReturnValuesFunctionsarenotrequiredtodefineareturntype.Here’saversionofthegreet(person:)function,whichprintsitsownStringvalueratherthanreturningit:

1 funcgreet(person:String){

2 print("Hello,\(person)!")

3 }

4 greet(person:"Dave")

5 //Prints"Hello,Dave!"

Becauseitdoesnotneedtoreturnavalue,thefunction’sdefinitiondoesnotincludethereturnarrow(->)orareturntype.

NOTE

Strictlyspeaking,thisversionofthegreet(person:)functiondoesstillreturnavalue,eventhoughnoreturnvalueisdefined.FunctionswithoutadefinedreturntypereturnaspecialvalueoftypeVoid.Thisissimplyanemptytuple,whichiswrittenas().

Thereturnvalueofafunctioncanbeignoredwhenitiscalled:

1 funcprintAndCount(string:String)->Int{

2 print(string)

3 returnstring.count

4 }

5 funcprintWithoutCounting(string:String){

6 let_=printAndCount(string:string)

7 }

8 printAndCount(string:"hello,world")

9 //prints"hello,world"andreturnsavalueof12

10 printWithoutCounting(string:"hello,world")

11 //prints"hello,world"butdoesnotreturnavalue

Thefirstfunction,printAndCount(string:),printsastring,andthenreturnsitscharactercountasanInt.Thesecondfunction,printWithoutCounting(string:),callsthefirstfunction,butignoresitsreturnvalue.Whenthesecondfunctioniscalled,themessageisstillprintedbythefirstfunction,butthereturnedvalueisnotused.

NOTE

Returnvaluescanbeignored,butafunctionthatsaysitwillreturnavaluemustalwaysdoso.Afunctionwithadefinedreturntypecannotallowcontroltofalloutofthebottomofthefunctionwithoutreturningavalue,andattemptingtodosowillresultinacompile-timeerror.

FunctionswithMultipleReturnValuesYoucanuseatupletypeasthereturntypeforafunctiontoreturnmultiplevaluesaspartofonecompoundreturnvalue.

TheexamplebelowdefinesafunctioncalledminMax(array:),whichfindsthesmallestandlargestnumbersinanarrayofIntvalues:

1 funcminMax(array:[Int])->(min:Int,max:Int){

2 varcurrentMin=array[0]

3 varcurrentMax=array[0]

4 forvalueinarray[1..<array.count]{

5 ifvalue<currentMin{

6 currentMin=value

7 }elseifvalue>currentMax{

8 currentMax=value

9 }

10 }

11 return(currentMin,currentMax)

12 }

TheminMax(array:)functionreturnsatuplecontainingtwoIntvalues.Thesevaluesarelabeledminandmaxsothattheycanbeaccessedbynamewhenqueryingthefunction’sreturnvalue.

ThebodyoftheminMax(array:)functionstartsbysettingtwoworkingvariablescalledcurrentMinandcurrentMaxtothevalueofthefirstintegerinthearray.ThefunctiontheniteratesovertheremainingvaluesinthearrayandcheckseachvaluetoseeifitissmallerorlargerthanthevaluesofcurrentMinandcurrentMaxrespectively.Finally,theoverallminimumandmaximumvaluesarereturnedasatupleoftwoIntvalues.

Becausethetuple’smembervaluesarenamedaspartofthefunction’sreturntype,theycanbeaccessedwithdotsyntaxtoretrievetheminimumandmaximumfoundvalues:

1 letbounds=minMax(array:[8,-6,2,109,3,71])

2 print("minis\(bounds.min)andmaxis\(bounds.max)")

3 //Prints"minis-6andmaxis109"

Notethatthetuple’smembersdonotneedtobenamedatthepointthatthetupleisreturnedfromthefunction,becausetheirnamesarealreadyspecifiedaspartofthefunction’sreturntype.

OptionalTupleReturnTypes

Ifthetupletypetobereturnedfromafunctionhasthepotentialtohave“novalue”fortheentiretuple,youcanuseanoptionaltuplereturntypetoreflectthefactthattheentiretuplecanbenil.Youwriteanoptionaltuplereturntypebyplacingaquestionmarkafterthetupletype’sclosingparenthesis,suchas(Int,Int)?or(String,Int,Bool)?.

NOTE

Anoptionaltupletypesuchas(Int,Int)?isdifferentfromatuplethatcontainsoptionaltypes

suchas(Int?,Int?).Withanoptionaltupletype,theentiretupleisoptional,notjusteachindividualvaluewithinthetuple.

TheminMax(array:)functionabovereturnsatuplecontainingtwoIntvalues.However,thefunctiondoesnotperformanysafetychecksonthearrayitispassed.Ifthearrayargumentcontainsanemptyarray,theminMax(array:)function,asdefinedabove,willtriggeraruntimeerrorwhenattemptingtoaccessarray[0].

Tohandleanemptyarraysafely,writetheminMax(array:)functionwithanoptionaltuplereturntypeandreturnavalueofnilwhenthearrayisempty:

1 funcminMax(array:[Int])->(min:Int,max:Int)?{

2 ifarray.isEmpty{returnnil}

3 varcurrentMin=array[0]

4 varcurrentMax=array[0]

5 forvalueinarray[1..<array.count]{

6 ifvalue<currentMin{

7 currentMin=value

8 }elseifvalue>currentMax{

9 currentMax=value

10 }

11 }

12 return(currentMin,currentMax)

13 }

YoucanuseoptionalbindingtocheckwhetherthisversionoftheminMax(array:)functionreturnsanactualtuplevalueornil:

1 ifletbounds=minMax(array:[8,-6,2,109,3,71]){

2 print("minis\(bounds.min)andmaxis\(bounds.max)")

3 }

4 //Prints"minis-6andmaxis109"

FunctionsWithanImplicitReturnIftheentirebodyofthefunctionisasingleexpression,thefunctionimplicitlyreturnsthatexpression.Forexample,bothfunctionsbelowhavethesamebehavior:

1 funcgreeting(forperson:String)->String{

2 "Hello,"+person+"!"

3 }

4 print(greeting(for:"Dave"))

5 //Prints"Hello,Dave!"

6

7 funcanotherGreeting(forperson:String)->String{

8 return"Hello,"+person+"!"

9 }

10 print(anotherGreeting(for:"Dave"))

11 //Prints"Hello,Dave!"

Theentiredefinitionofthegreeting(for:)functionisthegreetingmessagethatitreturns,whichmeansitcanusethisshorterform.TheanotherGreeting(for:)functionreturnsthesamegreetingmessage,usingthereturnkeywordlikealongerfunction.Anyfunctionthatyouwriteasjustonereturnlinecanomitthereturn.

Asyou’llseeinShorthandGetterDeclaration,propertygetterscanalsouseanimplicitreturn.

FunctionArgumentLabelsandParameterNames

Eachfunctionparameterhasbothanargumentlabelandaparametername.Theargumentlabelisusedwhencallingthefunction;eachargumentiswritteninthefunctioncallwithitsargumentlabelbeforeit.Theparameternameisusedintheimplementationofthefunction.Bydefault,parametersusetheirparametername

astheirargumentlabel.

1 funcsomeFunction(firstParameterName:Int,secondParameterName:

Int){

2 //Inthefunctionbody,firstParameterNameand

secondParameterName

3 //refertotheargumentvaluesforthefirstandsecond

parameters.

4 }

5 someFunction(firstParameterName:1,secondParameterName:2)

Allparametersmusthaveuniquenames.Althoughit’spossibleformultipleparameterstohavethesameargumentlabel,uniqueargumentlabelshelpmakeyourcodemorereadable.

SpecifyingArgumentLabelsYouwriteanargumentlabelbeforetheparametername,separatedbyaspace:

1 funcsomeFunction(argumentLabelparameterName:Int){

2 //Inthefunctionbody,parameterNamereferstothe

argumentvalue

3 //forthatparameter.

4 }

Here’savariationofthegreet(person:)functionthattakesaperson’snameandhometownandreturnsagreeting:

1 funcgreet(person:String,fromhometown:String)->String{

2 return"Hello\(person)!Gladyoucouldvisitfrom\

(hometown)."

3 }

4 print(greet(person:"Bill",from:"Cupertino"))

5 //Prints"HelloBill!GladyoucouldvisitfromCupertino."

Theuseofargumentlabelscanallowafunctiontobecalledinanexpressive,sentence-likemanner,whilestillprovidingafunctionbodythatisreadableandclearinintent.

OmittingArgumentLabelsIfyoudon’twantanargumentlabelforaparameter,writeanunderscore(_)insteadofanexplicitargumentlabelforthatparameter.

1 funcsomeFunction(_firstParameterName:Int,

secondParameterName:Int){

2 //Inthefunctionbody,firstParameterNameand

secondParameterName

3 //refertotheargumentvaluesforthefirstandsecond

parameters.

4 }

5 someFunction(1,secondParameterName:2)

Ifaparameterhasanargumentlabel,theargumentmustbelabeledwhenyoucallthefunction.

DefaultParameterValuesYoucandefineadefaultvalueforanyparameterinafunctionbyassigningavaluetotheparameterafterthatparameter’stype.Ifadefaultvalueisdefined,youcanomitthatparameterwhencallingthefunction.

1 funcsomeFunction(parameterWithoutDefault:Int,

parameterWithDefault:Int=12){

2 //Ifyouomitthesecondargumentwhencallingthis

function,then

3 //thevalueofparameterWithDefaultis12insidethe

functionbody.

4 }

5 someFunction(parameterWithoutDefault:3,parameterWithDefault:

6)//parameterWithDefaultis6

6 someFunction(parameterWithoutDefault:4)//

parameterWithDefaultis12

Placeparametersthatdon’thavedefaultvaluesatthebeginningofafunction’sparameterlist,beforetheparametersthathavedefaultvalues.Parametersthatdon’thavedefaultvaluesareusuallymoreimportanttothefunction’smeaning—writingthemfirstmakesiteasiertorecognizethatthesamefunctionisbeingcalled,regardlessofwhetheranydefaultparametersareomitted.

VariadicParametersAvariadicparameteracceptszeroormorevaluesofaspecifiedtype.Youuseavariadicparametertospecifythattheparametercanbepassedavaryingnumberofinputvalueswhenthefunctioniscalled.Writevariadicparametersbyinsertingthreeperiodcharacters(...)aftertheparameter’stypename.

Thevaluespassedtoavariadicparameteraremadeavailablewithinthefunction’sbodyasanarrayoftheappropriatetype.Forexample,avariadicparameterwithanameofnumbersandatypeofDouble...ismadeavailablewithinthefunction’sbodyasaconstantarraycallednumbersoftype[Double].

Theexamplebelowcalculatesthearithmeticmean(alsoknownastheaverage)foralistofnumbersofanylength:

1 funcarithmeticMean(_numbers:Double...)->Double{

2 vartotal:Double=0

3 fornumberinnumbers{

4 total+=number

5 }

6 returntotal/Double(numbers.count)

7 }

8 arithmeticMean(1,2,3,4,5)

9 //returns3.0,whichisthearithmeticmeanofthesefive

numbers

10 arithmeticMean(3,8.25,18.75)

11 //returns10.0,whichisthearithmeticmeanofthesethree

numbers

NOTE

Afunctionmayhaveatmostonevariadicparameter.

In-OutParametersFunctionparametersareconstantsbydefault.Tryingtochangethevalueofafunctionparameterfromwithinthebodyofthatfunctionresultsinacompile-timeerror.Thismeansthatyoucan’tchangethevalueofaparameterbymistake.Ifyouwantafunctiontomodifyaparameter’svalue,andyouwantthosechangestopersistafterthefunctioncallhasended,definethatparameterasanin-outparameterinstead.

Youwriteanin-outparameterbyplacingtheinoutkeywordrightbeforeaparameter’stype.Anin-outparameterhasavaluethatispassedintothefunction,ismodifiedbythefunction,andispassedbackoutofthefunctiontoreplacetheoriginalvalue.Foradetaileddiscussionofthebehaviorofin-outparametersandassociatedcompileroptimizations,seeIn-OutParameters.

Youcanonlypassavariableastheargumentforanin-outparameter.Youcannotpassaconstantoraliteralvalueastheargument,becauseconstantsandliteralscannotbemodified.Youplaceanampersand(&)directlybeforeavariable’s

namewhenyoupassitasanargumenttoanin-outparameter,toindicatethatitcanbemodifiedbythefunction.

NOTE

In-outparameterscannothavedefaultvalues,andvariadicparameterscannotbemarkedasinout.

Here’sanexampleofafunctioncalledswapTwoInts(_:_:),whichhastwoin-outintegerparameterscalledaandb:

1 funcswapTwoInts(_a:inoutInt,_b:inoutInt){

2 lettemporaryA=a

3 a=b

4 b=temporaryA

5 }

TheswapTwoInts(_:_:)functionsimplyswapsthevalueofbintoa,andthevalueofaintob.ThefunctionperformsthisswapbystoringthevalueofainatemporaryconstantcalledtemporaryA,assigningthevalueofbtoa,andthenassigningtemporaryAtob.

YoucancalltheswapTwoInts(_:_:)functionwithtwovariablesoftypeInttoswaptheirvalues.NotethatthenamesofsomeIntandanotherIntareprefixedwithanampersandwhentheyarepassedtotheswapTwoInts(_:_:)function:

1 varsomeInt=3

2 varanotherInt=107

3 swapTwoInts(&someInt,&anotherInt)

4 print("someIntisnow\(someInt),andanotherIntisnow\

(anotherInt)")

5 //Prints"someIntisnow107,andanotherIntisnow3"

TheexampleaboveshowsthattheoriginalvaluesofsomeIntandanotherIntaremodifiedbytheswapTwoInts(_:_:)function,eventhoughtheywereoriginallydefinedoutsideofthefunction.

NOTE

In-outparametersarenotthesameasreturningavaluefromafunction.TheswapTwoIntsexampleabovedoesnotdefineareturntypeorreturnavalue,butitstillmodifiesthevaluesofsomeIntandanotherInt.In-outparametersareanalternativewayforafunctiontohaveaneffectoutsideofthescopeofitsfunctionbody.

FunctionTypes

Everyfunctionhasaspecificfunctiontype,madeupoftheparametertypesandthereturntypeofthefunction.

Forexample:

1 funcaddTwoInts(_a:Int,_b:Int)->Int{

2 returna+b

3 }

4 funcmultiplyTwoInts(_a:Int,_b:Int)->Int{

5 returna*b

6 }

ThisexampledefinestwosimplemathematicalfunctionscalledaddTwoIntsandmultiplyTwoInts.ThesefunctionseachtaketwoIntvalues,andreturnanIntvalue,whichistheresultofperforminganappropriatemathematicaloperation.

Thetypeofbothofthesefunctionsis(Int,Int)->Int.Thiscanbereadas:

“Afunctionthathastwoparameters,bothoftypeInt,andthatreturnsavalueoftypeInt.”

Here’sanotherexample,forafunctionwithnoparametersorreturnvalue:

1 funcprintHelloWorld(){

2 print("hello,world")

3 }

Thetypeofthisfunctionis()->Void,or“afunctionthathasnoparameters,andreturnsVoid.”

UsingFunctionTypesYouusefunctiontypesjustlikeanyothertypesinSwift.Forexample,youcandefineaconstantorvariabletobeofafunctiontypeandassignanappropriatefunctiontothatvariable:

varmathFunction:(Int,Int)->Int=addTwoInts

Thiscanbereadas:

“DefineavariablecalledmathFunction,whichhasatypeof‘afunctionthattakestwoIntvalues,andreturnsanIntvalue.’SetthisnewvariabletorefertothefunctioncalledaddTwoInts.”

TheaddTwoInts(_:_:)functionhasthesametypeasthemathFunctionvariable,andsothisassignmentisallowedbySwift’stype-checker.

YoucannowcalltheassignedfunctionwiththenamemathFunction:

1 print("Result:\(mathFunction(2,3))")

2 //Prints"Result:5"

Adifferentfunctionwiththesamematchingtypecanbeassignedtothesamevariable,inthesamewayasfornonfunctiontypes:

1 mathFunction=multiplyTwoInts

2 print("Result:\(mathFunction(2,3))")

3 //Prints"Result:6"

Aswithanyothertype,youcanleaveittoSwifttoinferthefunctiontypewhen

youassignafunctiontoaconstantorvariable:

1 letanotherMathFunction=addTwoInts

2 //anotherMathFunctionisinferredtobeoftype(Int,Int)->

Int

FunctionTypesasParameterTypesYoucanuseafunctiontypesuchas(Int,Int)->Intasaparametertypeforanotherfunction.Thisenablesyoutoleavesomeaspectsofafunction’simplementationforthefunction’scallertoprovidewhenthefunctioniscalled.

Here’sanexampletoprinttheresultsofthemathfunctionsfromabove:

1 funcprintMathResult(_mathFunction:(Int,Int)->Int,_a:

Int,_b:Int){

2 print("Result:\(mathFunction(a,b))")

3 }

4 printMathResult(addTwoInts,3,5)

5 //Prints"Result:8"

ThisexampledefinesafunctioncalledprintMathResult(_:_:_:),whichhasthreeparameters.ThefirstparameteriscalledmathFunction,andisoftype(Int,Int)->Int.Youcanpassanyfunctionofthattypeastheargumentforthisfirstparameter.Thesecondandthirdparametersarecalledaandb,andarebothoftypeInt.Theseareusedasthetwoinputvaluesfortheprovidedmathfunction.

WhenprintMathResult(_:_:_:)iscalled,itispassedtheaddTwoInts(_:_:)function,andtheintegervalues3and5.Itcallstheprovidedfunctionwiththevalues3and5,andprintstheresultof8.

TheroleofprintMathResult(_:_:_:)istoprinttheresultofacalltoamathfunctionofanappropriatetype.Itdoesn’tmatterwhatthatfunction’simplementationactuallydoes—itmattersonlythatthefunctionisofthecorrect

type.ThisenablesprintMathResult(_:_:_:)tohandoffsomeofitsfunctionalitytothecallerofthefunctioninatype-safeway.

FunctionTypesasReturnTypesYoucanuseafunctiontypeasthereturntypeofanotherfunction.Youdothisbywritingacompletefunctiontypeimmediatelyafterthereturnarrow(->)ofthereturningfunction.

ThenextexampledefinestwosimplefunctionscalledstepForward(_:)andstepBackward(_:).ThestepForward(_:)functionreturnsavalueonemorethanitsinputvalue,andthestepBackward(_:)functionreturnsavalueonelessthanitsinputvalue.Bothfunctionshaveatypeof(Int)->Int:

1 funcstepForward(_input:Int)->Int{

2 returninput+1

3 }

4 funcstepBackward(_input:Int)->Int{

5 returninput-1

6 }

Here’safunctioncalledchooseStepFunction(backward:),whosereturntypeis(Int)->Int.ThechooseStepFunction(backward:)functionreturnsthestepForward(_:)functionorthestepBackward(_:)functionbasedonaBooleanparametercalledbackward:

1 funcchooseStepFunction(backward:Bool)->(Int)->Int{

2 returnbackward?stepBackward:stepForward

3 }

YoucannowusechooseStepFunction(backward:)toobtainafunctionthatwillstepinonedirectionortheother:

1 varcurrentValue=3

2 letmoveNearerToZero=chooseStepFunction(backward:

currentValue>0)

3 //moveNearerToZeronowreferstothestepBackward()function

TheexampleabovedetermineswhetherapositiveornegativestepisneededtomoveavariablecalledcurrentValueprogressivelyclosertozero.currentValuehasaninitialvalueof3,whichmeansthatcurrentValue>0returnstrue,causingchooseStepFunction(backward:)toreturnthestepBackward(_:)function.AreferencetothereturnedfunctionisstoredinaconstantcalledmoveNearerToZero.

NowthatmoveNearerToZeroreferstothecorrectfunction,itcanbeusedtocounttozero:

1 print("Countingtozero:")

2 //Countingtozero:

3 whilecurrentValue!=0{

4 print("\(currentValue)...")

5 currentValue=moveNearerToZero(currentValue)

6 }

7 print("zero!")

8 //3...

9 //2...

10 //1...

11 //zero!

NestedFunctions

Allofthefunctionsyouhaveencounteredsofarinthischapterhavebeenexamplesofglobalfunctions,whicharedefinedataglobalscope.Youcanalsodefinefunctionsinsidethebodiesofotherfunctions,knownasnestedfunctions.

Nestedfunctionsarehiddenfromtheoutsideworldbydefault,butcanstillbecalledandusedbytheirenclosingfunction.Anenclosingfunctioncanalsoreturnoneofitsnestedfunctionstoallowthenestedfunctiontobeusedinanotherscope.

YoucanrewritethechooseStepFunction(backward:)exampleabovetouseandreturnnestedfunctions:

1 funcchooseStepFunction(backward:Bool)->(Int)->Int{

2 funcstepForward(input:Int)->Int{returninput+1}

3 funcstepBackward(input:Int)->Int{returninput-1}

4 returnbackward?stepBackward:stepForward

5 }

6 varcurrentValue=-4

7 letmoveNearerToZero=chooseStepFunction(backward:

currentValue>0)

8 //moveNearerToZeronowreferstothenestedstepForward()

function

9 whilecurrentValue!=0{

10 print("\(currentValue)...")

11 currentValue=moveNearerToZero(currentValue)

12 }

13 print("zero!")

14 //-4...

15 //-3...

16 //-2...

17 //-1...

18 //zero!

Closures

Closuresareself-containedblocksoffunctionalitythatcanbepassedaroundandusedinyourcode.ClosuresinSwiftaresimilartoblocksinCandObjective-Candtolambdasinotherprogramminglanguages.

Closurescancaptureandstorereferencestoanyconstantsandvariablesfromthecontextinwhichtheyaredefined.Thisisknownasclosingoverthoseconstantsandvariables.Swifthandlesallofthememorymanagementofcapturingforyou.

NOTE

Don’tworryifyouarenotfamiliarwiththeconceptofcapturing.ItisexplainedindetailbelowinCapturingValues.

Globalandnestedfunctions,asintroducedinFunctions,areactuallyspecialcasesofclosures.Closurestakeoneofthreeforms:

Globalfunctionsareclosuresthathaveanameanddonotcaptureanyvalues.

Nestedfunctionsareclosuresthathaveanameandcancapturevaluesfromtheirenclosingfunction.

Closureexpressionsareunnamedclosureswritteninalightweightsyntaxthatcancapturevaluesfromtheirsurroundingcontext.

Swift’sclosureexpressionshaveaclean,clearstyle,withoptimizationsthatencouragebrief,clutter-freesyntaxincommonscenarios.Theseoptimizationsinclude:

Inferringparameterandreturnvaluetypesfromcontext

Implicitreturnsfromsingle-expressionclosures

Shorthandargumentnames

Trailingclosuresyntax

ClosureExpressions

Nestedfunctions,asintroducedinNestedFunctions,areaconvenientmeansofnaminganddefiningself-containedblocksofcodeaspartofalargerfunction.However,itissometimesusefultowriteshorterversionsoffunction-likeconstructswithoutafulldeclarationandname.Thisisparticularlytruewhenyouworkwithfunctionsormethodsthattakefunctionsasoneormoreoftheirarguments.

Closureexpressionsareawaytowriteinlineclosuresinabrief,focusedsyntax.Closureexpressionsprovideseveralsyntaxoptimizationsforwritingclosuresinashortenedformwithoutlossofclarityorintent.Theclosureexpressionexamplesbelowillustratetheseoptimizationsbyrefiningasingleexampleofthesorted(by:)methodoverseveraliterations,eachofwhichexpressesthesamefunctionalityinamoresuccinctway.

TheSortedMethodSwift’sstandardlibraryprovidesamethodcalledsorted(by:),whichsortsanarrayofvaluesofaknowntype,basedontheoutputofasortingclosurethatyouprovide.Onceitcompletesthesortingprocess,thesorted(by:)methodreturnsanewarrayofthesametypeandsizeastheoldone,withitselementsinthecorrectsortedorder.Theoriginalarrayisnotmodifiedbythesorted(by:)method.

Theclosureexpressionexamplesbelowusethesorted(by:)methodtosortanarrayofStringvaluesinreversealphabeticalorder.Here’stheinitialarraytobesorted:

letnames=["Chris","Alex","Ewa","Barry","Daniella"]

Thesorted(by:)methodacceptsaclosurethattakestwoargumentsofthesame

typeasthearray’scontents,andreturnsaBoolvaluetosaywhetherthefirstvalueshouldappearbeforeorafterthesecondvalueoncethevaluesaresorted.Thesortingclosureneedstoreturntrueifthefirstvalueshouldappearbeforethesecondvalue,andfalseotherwise.

ThisexampleissortinganarrayofStringvalues,andsothesortingclosureneedstobeafunctionoftype(String,String)->Bool.

Onewaytoprovidethesortingclosureistowriteanormalfunctionofthecorrecttype,andtopassitinasanargumenttothesorted(by:)method:

1 funcbackward(_s1:String,_s2:String)->Bool{

2 returns1>s2

3 }

4 varreversedNames=names.sorted(by:backward)

5 //reversedNamesisequalto["Ewa","Daniella","Chris",

"Barry","Alex"]

Ifthefirststring(s1)isgreaterthanthesecondstring(s2),thebackward(_:_:)functionwillreturntrue,indicatingthats1shouldappearbefores2inthesortedarray.Forcharactersinstrings,“greaterthan”means“appearslaterinthealphabetthan”.Thismeansthattheletter"B"is“greaterthan”theletter"A",andthestring"Tom"isgreaterthanthestring"Tim".Thisgivesareversealphabeticalsort,with"Barry"beingplacedbefore"Alex",andsoon.

However,thisisaratherlong-windedwaytowritewhatisessentiallyasingle-expressionfunction(a>b).Inthisexample,itwouldbepreferabletowritethesortingclosureinline,usingclosureexpressionsyntax.

ClosureExpressionSyntaxClosureexpressionsyntaxhasthefollowinggeneralform:

{( parameters )-> returntype in

statements

}

Theparametersinclosureexpressionsyntaxcanbein-outparameters,buttheycan’thaveadefaultvalue.Variadicparameterscanbeusedifyounamethevariadicparameter.Tuplescanalsobeusedasparametertypesandreturntypes.

Theexamplebelowshowsaclosureexpressionversionofthebackward(_:_:)functionfromabove:

1 reversedNames=names.sorted(by:{(s1:String,s2:String)->

Boolin

2 returns1>s2

3 })

Notethatthedeclarationofparametersandreturntypeforthisinlineclosureisidenticaltothedeclarationfromthebackward(_:_:)function.Inbothcases,itiswrittenas(s1:String,s2:String)->Bool.However,fortheinlineclosureexpression,theparametersandreturntypearewritteninsidethecurlybraces,notoutsideofthem.

Thestartoftheclosure’sbodyisintroducedbytheinkeyword.Thiskeywordindicatesthatthedefinitionoftheclosure’sparametersandreturntypehasfinished,andthebodyoftheclosureisabouttobegin.

Becausethebodyoftheclosureissoshort,itcanevenbewrittenonasingleline:

reversedNames=names.sorted(by:{(s1:String,s2:String)->

Boolinreturns1>s2})

Thisillustratesthattheoverallcalltothesorted(by:)methodhasremainedthesame.Apairofparenthesesstillwraptheentireargumentforthemethod.However,thatargumentisnowaninlineclosure.

InferringTypeFromContextBecausethesortingclosureispassedasanargumenttoamethod,Swiftcaninferthetypesofitsparametersandthetypeofthevalueitreturns.Thesorted(by:)methodisbeingcalledonanarrayofstrings,soitsargumentmustbeafunctionoftype(String,String)->Bool.Thismeansthatthe(String,String)andBooltypesdonotneedtobewrittenaspartoftheclosureexpression’sdefinition.Becauseallofthetypescanbeinferred,thereturnarrow(->)andtheparenthesesaroundthenamesoftheparameterscanalsobeomitted:

reversedNames=names.sorted(by:{s1,s2inreturns1>s2})

Itisalwayspossibletoinfertheparametertypesandreturntypewhenpassingaclosuretoafunctionormethodasaninlineclosureexpression.Asaresult,youneverneedtowriteaninlineclosureinitsfullestformwhentheclosureisusedasafunctionormethodargument.

Nonetheless,youcanstillmakethetypesexplicitifyouwish,anddoingsoisencouragedifitavoidsambiguityforreadersofyourcode.Inthecaseofthesorted(by:)method,thepurposeoftheclosureisclearfromthefactthatsortingistakingplace,anditissafeforareadertoassumethattheclosureislikelytobeworkingwithStringvalues,becauseitisassistingwiththesortingofanarrayofstrings.

ImplicitReturnsfromSingle-ExpressionClosuresSingle-expressionclosurescanimplicitlyreturntheresultoftheirsingleexpressionbyomittingthereturnkeywordfromtheirdeclaration,asinthisversionofthepreviousexample:

reversedNames=names.sorted(by:{s1,s2ins1>s2})

Here,thefunctiontypeofthesorted(by:)method’sargumentmakesitclearthataBoolvaluemustbereturnedbytheclosure.Becausetheclosure’sbodycontainsasingleexpression(s1>s2)thatreturnsaBoolvalue,thereisnoambiguity,andthereturnkeywordcanbeomitted.

ShorthandArgumentNamesSwiftautomaticallyprovidesshorthandargumentnamestoinlineclosures,whichcanbeusedtorefertothevaluesoftheclosure’sargumentsbythenames$0,$1,$2,andsoon.

Ifyouusetheseshorthandargumentnameswithinyourclosureexpression,youcanomittheclosure’sargumentlistfromitsdefinition,andthenumberandtypeoftheshorthandargumentnameswillbeinferredfromtheexpectedfunctiontype.Theinkeywordcanalsobeomitted,becausetheclosureexpressionismadeupentirelyofitsbody:

reversedNames=names.sorted(by:{$0>$1})

Here,$0and$1refertotheclosure’sfirstandsecondStringarguments.

OperatorMethodsThere’sactuallyanevenshorterwaytowritetheclosureexpressionabove.Swift’sStringtypedefinesitsstring-specificimplementationofthegreater-thanoperator(>)asamethodthathastwoparametersoftypeString,andreturnsavalueoftypeBool.Thisexactlymatchesthemethodtypeneededbythesorted(by:)method.Therefore,youcansimplypassinthegreater-thanoperator,andSwiftwillinferthatyouwanttouseitsstring-specificimplementation:

reversedNames=names.sorted(by:>)

Formoreaboutoperatormethod,seeOperatorMethods.

TrailingClosures

Ifyouneedtopassaclosureexpressiontoafunctionasthefunction’sfinalargumentandtheclosureexpressionislong,itcanbeusefultowriteitasa

trailingclosureinstead.Atrailingclosureiswrittenafterthefunctioncall’sparentheses,eventhoughitisstillanargumenttothefunction.Whenyouusethetrailingclosuresyntax,youdon’twritetheargumentlabelfortheclosureaspartofthefunctioncall.

1 funcsomeFunctionThatTakesAClosure(closure:()->Void){

2 //functionbodygoeshere

3 }

4

5 //Here'showyoucallthisfunctionwithoutusingatrailing

closure:

6

7 someFunctionThatTakesAClosure(closure:{

8 //closure'sbodygoeshere

9 })

10

11 //Here'showyoucallthisfunctionwithatrailingclosure

instead:

12

13 someFunctionThatTakesAClosure(){

14 //trailingclosure'sbodygoeshere

15 }

Thestring-sortingclosurefromtheClosureExpressionSyntaxsectionabovecanbewrittenoutsideofthesorted(by:)method’sparenthesesasatrailingclosure:

reversedNames=names.sorted(){$0>$1}

Ifaclosureexpressionisprovidedasthefunctionormethod’sonlyargumentandyouprovidethatexpressionasatrailingclosure,youdonotneedtowriteapairofparentheses()afterthefunctionormethod’snamewhenyoucallthefunction:

reversedNames=names.sorted{$0>$1}

Trailingclosuresaremostusefulwhentheclosureissufficientlylongthatitisnotpossibletowriteitinlineonasingleline.Asanexample,Swift’sArraytypehasamap(_:)methodwhichtakesaclosureexpressionasitssingleargument.Theclosureiscalledonceforeachiteminthearray,andreturnsanalternativemappedvalue(possiblyofsomeothertype)forthatitem.Thenatureofthemappingandthetypeofthereturnedvalueisleftuptotheclosuretospecify.

Afterapplyingtheprovidedclosuretoeacharrayelement,themap(_:)methodreturnsanewarraycontainingallofthenewmappedvalues,inthesameorderastheircorrespondingvaluesintheoriginalarray.

Here’showyoucanusethemap(_:)methodwithatrailingclosuretoconvertanarrayofIntvaluesintoanarrayofStringvalues.Thearray[16,58,510]isusedtocreatethenewarray["OneSix","FiveEight","FiveOneZero"]:

1 letdigitNames=[

2 0:"Zero",1:"One",2:"Two",3:"Three",4:"Four",

3 5:"Five",6:"Six",7:"Seven",8:"Eight",9:"Nine"

4 ]

5 letnumbers=[16,58,510]

ThecodeabovecreatesadictionaryofmappingsbetweentheintegerdigitsandEnglish-languageversionsoftheirnames.Italsodefinesanarrayofintegers,readytobeconvertedintostrings.

YoucannowusethenumbersarraytocreateanarrayofStringvalues,bypassingaclosureexpressiontothearray’smap(_:)methodasatrailingclosure:

1 letstrings=numbers.map{(number)->Stringin

2 varnumber=number

3 varoutput=""

4 repeat{

5 output=digitNames[number%10]!+output

6 number/=10

7 }whilenumber>0

8 returnoutput

9 }

10 //stringsisinferredtobeoftype[String]

11 //itsvalueis["OneSix","FiveEight","FiveOneZero"]

Themap(_:)methodcallstheclosureexpressiononceforeachiteminthearray.Youdonotneedtospecifythetypeoftheclosure’sinputparameter,number,becausethetypecanbeinferredfromthevaluesinthearraytobemapped.

Inthisexample,thevariablenumberisinitializedwiththevalueoftheclosure’snumberparameter,sothatthevaluecanbemodifiedwithintheclosurebody.(Theparameterstofunctionsandclosuresarealwaysconstants.)TheclosureexpressionalsospecifiesareturntypeofString,toindicatethetypethatwillbestoredinthemappedoutputarray.

Theclosureexpressionbuildsastringcalledoutputeachtimeitiscalled.Itcalculatesthelastdigitofnumberbyusingtheremainderoperator(number%10),andusesthisdigittolookupanappropriatestringinthedigitNamesdictionary.Theclosurecanbeusedtocreateastringrepresentationofanyintegergreaterthanzero.

NOTE

ThecalltothedigitNamesdictionary’ssubscriptisfollowedbyanexclamationmark(!),becausedictionarysubscriptsreturnanoptionalvaluetoindicatethatthedictionarylookupcanfailifthekeydoesnotexist.Intheexampleabove,itisguaranteedthatnumber%10willalwaysbeavalidsubscriptkeyforthedigitNamesdictionary,andsoanexclamationmarkisusedtoforce-unwraptheStringvaluestoredinthesubscript’soptionalreturnvalue.

ThestringretrievedfromthedigitNamesdictionaryisaddedtothefrontofoutput,effectivelybuildingastringversionofthenumberinreverse.(Theexpressionnumber%10givesavalueof6for16,8for58,and0for510.)

Thenumbervariableisthendividedby10.Becauseitisaninteger,itisroundeddownduringthedivision,so16becomes1,58becomes5,and510becomes51.

Theprocessisrepeateduntilnumberisequalto0,atwhichpointtheoutputstringisreturnedbytheclosure,andisaddedtotheoutputarraybythemap(_:)method.

Theuseoftrailingclosuresyntaxintheexampleaboveneatlyencapsulatestheclosure’sfunctionalityimmediatelyafterthefunctionthatclosuresupports,withoutneedingtowraptheentireclosurewithinthemap(_:)method’souterparentheses.

CapturingValues

Aclosurecancaptureconstantsandvariablesfromthesurroundingcontextinwhichitisdefined.Theclosurecanthenrefertoandmodifythevaluesofthoseconstantsandvariablesfromwithinitsbody,eveniftheoriginalscopethatdefinedtheconstantsandvariablesnolongerexists.

InSwift,thesimplestformofaclosurethatcancapturevaluesisanestedfunction,writtenwithinthebodyofanotherfunction.Anestedfunctioncancaptureanyofitsouterfunction’sargumentsandcanalsocaptureanyconstantsandvariablesdefinedwithintheouterfunction.

Here’sanexampleofafunctioncalledmakeIncrementer,whichcontainsanestedfunctioncalledincrementer.Thenestedincrementer()functioncapturestwovalues,runningTotalandamount,fromitssurroundingcontext.Aftercapturingthesevalues,incrementerisreturnedbymakeIncrementerasaclosurethatincrementsrunningTotalbyamounteachtimeitiscalled.

1 funcmakeIncrementer(forIncrementamount:Int)->()->Int{

2 varrunningTotal=0

3 funcincrementer()->Int{

4 runningTotal+=amount

5 returnrunningTotal

6 }

7 returnincrementer

8 }

ThereturntypeofmakeIncrementeris()->Int.Thismeansthatitreturnsafunction,ratherthanasimplevalue.Thefunctionitreturnshasnoparameters,andreturnsanIntvalueeachtimeitiscalled.Tolearnhowfunctionscanreturnotherfunctions,seeFunctionTypesasReturnTypes.

ThemakeIncrementer(forIncrement:)functiondefinesanintegervariablecalledrunningTotal,tostorethecurrentrunningtotaloftheincrementerthatwillbereturned.Thisvariableisinitializedwithavalueof0.

ThemakeIncrementer(forIncrement:)functionhasasingleIntparameterwithanargumentlabelofforIncrement,andaparameternameofamount.TheargumentvaluepassedtothisparameterspecifieshowmuchrunningTotalshouldbeincrementedbyeachtimethereturnedincrementerfunctioniscalled.ThemakeIncrementerfunctiondefinesanestedfunctioncalledincrementer,whichperformstheactualincrementing.ThisfunctionsimplyaddsamounttorunningTotal,andreturnstheresult.

Whenconsideredinisolation,thenestedincrementer()functionmightseemunusual:

1 funcincrementer()->Int{

2 runningTotal+=amount

3 returnrunningTotal

4 }

Theincrementer()functiondoesn’thaveanyparameters,andyetitreferstorunningTotalandamountfromwithinitsfunctionbody.ItdoesthisbycapturingareferencetorunningTotalandamountfromthesurroundingfunctionandusingthemwithinitsownfunctionbody.CapturingbyreferenceensuresthatrunningTotalandamountdonotdisappearwhenthecalltomakeIncrementerends,andalsoensuresthatrunningTotalisavailablethenexttimetheincrementerfunctioniscalled.

NOTE

Asanoptimization,Swiftmayinsteadcaptureandstoreacopyofavalueifthatvalueisnotmutatedbyaclosure,andifthevalueisnotmutatedaftertheclosureiscreated.

Swiftalsohandlesallmemorymanagementinvolvedindisposingofvariableswhentheyarenolongerneeded.

Here’sanexampleofmakeIncrementerinaction:

letincrementByTen=makeIncrementer(forIncrement:10)

ThisexamplesetsaconstantcalledincrementByTentorefertoanincrementerfunctionthatadds10toitsrunningTotalvariableeachtimeitiscalled.Callingthefunctionmultipletimesshowsthisbehaviorinaction:

1 incrementByTen()

2 //returnsavalueof10

3 incrementByTen()

4 //returnsavalueof20

5 incrementByTen()

6 //returnsavalueof30

Ifyoucreateasecondincrementer,itwillhaveitsownstoredreferencetoanew,separaterunningTotalvariable:

1 letincrementBySeven=makeIncrementer(forIncrement:7)

2 incrementBySeven()

3 //returnsavalueof7

Callingtheoriginalincrementer(incrementByTen)againcontinuestoincrementitsownrunningTotalvariable,anddoesnotaffectthevariablecapturedbyincrementBySeven:

1 incrementByTen()

2 //returnsavalueof40

NOTE

Ifyouassignaclosuretoapropertyofaclassinstance,andtheclosurecapturesthatinstancebyreferringtotheinstanceoritsmembers,youwillcreateastrongreferencecyclebetweentheclosureandtheinstance.Swiftusescaptureliststobreakthesestrongreferencecycles.Formoreinformation,seeStrongReferenceCyclesforClosures.

ClosuresAreReferenceTypes

Intheexampleabove,incrementBySevenandincrementByTenareconstants,buttheclosurestheseconstantsrefertoarestillabletoincrementtherunningTotalvariablesthattheyhavecaptured.Thisisbecausefunctionsandclosuresarereferencetypes.

Wheneveryouassignafunctionoraclosuretoaconstantoravariable,youareactuallysettingthatconstantorvariabletobeareferencetothefunctionorclosure.Intheexampleabove,itisthechoiceofclosurethatincrementByTenreferstothatisconstant,andnotthecontentsoftheclosureitself.

Thisalsomeansthatifyouassignaclosuretotwodifferentconstantsorvariables,bothofthoseconstantsorvariablesrefertothesameclosure.

1 letalsoIncrementByTen=incrementByTen

2 alsoIncrementByTen()

3 //returnsavalueof50

4

5 incrementByTen()

6 //returnsavalueof60

TheexampleaboveshowsthatcallingalsoIncrementByTenisthesameascallingincrementByTen.Becausebothofthemrefertothesameclosure,theybothincrementandreturnthesamerunningtotal.

EscapingClosures

Aclosureissaidtoescapeafunctionwhentheclosureispassedasanargumenttothefunction,butiscalledafterthefunctionreturns.Whenyoudeclareafunctionthattakesaclosureasoneofitsparameters,youcanwrite@escapingbeforetheparameter’stypetoindicatethattheclosureisallowedtoescape.

Onewaythataclosurecanescapeisbybeingstoredinavariablethatisdefinedoutsidethefunction.Asanexample,manyfunctionsthatstartanasynchronousoperationtakeaclosureargumentasacompletionhandler.Thefunctionreturnsafteritstartstheoperation,buttheclosureisn’tcalleduntiltheoperationiscompleted—theclosureneedstoescape,tobecalledlater.Forexample:

1 varcompletionHandlers:[()->Void]=[]

2 funcsomeFunctionWithEscapingClosure(completionHandler:

@escaping()->Void){

3 completionHandlers.append(completionHandler)

4 }

ThesomeFunctionWithEscapingClosure(_:)functiontakesaclosureasitsargumentandaddsittoanarraythat’sdeclaredoutsidethefunction.Ifyoudidn’tmarktheparameterofthisfunctionwith@escaping,youwouldgetacompile-timeerror.

Markingaclosurewith@escapingmeansyouhavetorefertoselfexplicitlywithintheclosure.Forexample,inthecodebelow,theclosurepassedtosomeFunctionWithEscapingClosure(_:)isanescapingclosure,whichmeansitneedstorefertoselfexplicitly.Incontrast,theclosurepassedtosomeFunctionWithNonescapingClosure(_:)isanonescapingclosure,whichmeansitcanrefertoselfimplicitly.

1 funcsomeFunctionWithNonescapingClosure(closure:()->Void){

2 closure()

3 }

4

5 classSomeClass{

6 varx=10

7 funcdoSomething(){

8 someFunctionWithEscapingClosure{self.x=100}

9 someFunctionWithNonescapingClosure{x=200}

10 }

11 }

12

13 letinstance=SomeClass()

14 instance.doSomething()

15 print(instance.x)

16 //Prints"200"

17

18 completionHandlers.first?()

19 print(instance.x)

20 //Prints"100"

Autoclosures

Anautoclosureisaclosurethatisautomaticallycreatedtowrapanexpressionthat’sbeingpassedasanargumenttoafunction.Itdoesn’ttakeanyarguments,andwhenit’scalled,itreturnsthevalueoftheexpressionthat’swrappedinsideofit.Thissyntacticconvenienceletsyouomitbracesaroundafunction’sparameterbywritinganormalexpressioninsteadofanexplicitclosure.

It’scommontocallfunctionsthattakeautoclosures,butit’snotcommontoimplementthatkindoffunction.Forexample,theassert(condition:message:file:line:)functiontakesanautoclosureforitsconditionandmessageparameters;itsconditionparameterisevaluatedonlyindebugbuildsanditsmessageparameterisevaluatedonlyifconditionisfalse.

Anautoclosureletsyoudelayevaluation,becausethecodeinsideisn’trununtilyoucalltheclosure.Delayingevaluationisusefulforcodethathassideeffectsoriscomputationallyexpensive,becauseitletsyoucontrolwhenthatcodeisevaluated.Thecodebelowshowshowaclosuredelaysevaluation.

1 varcustomersInLine=["Chris","Alex","Ewa","Barry",

"Daniella"]

2 print(customersInLine.count)

3 //Prints"5"

4

5 letcustomerProvider={customersInLine.remove(at:0)}

6 print(customersInLine.count)

7 //Prints"5"

8

9 print("Nowserving\(customerProvider())!")

10 //Prints"NowservingChris!"

11 print(customersInLine.count)

12 //Prints"4"

EventhoughthefirstelementofthecustomersInLinearrayisremovedbythecodeinsidetheclosure,thearrayelementisn’tremoveduntiltheclosureisactuallycalled.Iftheclosureisnevercalled,theexpressioninsidetheclosureisneverevaluated,whichmeansthearrayelementisneverremoved.NotethatthetypeofcustomerProviderisnotStringbut()->String—afunctionwithnoparametersthatreturnsastring.

Yougetthesamebehaviorofdelayedevaluationwhenyoupassaclosureasanargumenttoafunction.

1 //customersInLineis["Alex","Ewa","Barry","Daniella"]

2 funcserve(customercustomerProvider:()->String){

3 print("Nowserving\(customerProvider())!")

4 }

5 serve(customer:{customersInLine.remove(at:0)})

6 //Prints"NowservingAlex!"

Theserve(customer:)functioninthelistingabovetakesanexplicitclosurethatreturnsacustomer’sname.Theversionofserve(customer:)belowperformsthesameoperationbut,insteadoftakinganexplicitclosure,ittakesanautoclosurebymarkingitsparameter’stypewiththe@autoclosureattribute.NowyoucancallthefunctionasifittookaStringargumentinsteadofaclosure.Theargumentisautomaticallyconvertedtoaclosure,becausethecustomerProviderparameter’stypeismarkedwiththe@autoclosureattribute.

1 //customersInLineis["Ewa","Barry","Daniella"]

2 funcserve(customercustomerProvider:@autoclosure()->

String){

3 print("Nowserving\(customerProvider())!")

4 }

5 serve(customer:customersInLine.remove(at:0))

6 //Prints"NowservingEwa!"

NOTE

Overusingautoclosurescanmakeyourcodehardtounderstand.Thecontextandfunctionnameshouldmakeitclearthatevaluationisbeingdeferred.

Ifyouwantanautoclosurethatisallowedtoescape,useboththe@autoclosureand@escapingattributes.The@escapingattributeisdescribedaboveinEscapingClosures.

1 //customersInLineis["Barry","Daniella"]

2 varcustomerProviders:[()->String]=[]

3 funccollectCustomerProviders(_customerProvider:@autoclosure

@escaping()->String){

4 customerProviders.append(customerProvider)

5 }

6 collectCustomerProviders(customersInLine.remove(at:0))

7 collectCustomerProviders(customersInLine.remove(at:0))

8

9 print("Collected\(customerProviders.count)closures.")

10 //Prints"Collected2closures."

11 forcustomerProviderincustomerProviders{

12 print("Nowserving\(customerProvider())!")

13 }

14 //Prints"NowservingBarry!"

15 //Prints"NowservingDaniella!"

Inthecodeabove,insteadofcallingtheclosurepassedtoitasitscustomerProviderargument,thecollectCustomerProviders(_:)functionappendstheclosuretothecustomerProvidersarray.Thearrayisdeclaredoutsidethescopeofthefunction,whichmeanstheclosuresinthearraycanbeexecutedafterthefunctionreturns.Asaresult,thevalueofthecustomerProviderargumentmustbeallowedtoescapethefunction’sscope.

Enumerations

Anenumerationdefinesacommontypeforagroupofrelatedvaluesandenablesyoutoworkwiththosevaluesinatype-safewaywithinyourcode.

IfyouarefamiliarwithC,youwillknowthatCenumerationsassignrelatednamestoasetofintegervalues.EnumerationsinSwiftaremuchmoreflexible,anddon’thavetoprovideavalueforeachcaseoftheenumeration.Ifavalue(knownasarawvalue)isprovidedforeachenumerationcase,thevaluecanbeastring,acharacter,oravalueofanyintegerorfloating-pointtype.

Alternatively,enumerationcasescanspecifyassociatedvaluesofanytypetobestoredalongwitheachdifferentcasevalue,muchasunionsorvariantsdoinotherlanguages.Youcandefineacommonsetofrelatedcasesaspartofoneenumeration,eachofwhichhasadifferentsetofvaluesofappropriatetypesassociatedwithit.

EnumerationsinSwiftarefirst-classtypesintheirownright.Theyadoptmanyfeaturestraditionallysupportedonlybyclasses,suchascomputedpropertiestoprovideadditionalinformationabouttheenumeration’scurrentvalue,andinstancemethodstoprovidefunctionalityrelatedtothevaluestheenumerationrepresents.Enumerationscanalsodefineinitializerstoprovideaninitialcasevalue;canbeextendedtoexpandtheirfunctionalitybeyondtheiroriginalimplementation;andcanconformtoprotocolstoprovidestandardfunctionality.

Formoreaboutthesecapabilities,seeProperties,Methods,Initialization,Extensions,andProtocols.

EnumerationSyntax

Youintroduceenumerationswiththeenumkeywordandplacetheirentiredefinitionwithinapairofbraces:

1 enumSomeEnumeration{

2 //enumerationdefinitiongoeshere

3 }

Here’sanexampleforthefourmainpointsofacompass:

1 enumCompassPoint{

2 casenorth

3 casesouth

4 caseeast

5 casewest

6 }

Thevaluesdefinedinanenumeration(suchasnorth,south,east,andwest)areitsenumerationcases.Youusethecasekeywordtointroducenewenumerationcases.

NOTE

Swiftenumerationcasesdon’thaveanintegervaluesetbydefault,unlikelanguageslikeCandObjective-C.IntheCompassPointexampleabove,north,south,eastandwestdon’timplicitlyequal0,1,2and3.Instead,thedifferentenumerationcasesarevaluesintheirownright,withanexplicitlydefinedtypeofCompassPoint.

Multiplecasescanappearonasingleline,separatedbycommas:

1 enumPlanet{

2 casemercury,venus,earth,mars,jupiter,saturn,uranus,

neptune

3 }

Eachenumerationdefinitiondefinesanewtype.LikeothertypesinSwift,theirnames(suchasCompassPointandPlanet)startwithacapitalletter.Giveenumerationtypessingularratherthanpluralnames,sothattheyreadasself-evident:

vardirectionToHead=CompassPoint.west

ThetypeofdirectionToHeadisinferredwhenit’sinitializedwithoneofthepossiblevaluesofCompassPoint.OncedirectionToHeadisdeclaredasaCompassPoint,youcansetittoadifferentCompassPointvalueusingashorterdotsyntax:

directionToHead=.east

ThetypeofdirectionToHeadisalreadyknown,andsoyoucandropthetypewhensettingitsvalue.Thismakesforhighlyreadablecodewhenworkingwithexplicitlytypedenumerationvalues.

MatchingEnumerationValueswithaSwitchStatement

Youcanmatchindividualenumerationvalueswithaswitchstatement:

1 directionToHead=.south

2 switchdirectionToHead{

3 case.north:

4 print("Lotsofplanetshaveanorth")

5 case.south:

6 print("Watchoutforpenguins")

7 case.east:

8 print("Wherethesunrises")

9 case.west:

10 print("Wheretheskiesareblue")

11 }

12 //Prints"Watchoutforpenguins"

Youcanreadthiscodeas:

“ConsiderthevalueofdirectionToHead.Inthecasewhereitequals.north,print"Lotsofplanetshaveanorth".Inthecasewhereitequals.south,print"Watchoutforpenguins".”

…andsoon.

AsdescribedinControlFlow,aswitchstatementmustbeexhaustivewhenconsideringanenumeration’scases.Ifthecasefor.westisomitted,thiscodedoesn’tcompile,becauseitdoesn’tconsiderthecompletelistofCompassPointcases.Requiringexhaustivenessensuresthatenumerationcasesaren’taccidentallyomitted.

Whenitisn’tappropriatetoprovideacaseforeveryenumerationcase,youcanprovideadefaultcasetocoveranycasesthataren’taddressedexplicitly:

1 letsomePlanet=Planet.earth

2 switchsomePlanet{

3 case.earth:

4 print("Mostlyharmless")

5 default:

6 print("Notasafeplaceforhumans")

7 }

8 //Prints"Mostlyharmless"

IteratingoverEnumerationCases

Forsomeenumerations,it’susefultohaveacollectionofallofthatenumeration’scases.Youenablethisbywriting:CaseIterableaftertheenumeration’sname.SwiftexposesacollectionofallthecasesasanallCasespropertyoftheenumerationtype.Here’sanexample:

1 enumBeverage:CaseIterable{

2 casecoffee,tea,juice

3 }

4 letnumberOfChoices=Beverage.allCases.count

5 print("\(numberOfChoices)beveragesavailable")

6 //Prints"3beveragesavailable"

Intheexampleabove,youwriteBeverage.allCasestoaccessacollectionthatcontainsallofthecasesoftheBeverageenumeration.YoucanuseallCaseslikeanyothercollection—thecollection’selementsareinstancesoftheenumerationtype,sointhiscasethey’reBeveragevalues.Theexampleabovecountshowmanycasesthereare,andtheexamplebelowusesaforlooptoiterateoverallthecases.

1 forbeverageinBeverage.allCases{

2 print(beverage)

3 }

4 //coffee

5 //tea

6 //juice

ThesyntaxusedintheexamplesabovemarkstheenumerationasconformingtotheCaseIterableprotocol.Forinformationaboutprotocols,seeProtocols.

AssociatedValues

Theexamplesintheprevioussectionshowhowthecasesofanenumerationareadefined(andtyped)valueintheirownright.YoucansetaconstantorvariabletoPlanet.earth,andcheckforthisvaluelater.However,it’ssometimesusefultobeabletostorevaluesofothertypesalongsidethesecasevalues.Thisadditionalinformationiscalledanassociatedvalue,anditvarieseachtimeyouusethatcaseasavalueinyourcode.

YoucandefineSwiftenumerationstostoreassociatedvaluesofanygiventype,

andthevaluetypescanbedifferentforeachcaseoftheenumerationifneeded.Enumerationssimilartotheseareknownasdiscriminatedunions,taggedunions,orvariantsinotherprogramminglanguages.

Forexample,supposeaninventorytrackingsystemneedstotrackproductsbytwodifferenttypesofbarcode.Someproductsarelabeledwith1DbarcodesinUPCformat,whichusesthenumbers0to9.Eachbarcodehasanumbersystemdigit,followedbyfivemanufacturercodedigitsandfiveproductcodedigits.Thesearefollowedbyacheckdigittoverifythatthecodehasbeenscannedcorrectly:

Otherproductsarelabeledwith2DbarcodesinQRcodeformat,whichcanuseanyISO8859-1characterandcanencodeastringupto2,953characterslong:

It’sconvenientforaninventorytrackingsystemtostoreUPCbarcodesasatupleoffourintegers,andQRcodebarcodesasastringofanylength.

InSwift,anenumerationtodefineproductbarcodesofeithertypemightlooklikethis:

1 enumBarcode{

2 caseupc(Int,Int,Int,Int)

3 caseqrCode(String)

4 }

Thiscanbereadas:

“DefineanenumerationtypecalledBarcode,whichcantakeeitheravalueofupcwithanassociatedvalueoftype(Int,Int,Int,Int),oravalueofqrCodewithanassociatedvalueoftypeString.”

Thisdefinitiondoesn’tprovideanyactualIntorStringvalues—itjustdefinesthetypeofassociatedvaluesthatBarcodeconstantsandvariablescanstorewhentheyareequaltoBarcode.upcorBarcode.qrCode.

Youcanthencreatenewbarcodesusingeithertype:

varproductBarcode=Barcode.upc(8,85909,51226,3)

ThisexamplecreatesanewvariablecalledproductBarcodeandassignsitavalueofBarcode.upcwithanassociatedtuplevalueof(8,85909,51226,3).

Youcanassignthesameproductadifferenttypeofbarcode:

productBarcode=.qrCode("ABCDEFGHIJKLMNOP")

Atthispoint,theoriginalBarcode.upcanditsintegervaluesarereplacedbythenewBarcode.qrCodeanditsstringvalue.ConstantsandvariablesoftypeBarcodecanstoreeithera.upcora.qrCode(togetherwiththeirassociatedvalues),buttheycanstoreonlyoneofthematanygiventime.

Youcancheckthedifferentbarcodetypesusingaswitchstatement,similartotheexampleinMatchingEnumerationValueswithaSwitchStatement.Thistime,however,theassociatedvaluesareextractedaspartoftheswitchstatement.Youextracteachassociatedvalueasaconstant(withtheletprefix)oravariable(withthevarprefix)forusewithintheswitchcase’sbody:

1 switchproductBarcode{

2 case.upc(letnumberSystem,letmanufacturer,letproduct,let

check):

3 print("UPC:\(numberSystem),\(manufacturer),\(product),\

(check).")

4 case.qrCode(letproductCode):

5 print("QRcode:\(productCode).")

6 }

7 //Prints"QRcode:ABCDEFGHIJKLMNOP."

Ifalloftheassociatedvaluesforanenumerationcaseareextractedasconstants,orifallareextractedasvariables,youcanplaceasinglevarorletannotationbeforethecasename,forbrevity:

1 switchproductBarcode{

2 caselet.upc(numberSystem,manufacturer,product,check):

3 print("UPC:\(numberSystem),\(manufacturer),\(product),

\(check).")

4 caselet.qrCode(productCode):

5 print("QRcode:\(productCode).")

6 }

7 //Prints"QRcode:ABCDEFGHIJKLMNOP."

RawValues

ThebarcodeexampleinAssociatedValuesshowshowcasesofanenumerationcandeclarethattheystoreassociatedvaluesofdifferenttypes.Asanalternativetoassociatedvalues,enumerationcasescancomeprepopulatedwithdefaultvalues(calledrawvalues),whichareallofthesametype.

Here’sanexamplethatstoresrawASCIIvaluesalongsidenamedenumerationcases:

1 enumASCIIControlCharacter:Character{

2 casetab="\t"

3 caselineFeed="\n"

4 casecarriageReturn="\r"

5 }

Here,therawvaluesforanenumerationcalledASCIIControlCharacteraredefinedtobeoftypeCharacter,andaresettosomeofthemorecommonASCIIcontrolcharacters.CharactervaluesaredescribedinStringsandCharacters.

Rawvaluescanbestrings,characters,oranyoftheintegerorfloating-pointnumbertypes.Eachrawvaluemustbeuniquewithinitsenumerationdeclaration.

NOTE

Rawvaluesarenotthesameasassociatedvalues.Rawvaluesaresettoprepopulatedvalueswhenyoufirstdefinetheenumerationinyourcode,likethethreeASCIIcodesabove.Therawvalueforaparticularenumerationcaseisalwaysthesame.Associatedvaluesaresetwhenyoucreateanewconstantorvariablebasedononeoftheenumeration’scases,andcanbedifferenteachtimeyoudoso.

ImplicitlyAssignedRawValuesWhenyou’reworkingwithenumerationsthatstoreintegerorstringrawvalues,youdon’thavetoexplicitlyassignarawvalueforeachcase.Whenyoudon’t,Swiftautomaticallyassignsthevaluesforyou.

Forexample,whenintegersareusedforrawvalues,theimplicitvalueforeachcaseisonemorethanthepreviouscase.Ifthefirstcasedoesn’thaveavalueset,itsvalueis0.

TheenumerationbelowisarefinementoftheearlierPlanetenumeration,withintegerrawvaluestorepresenteachplanet’sorderfromthesun:

1 enumPlanet:Int{

2 casemercury=1,venus,earth,mars,jupiter,saturn,

uranus,neptune

3 }

Intheexampleabove,Planet.mercuryhasanexplicitrawvalueof1,Planet.venushasanimplicitrawvalueof2,andsoon.

Whenstringsareusedforrawvalues,theimplicitvalueforeachcaseisthetextofthatcase’sname.

TheenumerationbelowisarefinementoftheearlierCompassPointenumeration,withstringrawvaluestorepresenteachdirection’sname:

1 enumCompassPoint:String{

2 casenorth,south,east,west

3 }

Intheexampleabove,CompassPoint.southhasanimplicitrawvalueof"south",andsoon.

YouaccesstherawvalueofanenumerationcasewithitsrawValueproperty:

1 letearthsOrder=Planet.earth.rawValue

2 //earthsOrderis3

3

4 letsunsetDirection=CompassPoint.west.rawValue

5 //sunsetDirectionis"west"

InitializingfromaRawValueIfyoudefineanenumerationwitharaw-valuetype,theenumerationautomaticallyreceivesaninitializerthattakesavalueoftherawvalue’stype(asaparametercalledrawValue)andreturnseitheranenumerationcaseornil.Youcanusethisinitializertotrytocreateanewinstanceoftheenumeration.

ThisexampleidentifiesUranusfromitsrawvalueof7:

1 letpossiblePlanet=Planet(rawValue:7)

2 //possiblePlanetisoftypePlanet?andequalsPlanet.uranus

NotallpossibleIntvalueswillfindamatchingplanet,however.Becauseofthis,therawvalueinitializeralwaysreturnsanoptionalenumerationcase.Intheexampleabove,possiblePlanetisoftypePlanet?,or“optionalPlanet.”

NOTE

Therawvalueinitializerisafailableinitializer,becausenoteveryrawvaluewillreturnanenumerationcase.Formoreinformation,seeFailableInitializers.

Ifyoutrytofindaplanetwithapositionof11,theoptionalPlanetvaluereturnedbytherawvalueinitializerwillbenil:

1 letpositionToFind=11

2 ifletsomePlanet=Planet(rawValue:positionToFind){

3 switchsomePlanet{

4 case.earth:

5 print("Mostlyharmless")

6 default:

7 print("Notasafeplaceforhumans")

8 }

9 }else{

10 print("Thereisn'taplanetatposition\

(positionToFind)")

11 }

12 //Prints"Thereisn'taplanetatposition11"

Thisexampleusesoptionalbindingtotrytoaccessaplanetwitharawvalueof11.ThestatementifletsomePlanet=Planet(rawValue:11)createsanoptionalPlanet,andsetssomePlanettothevalueofthatoptionalPlanetifitcan

beretrieved.Inthiscase,itisn’tpossibletoretrieveaplanetwithapositionof11,andsotheelsebranchisexecutedinstead.

RecursiveEnumerations

Arecursiveenumerationisanenumerationthathasanotherinstanceoftheenumerationastheassociatedvalueforoneormoreoftheenumerationcases.Youindicatethatanenumerationcaseisrecursivebywritingindirectbeforeit,whichtellsthecompilertoinsertthenecessarylayerofindirection.

Forexample,hereisanenumerationthatstoressimplearithmeticexpressions:

1 enumArithmeticExpression{

2 casenumber(Int)

3 indirectcaseaddition(ArithmeticExpression,

ArithmeticExpression)

4 indirectcasemultiplication(ArithmeticExpression,

ArithmeticExpression)

5 }

Youcanalsowriteindirectbeforethebeginningoftheenumerationtoenableindirectionforalloftheenumeration’scasesthathaveanassociatedvalue:

1 indirectenumArithmeticExpression{

2 casenumber(Int)

3 caseaddition(ArithmeticExpression,ArithmeticExpression)

4 casemultiplication(ArithmeticExpression,

ArithmeticExpression)

5 }

Thisenumerationcanstorethreekindsofarithmeticexpressions:aplainnumber,theadditionoftwoexpressions,andthemultiplicationoftwo

expressions.Theadditionandmultiplicationcaseshaveassociatedvaluesthatarealsoarithmeticexpressions—theseassociatedvaluesmakeitpossibletonestexpressions.Forexample,theexpression(5+4)*2hasanumberontheright-handsideofthemultiplicationandanotherexpressionontheleft-handsideofthemultiplication.Becausethedataisnested,theenumerationusedtostorethedataalsoneedstosupportnesting—thismeanstheenumerationneedstoberecursive.ThecodebelowshowstheArithmeticExpressionrecursiveenumerationbeingcreatedfor(5+4)*2:

1 letfive=ArithmeticExpression.number(5)

2 letfour=ArithmeticExpression.number(4)

3 letsum=ArithmeticExpression.addition(five,four)

4 letproduct=ArithmeticExpression.multiplication(sum,

ArithmeticExpression.number(2))

Arecursivefunctionisastraightforwardwaytoworkwithdatathathasarecursivestructure.Forexample,here’safunctionthatevaluatesanarithmeticexpression:

1 funcevaluate(_expression:ArithmeticExpression)->Int{

2 switchexpression{

3 caselet.number(value):

4 returnvalue

5 caselet.addition(left,right):

6 returnevaluate(left)+evaluate(right)

7 caselet.multiplication(left,right):

8 returnevaluate(left)*evaluate(right)

9 }

10 }

11

12 print(evaluate(product))

13 //Prints"18"

Thisfunctionevaluatesaplainnumberbysimplyreturningtheassociatedvalue.Itevaluatesanadditionormultiplicationbyevaluatingtheexpressionontheleft-handside,evaluatingtheexpressionontheright-handside,andthenaddingthemormultiplyingthem.

StructuresandClasses

Structuresandclassesaregeneral-purpose,flexibleconstructsthatbecomethebuildingblocksofyourprogram’scode.Youdefinepropertiesandmethodstoaddfunctionalitytoyourstructuresandclassesusingthesamesyntaxyouusetodefineconstants,variables,andfunctions.

Unlikeotherprogramminglanguages,Swiftdoesn’trequireyoutocreateseparateinterfaceandimplementationfilesforcustomstructuresandclasses.InSwift,youdefineastructureorclassinasinglefile,andtheexternalinterfacetothatclassorstructureisautomaticallymadeavailableforothercodetouse.

NOTE

Aninstanceofaclassistraditionallyknownasanobject.However,Swiftstructuresandclassesaremuchcloserinfunctionalitythaninotherlanguages,andmuchofthischapterdescribesfunctionalitythatappliestoinstancesofeitheraclassorastructuretype.Becauseofthis,themoregeneralterminstanceisused.

ComparingStructuresandClasses

StructuresandclassesinSwifthavemanythingsincommon.Bothcan:

Definepropertiestostorevalues

Definemethodstoprovidefunctionality

Definesubscriptstoprovideaccesstotheirvaluesusingsubscriptsyntax

Defineinitializerstosetuptheirinitialstate

Beextendedtoexpandtheirfunctionalitybeyondadefaultimplementation

Conformtoprotocolstoprovidestandardfunctionalityofacertainkind

Formoreinformation,seeProperties,Methods,Subscripts,Initialization,Extensions,andProtocols.

Classeshaveadditionalcapabilitiesthatstructuresdon’thave:

Inheritanceenablesoneclasstoinheritthecharacteristicsofanother.

Typecastingenablesyoutocheckandinterpretthetypeofaclassinstanceatruntime.

Deinitializersenableaninstanceofaclasstofreeupanyresourcesithasassigned.

Referencecountingallowsmorethanonereferencetoaclassinstance.

Formoreinformation,seeInheritance,TypeCasting,Deinitialization,andAutomaticReferenceCounting.

Theadditionalcapabilitiesthatclassessupportcomeatthecostofincreasedcomplexity.Asageneralguideline,preferstructuresbecausethey’reeasiertoreasonabout,anduseclasseswhenthey’reappropriateornecessary.Inpractice,thismeansmostofthecustomdatatypesyoudefinewillbestructuresandenumerations.Foramoredetailedcomparison,seeChoosingBetweenStructuresandClasses.

DefinitionSyntaxStructuresandclasseshaveasimilardefinitionsyntax.Youintroducestructureswiththestructkeywordandclasseswiththeclasskeyword.Bothplacetheirentiredefinitionwithinapairofbraces:

1 structSomeStructure{

2 //structuredefinitiongoeshere

3 }

4 classSomeClass{

5 //classdefinitiongoeshere

6 }

NOTE

Wheneveryoudefineanewstructureorclass,youdefineanewSwifttype.GivetypesUpperCamelCasenames(suchasSomeStructureandSomeClasshere)tomatchthecapitalizationofstandardSwifttypes(suchasString,Int,andBool).GivepropertiesandmethodslowerCamelCasenames(suchasframeRateandincrementCount)todifferentiatethemfromtypenames.

Here’sanexampleofastructuredefinitionandaclassdefinition:

1 structResolution{

2 varwidth=0

3 varheight=0

4 }

5 classVideoMode{

6 varresolution=Resolution()

7 varinterlaced=false

8 varframeRate=0.0

9 varname:String?

10 }

TheexampleabovedefinesanewstructurecalledResolution,todescribeapixel-baseddisplayresolution.Thisstructurehastwostoredpropertiescalledwidthandheight.Storedpropertiesareconstantsorvariablesthatarebundledupandstoredaspartofthestructureorclass.ThesetwopropertiesareinferredtobeoftypeIntbysettingthemtoaninitialintegervalueof0.

TheexampleabovealsodefinesanewclasscalledVideoMode,todescribeaspecificvideomodeforvideodisplay.Thisclasshasfourvariablestoredproperties.Thefirst,resolution,isinitializedwithanewResolutionstructureinstance,whichinfersapropertytypeofResolution.Fortheotherthreeproperties,newVideoModeinstanceswillbeinitializedwithaninterlacedsettingoffalse(meaning“noninterlacedvideo”),aplaybackframerateof0.0,andanoptionalStringvaluecalledname.Thenamepropertyisautomatically

givenadefaultvalueofnil,or“nonamevalue”,becauseit’sofanoptionaltype.

StructureandClassInstancesTheResolutionstructuredefinitionandtheVideoModeclassdefinitiononlydescribewhataResolutionorVideoModewilllooklike.Theythemselvesdon’tdescribeaspecificresolutionorvideomode.Todothat,youneedtocreateaninstanceofthestructureorclass.

Thesyntaxforcreatinginstancesisverysimilarforbothstructuresandclasses:

1 letsomeResolution=Resolution()

2 letsomeVideoMode=VideoMode()

Structuresandclassesbothuseinitializersyntaxfornewinstances.Thesimplestformofinitializersyntaxusesthetypenameoftheclassorstructurefollowedbyemptyparentheses,suchasResolution()orVideoMode().Thiscreatesanewinstanceoftheclassorstructure,withanypropertiesinitializedtotheirdefaultvalues.ClassandstructureinitializationisdescribedinmoredetailinInitialization.

AccessingPropertiesYoucanaccessthepropertiesofaninstanceusingdotsyntax.Indotsyntax,youwritethepropertynameimmediatelyaftertheinstancename,separatedbyaperiod(.),withoutanyspaces:

1 print("ThewidthofsomeResolutionis\(someResolution.width)")

2 //Prints"ThewidthofsomeResolutionis0"

Inthisexample,someResolution.widthreferstothewidthpropertyofsomeResolution,andreturnsitsdefaultinitialvalueof0.

Youcandrilldownintosubproperties,suchasthewidthpropertyintheresolutionpropertyofaVideoMode:

1 print("ThewidthofsomeVideoModeis\

(someVideoMode.resolution.width)")

2 //Prints"ThewidthofsomeVideoModeis0"

Youcanalsousedotsyntaxtoassignanewvaluetoavariableproperty:

1 someVideoMode.resolution.width=1280

2 print("ThewidthofsomeVideoModeisnow\

(someVideoMode.resolution.width)")

3 //Prints"ThewidthofsomeVideoModeisnow1280"

MemberwiseInitializersforStructureTypesAllstructureshaveanautomaticallygeneratedmemberwiseinitializer,whichyoucanusetoinitializethememberpropertiesofnewstructureinstances.Initialvaluesforthepropertiesofthenewinstancecanbepassedtothememberwiseinitializerbyname:

letvga=Resolution(width:640,height:480)

Unlikestructures,classinstancesdon’treceiveadefaultmemberwiseinitializer.InitializersaredescribedinmoredetailinInitialization.

StructuresandEnumerationsAreValueTypes

Avaluetypeisatypewhosevalueiscopiedwhenit’sassignedtoavariableorconstant,orwhenit’spassedtoafunction.

You’veactuallybeenusingvaluetypesextensivelythroughoutthepreviouschapters.Infact,allofthebasictypesinSwift—integers,floating-pointnumbers,Booleans,strings,arraysanddictionaries—arevaluetypes,andareimplementedasstructuresbehindthescenes.

AllstructuresandenumerationsarevaluetypesinSwift.Thismeansthatanystructureandenumerationinstancesyoucreate—andanyvaluetypestheyhaveasproperties—arealwayscopiedwhentheyarepassedaroundinyourcode.

NOTE

Collectionsdefinedbythestandardlibrarylikearrays,dictionaries,andstringsuseanoptimizationtoreducetheperformancecostofcopying.Insteadofmakingacopyimmediately,thesecollectionssharethememorywheretheelementsarestoredbetweentheoriginalinstanceandanycopies.Ifoneofthecopiesofthecollectionismodified,theelementsarecopiedjustbeforethemodification.Thebehavioryouseeinyourcodeisalwaysasifacopytookplaceimmediately.

Considerthisexample,whichusestheResolutionstructurefromthepreviousexample:

1 lethd=Resolution(width:1920,height:1080)

2 varcinema=hd

ThisexampledeclaresaconstantcalledhdandsetsittoaResolutioninstanceinitializedwiththewidthandheightoffullHDvideo(1920pixelswideby1080pixelshigh).

Itthendeclaresavariablecalledcinemaandsetsittothecurrentvalueofhd.BecauseResolutionisastructure,acopyoftheexistinginstanceismade,andthisnewcopyisassignedtocinema.Eventhoughhdandcinemanowhavethesamewidthandheight,theyaretwocompletelydifferentinstancesbehindthescenes.

Next,thewidthpropertyofcinemaisamendedtobethewidthoftheslightlywider2Kstandardusedfordigitalcinemaprojection(2048pixelswideand1080pixelshigh):

cinema.width=2048

Checkingthewidthpropertyofcinemashowsthatithasindeedchangedtobe2048:

1 print("cinemaisnow\(cinema.width)pixelswide")

2 //Prints"cinemaisnow2048pixelswide"

However,thewidthpropertyoftheoriginalhdinstancestillhastheoldvalueof1920:

1 print("hdisstill\(hd.width)pixelswide")

2 //Prints"hdisstill1920pixelswide"

Whencinemawasgiventhecurrentvalueofhd,thevaluesstoredinhdwerecopiedintothenewcinemainstance.Theendresultwastwocompletelyseparateinstancesthatcontainedthesamenumericvalues.However,becausetheyareseparateinstances,settingthewidthofcinemato2048doesn’taffectthewidthstoredinhd,asshowninthefigurebelow:

Thesamebehaviorappliestoenumerations:

1 enumCompassPoint{

2 casenorth,south,east,west

3 mutatingfuncturnNorth(){

4 self=.north

5 }

6 }

7 varcurrentDirection=CompassPoint.west

8 letrememberedDirection=currentDirection

9 currentDirection.turnNorth()

10

11 print("Thecurrentdirectionis\(currentDirection)")

12 print("Theremembereddirectionis\(rememberedDirection)")

13 //Prints"Thecurrentdirectionisnorth"

14 //Prints"Theremembereddirectioniswest"

WhenrememberedDirectionisassignedthevalueofcurrentDirection,it’sactuallysettoacopyofthatvalue.ChangingthevalueofcurrentDirectionthereafterdoesn’taffectthecopyoftheoriginalvaluethatwasstoredinrememberedDirection.

ClassesAreReferenceTypes

Unlikevaluetypes,referencetypesarenotcopiedwhentheyareassignedtoavariableorconstant,orwhentheyarepassedtoafunction.Ratherthanacopy,areferencetothesameexistinginstanceisused.

Here’sanexample,usingtheVideoModeclassdefinedabove:

1 lettenEighty=VideoMode()

2 tenEighty.resolution=hd

3 tenEighty.interlaced=true

4 tenEighty.name="1080i"

5 tenEighty.frameRate=25.0

ThisexampledeclaresanewconstantcalledtenEightyandsetsittorefertoanewinstanceoftheVideoModeclass.ThevideomodeisassignedacopyoftheHDresolutionof1920by1080frombefore.It’ssettobeinterlaced,itsnameissetto"1080i",anditsframerateissetto25.0framespersecond.

Next,tenEightyisassignedtoanewconstant,calledalsoTenEighty,andtheframerateofalsoTenEightyismodified:

1 letalsoTenEighty=tenEighty

2 alsoTenEighty.frameRate=30.0

Becauseclassesarereferencetypes,tenEightyandalsoTenEightyactuallybothrefertothesameVideoModeinstance.Effectively,theyarejusttwodifferentnamesforthesamesingleinstance,asshowninthefigurebelow:

CheckingtheframeRatepropertyoftenEightyshowsthatitcorrectlyreportsthenewframerateof30.0fromtheunderlyingVideoModeinstance:

1 print("TheframeRatepropertyoftenEightyisnow\

(tenEighty.frameRate)")

2 //Prints"TheframeRatepropertyoftenEightyisnow30.0"

Thisexamplealsoshowshowreferencetypescanbehardertoreasonabout.IftenEightyandalsoTenEightywerefarapartinyourprogram’scode,itcouldbedifficulttofindallthewaysthatthevideomodeischanged.WhereveryouusetenEighty,youalsohavetothinkaboutthecodethatusesalsoTenEighty,andviceversa.Incontrast,valuetypesareeasiertoreasonaboutbecauseallofthecodethatinteractswiththesamevalueisclosetogetherinyoursourcefiles.

NotethattenEightyandalsoTenEightyaredeclaredasconstants,ratherthanvariables.However,youcanstillchangetenEighty.frameRateandalsoTenEighty.frameRatebecausethevaluesofthetenEightyandalsoTenEightyconstantsthemselvesdon’tactuallychange.tenEightyandalsoTenEightythemselvesdon’t“store”theVideoModeinstance—instead,theybothrefertoaVideoModeinstancebehindthescenes.It’stheframeRatepropertyoftheunderlyingVideoModethatischanged,notthevaluesoftheconstantreferencestothatVideoMode.

IdentityOperatorsBecauseclassesarereferencetypes,it’spossibleformultipleconstantsandvariablestorefertothesamesingleinstanceofaclassbehindthescenes.(Thesameisn’ttrueforstructuresandenumerations,becausetheyarealwayscopiedwhentheyareassignedtoaconstantorvariable,orpassedtoafunction.)

Itcansometimesbeusefultofindoutwhethertwoconstantsorvariablesrefertoexactlythesameinstanceofaclass.Toenablethis,Swiftprovidestwoidentityoperators:

Identicalto(===)

Notidenticalto(!==)

Usetheseoperatorstocheckwhethertwoconstantsorvariablesrefertothesamesingleinstance:

1 iftenEighty===alsoTenEighty{

2 print("tenEightyandalsoTenEightyrefertothesame

VideoModeinstance.")

3 }

4 //Prints"tenEightyandalsoTenEightyrefertothesame

VideoModeinstance."

Notethatidenticalto(representedbythreeequalssigns,or===)doesn’tmeanthesamethingasequalto(representedbytwoequalssigns,or==).Identicaltomeansthattwoconstantsorvariablesofclasstyperefertoexactlythesameclassinstance.Equaltomeansthattwoinstancesareconsideredequalorequivalentinvalue,forsomeappropriatemeaningofequal,asdefinedbythetype’sdesigner.

Whenyoudefineyourowncustomstructuresandclasses,it’syourresponsibilitytodecidewhatqualifiesastwoinstancesbeingequal.Theprocessofdefiningyourownimplementationsofthe==and!=operatorsisdescribedinEquivalenceOperators.

PointersIfyouhaveexperiencewithC,C++,orObjective-C,youmayknowthattheselanguagesusepointerstorefertoaddressesinmemory.ASwiftconstantorvariablethatreferstoaninstanceofsomereferencetypeissimilartoapointerinC,butisn’tadirectpointertoanaddressinmemory,anddoesn’trequireyoutowriteanasterisk(*)toindicatethatyouarecreatingareference.Instead,thesereferencesaredefinedlikeanyotherconstantorvariableinSwift.Thestandardlibraryprovidespointerandbuffertypesthatyoucanuseifyouneedtointeractwithpointersdirectly—seeManualMemoryManagement.

Properties

Propertiesassociatevalueswithaparticularclass,structure,orenumeration.Storedpropertiesstoreconstantandvariablevaluesaspartofaninstance,whereascomputedpropertiescalculate(ratherthanstore)avalue.Computedpropertiesareprovidedbyclasses,structures,andenumerations.Storedpropertiesareprovidedonlybyclassesandstructures.

Storedandcomputedpropertiesareusuallyassociatedwithinstancesofaparticulartype.However,propertiescanalsobeassociatedwiththetypeitself.Suchpropertiesareknownastypeproperties.

Inaddition,youcandefinepropertyobserverstomonitorchangesinaproperty’svalue,whichyoucanrespondtowithcustomactions.Propertyobserverscanbeaddedtostoredpropertiesyoudefineyourself,andalsotopropertiesthatasubclassinheritsfromitssuperclass.

Youcanalsouseapropertywrappertoreusecodeinthegetterandsetterofmultipleproperties.

StoredProperties

Initssimplestform,astoredpropertyisaconstantorvariablethatisstoredaspartofaninstanceofaparticularclassorstructure.Storedpropertiescanbeeithervariablestoredproperties(introducedbythevarkeyword)orconstantstoredproperties(introducedbytheletkeyword).

Youcanprovideadefaultvalueforastoredpropertyaspartofitsdefinition,asdescribedinDefaultPropertyValues.Youcanalsosetandmodifytheinitialvalueforastoredpropertyduringinitialization.Thisistrueevenforconstantstoredproperties,asdescribedinAssigningConstantPropertiesDuringInitialization.

TheexamplebelowdefinesastructurecalledFixedLengthRange,whichdescribes

arangeofintegerswhoserangelengthcannotbechangedafteritiscreated:

1 structFixedLengthRange{

2 varfirstValue:Int

3 letlength:Int

4 }

5 varrangeOfThreeItems=FixedLengthRange(firstValue:0,length:

3)

6 //therangerepresentsintegervalues0,1,and2

7 rangeOfThreeItems.firstValue=6

8 //therangenowrepresentsintegervalues6,7,and8

InstancesofFixedLengthRangehaveavariablestoredpropertycalledfirstValueandaconstantstoredpropertycalledlength.Intheexampleabove,lengthisinitializedwhenthenewrangeiscreatedandcannotbechangedthereafter,becauseitisaconstantproperty.

StoredPropertiesofConstantStructureInstancesIfyoucreateaninstanceofastructureandassignthatinstancetoaconstant,youcannotmodifytheinstance’sproperties,eveniftheyweredeclaredasvariableproperties:

1 letrangeOfFourItems=FixedLengthRange(firstValue:0,length:

4)

2 //thisrangerepresentsintegervalues0,1,2,and3

3 rangeOfFourItems.firstValue=6

4 //thiswillreportanerror,eventhoughfirstValueisa

variableproperty

BecauserangeOfFourItemsisdeclaredasaconstant(withtheletkeyword),itisnotpossibletochangeitsfirstValueproperty,eventhoughfirstValueisa

variableproperty.

Thisbehaviorisduetostructuresbeingvaluetypes.Whenaninstanceofavaluetypeismarkedasaconstant,soareallofitsproperties.

Thesameisnottrueforclasses,whicharereferencetypes.Ifyouassignaninstanceofareferencetypetoaconstant,youcanstillchangethatinstance’svariableproperties.

LazyStoredPropertiesAlazystoredpropertyisapropertywhoseinitialvalueisnotcalculateduntilthefirsttimeitisused.Youindicatealazystoredpropertybywritingthelazymodifierbeforeitsdeclaration.

NOTE

Youmustalwaysdeclarealazypropertyasavariable(withthevarkeyword),becauseitsinitialvaluemightnotberetrieveduntilafterinstanceinitializationcompletes.Constantpropertiesmustalwayshaveavaluebeforeinitializationcompletes,andthereforecannotbedeclaredaslazy.

Lazypropertiesareusefulwhentheinitialvalueforapropertyisdependentonoutsidefactorswhosevaluesarenotknownuntilafteraninstance’sinitializationiscomplete.Lazypropertiesarealsousefulwhentheinitialvalueforapropertyrequirescomplexorcomputationallyexpensivesetupthatshouldnotbeperformedunlessoruntilitisneeded.

Theexamplebelowusesalazystoredpropertytoavoidunnecessaryinitializationofacomplexclass.ThisexampledefinestwoclassescalledDataImporterandDataManager,neitherofwhichisshowninfull:

1 classDataImporter{

2 /*

3 DataImporterisaclasstoimportdatafromanexternal

file.

4 Theclassisassumedtotakeanontrivialamountoftimeto

initialize.

5 */

6 varfilename="data.txt"

7 //theDataImporterclasswouldprovidedataimporting

functionalityhere

8 }

9

10 classDataManager{

11 lazyvarimporter=DataImporter()

12 vardata=[String]()

13 //theDataManagerclasswouldprovidedatamanagement

functionalityhere

14 }

15

16 letmanager=DataManager()

17 manager.data.append("Somedata")

18 manager.data.append("Somemoredata")

19 //theDataImporterinstancefortheimporterpropertyhasnot

yetbeencreated

TheDataManagerclasshasastoredpropertycalleddata,whichisinitializedwithanew,emptyarrayofStringvalues.Althoughtherestofitsfunctionalityisnotshown,thepurposeofthisDataManagerclassistomanageandprovideaccesstothisarrayofStringdata.

PartofthefunctionalityoftheDataManagerclassistheabilitytoimportdatafromafile.ThisfunctionalityisprovidedbytheDataImporterclass,whichisassumedtotakeanontrivialamountoftimetoinitialize.ThismightbebecauseaDataImporterinstanceneedstoopenafileandreaditscontentsintomemorywhentheDataImporterinstanceisinitialized.

ItispossibleforaDataManagerinstancetomanageitsdatawithouteverimportingdatafromafile,sothereisnoneedtocreateanewDataImporter

instancewhentheDataManageritselfiscreated.Instead,itmakesmoresensetocreatetheDataImporterinstanceifandwhenitisfirstused.

Becauseitismarkedwiththelazymodifier,theDataImporterinstancefortheimporterpropertyisonlycreatedwhentheimporterpropertyisfirstaccessed,suchaswhenitsfilenamepropertyisqueried:

1 print(manager.importer.filename)

2 //theDataImporterinstancefortheimporterpropertyhasnow

beencreated

3 //Prints"data.txt"

NOTE

Ifapropertymarkedwiththelazymodifierisaccessedbymultiplethreadssimultaneouslyandthepropertyhasnotyetbeeninitialized,thereisnoguaranteethatthepropertywillbeinitializedonlyonce.

StoredPropertiesandInstanceVariablesIfyouhaveexperiencewithObjective-C,youmayknowthatitprovidestwowaystostorevaluesandreferencesaspartofaclassinstance.Inadditiontoproperties,youcanuseinstancevariablesasabackingstoreforthevaluesstoredinaproperty.

Swiftunifiestheseconceptsintoasinglepropertydeclaration.ASwiftpropertydoesnothaveacorrespondinginstancevariable,andthebackingstoreforapropertyisnotaccesseddirectly.Thisapproachavoidsconfusionabouthowthevalueisaccessedindifferentcontextsandsimplifiestheproperty’sdeclarationintoasingle,definitivestatement.Allinformationabouttheproperty—includingitsname,type,andmemorymanagementcharacteristics—isdefinedinasinglelocationaspartofthetype’sdefinition.

ComputedProperties

Inadditiontostoredproperties,classes,structures,andenumerationscandefinecomputedproperties,whichdonotactuallystoreavalue.Instead,theyprovideagetterandanoptionalsettertoretrieveandsetotherpropertiesandvaluesindirectly.

1 structPoint{

2 varx=0.0,y=0.0

3 }

4 structSize{

5 varwidth=0.0,height=0.0

6 }

7 structRect{

8 varorigin=Point()

9 varsize=Size()

10 varcenter:Point{

11 get{

12 letcenterX=origin.x+(size.width/2)

13 letcenterY=origin.y+(size.height/2)

14 returnPoint(x:centerX,y:centerY)

15 }

16 set(newCenter){

17 origin.x=newCenter.x-(size.width/2)

18 origin.y=newCenter.y-(size.height/2)

19 }

20 }

21 }

22 varsquare=Rect(origin:Point(x:0.0,y:0.0),

23 size:Size(width:10.0,height:10.0))

24 letinitialSquareCenter=square.center

25 square.center=Point(x:15.0,y:15.0)

26 print("square.originisnowat(\(square.origin.x),\

(square.origin.y))")

27 //Prints"square.originisnowat(10.0,10.0)"

Thisexampledefinesthreestructuresforworkingwithgeometricshapes:

Pointencapsulatesthex-andy-coordinateofapoint.

Sizeencapsulatesawidthandaheight.

Rectdefinesarectanglebyanoriginpointandasize.

TheRectstructurealsoprovidesacomputedpropertycalledcenter.ThecurrentcenterpositionofaRectcanalwaysbedeterminedfromitsoriginandsize,andsoyoudon’tneedtostorethecenterpointasanexplicitPointvalue.Instead,Rectdefinesacustomgetterandsetterforacomputedvariablecalledcenter,toenableyoutoworkwiththerectangle’scenterasifitwerearealstoredproperty.

TheexampleabovecreatesanewRectvariablecalledsquare.Thesquarevariableisinitializedwithanoriginpointof(0,0),andawidthandheightof10.Thissquareisrepresentedbythebluesquareinthediagrambelow.

Thesquarevariable’scenterpropertyisthenaccessedthroughdotsyntax(square.center),whichcausesthegetterforcentertobecalled,toretrievethecurrentpropertyvalue.Ratherthanreturninganexistingvalue,thegetteractuallycalculatesandreturnsanewPointtorepresentthecenterofthesquare.Ascanbeseenabove,thegettercorrectlyreturnsacenterpointof(5,5).

Thecenterpropertyisthensettoanewvalueof(15,15),whichmovesthesquareupandtotheright,tothenewpositionshownbytheorangesquareinthediagrambelow.Settingthecenterpropertycallsthesetterforcenter,whichmodifiesthexandyvaluesofthestoredoriginproperty,andmovesthesquaretoitsnewposition.

ShorthandSetterDeclarationIfacomputedproperty’ssetterdoesn’tdefineanameforthenewvaluetobeset,adefaultnameofnewValueisused.Here’sanalternativeversionoftheRectstructurethattakesadvantageofthisshorthandnotation:

1 structAlternativeRect{

2 varorigin=Point()

3 varsize=Size()

4 varcenter:Point{

5 get{

6 letcenterX=origin.x+(size.width/2)

7 letcenterY=origin.y+(size.height/2)

8 returnPoint(x:centerX,y:centerY)

9 }

10 set{

11 origin.x=newValue.x-(size.width/2)

12 origin.y=newValue.y-(size.height/2)

13 }

14 }

15 }

ShorthandGetterDeclarationIftheentirebodyofagetterisasingleexpression,thegetterimplicitlyreturnsthatexpression.Here’sananotherversionoftheRectstructurethattakesadvantageofthisshorthandnotationandtheshorthandnotationforsetters:

1 structCompactRect{

2 varorigin=Point()

3 varsize=Size()

4 varcenter:Point{

5 get{

6 Point(x:origin.x+(size.width/2),

7 y:origin.y+(size.height/2))

8 }

9 set{

10 origin.x=newValue.x-(size.width/2)

11 origin.y=newValue.y-(size.height/2)

12 }

13 }

14 }

Omittingthereturnfromagetterfollowsthesamerulesasomittingreturnfromafunction,asdescribedinFunctionsWithanImplicitReturn.

Read-OnlyComputedPropertiesAcomputedpropertywithagetterbutnosetterisknownasaread-onlycomputedproperty.Aread-onlycomputedpropertyalwaysreturnsavalue,andcanbeaccessedthroughdotsyntax,butcannotbesettoadifferentvalue.

NOTE

Youmustdeclarecomputedproperties—includingread-onlycomputedproperties—asvariablepropertieswiththevarkeyword,becausetheirvalueisnotfixed.Theletkeywordisonlyusedforconstantproperties,toindicatethattheirvaluescannotbechangedoncetheyaresetaspartofinstanceinitialization.

Youcansimplifythedeclarationofaread-onlycomputedpropertybyremovingthegetkeywordanditsbraces:

1 structCuboid{

2 varwidth=0.0,height=0.0,depth=0.0

3 varvolume:Double{

4 returnwidth*height*depth

5 }

6 }

7 letfourByFiveByTwo=Cuboid(width:4.0,height:5.0,depth:

2.0)

8 print("thevolumeoffourByFiveByTwois\

(fourByFiveByTwo.volume)")

9 //Prints"thevolumeoffourByFiveByTwois40.0"

ThisexampledefinesanewstructurecalledCuboid,whichrepresentsa3Drectangularboxwithwidth,height,anddepthproperties.Thisstructurealsohasaread-onlycomputedpropertycalledvolume,whichcalculatesandreturnsthecurrentvolumeofthecuboid.Itdoesn’tmakesenseforvolumetobesettable,becauseitwouldbeambiguousastowhichvaluesofwidth,height,anddepthshouldbeusedforaparticularvolumevalue.Nonetheless,itisusefulforaCuboidtoprovidearead-onlycomputedpropertytoenableexternaluserstodiscoveritscurrentcalculatedvolume.

PropertyObservers

Propertyobserversobserveandrespondtochangesinaproperty’svalue.Propertyobserversarecalledeverytimeaproperty’svalueisset,evenifthenewvalueisthesameastheproperty’scurrentvalue.

Youcanaddpropertyobserverstoanystoredpropertiesyoudefine,exceptforlazystoredproperties.Youcanalsoaddpropertyobserverstoanyinheritedproperty(whetherstoredorcomputed)byoverridingthepropertywithinasubclass.Youdon’tneedtodefinepropertyobserversfornonoverriddencomputedproperties,becauseyoucanobserveandrespondtochangestotheirvalueinthecomputedproperty’ssetter.PropertyoverridingisdescribedinOverriding.

Youhavetheoptiontodefineeitherorbothoftheseobserversonaproperty:

willSetiscalledjustbeforethevalueisstored.

didSetiscalledimmediatelyafterthenewvalueisstored.

IfyouimplementawillSetobserver,it’spassedthenewpropertyvalueasaconstantparameter.YoucanspecifyanameforthisparameteraspartofyourwillSetimplementation.Ifyoudon’twritetheparameternameandparentheseswithinyourimplementation,theparameterismadeavailablewithadefaultparameternameofnewValue.

Similarly,ifyouimplementadidSetobserver,it’spassedaconstantparametercontainingtheoldpropertyvalue.YoucannametheparameterorusethedefaultparameternameofoldValue.IfyouassignavaluetoapropertywithinitsowndidSetobserver,thenewvaluethatyouassignreplacestheonethatwasjustset.

NOTE

ThewillSetanddidSetobserversofsuperclasspropertiesarecalledwhenapropertyissetinasubclassinitializer,afterthesuperclassinitializerhasbeencalled.Theyarenotcalledwhileaclassissettingitsownproperties,beforethesuperclassinitializerhasbeencalled.

Formoreinformationaboutinitializerdelegation,seeInitializerDelegationforValueTypesandInitializerDelegationforClassTypes.

Here’sanexampleofwillSetanddidSetinaction.TheexamplebelowdefinesanewclasscalledStepCounter,whichtracksthetotalnumberofstepsthatapersontakeswhilewalking.Thisclassmightbeusedwithinputdatafromapedometerorotherstepcountertokeeptrackofaperson’sexerciseduringtheirdailyroutine.

1 classStepCounter{

2 vartotalSteps:Int=0{

3 willSet(newTotalSteps){

4 print("AbouttosettotalStepsto\

(newTotalSteps)")

5 }

6 didSet{

7 iftotalSteps>oldValue{

8 print("Added\(totalSteps-oldValue)steps")

9 }

10 }

11 }

12 }

13 letstepCounter=StepCounter()

14 stepCounter.totalSteps=200

15 //AbouttosettotalStepsto200

16 //Added200steps

17 stepCounter.totalSteps=360

18 //AbouttosettotalStepsto360

19 //Added160steps

20 stepCounter.totalSteps=896

21 //AbouttosettotalStepsto896

22 //Added536steps

TheStepCounterclassdeclaresatotalStepspropertyoftypeInt.Thisisa

storedpropertywithwillSetanddidSetobservers.

ThewillSetanddidSetobserversfortotalStepsarecalledwheneverthepropertyisassignedanewvalue.Thisistrueevenifthenewvalueisthesameasthecurrentvalue.

Thisexample’swillSetobserverusesacustomparameternameofnewTotalStepsfortheupcomingnewvalue.Inthisexample,itsimplyprintsoutthevaluethatisabouttobeset.

ThedidSetobserveriscalledafterthevalueoftotalStepsisupdated.ItcomparesthenewvalueoftotalStepsagainsttheoldvalue.Ifthetotalnumberofstepshasincreased,amessageisprintedtoindicatehowmanynewstepshavebeentaken.ThedidSetobserverdoesnotprovideacustomparameternamefortheoldvalue,andthedefaultnameofoldValueisusedinstead.

NOTE

Ifyoupassapropertythathasobserverstoafunctionasanin-outparameter,thewillSetanddidSetobserversarealwayscalled.Thisisbecauseofthecopy-incopy-outmemorymodelforin-outparameters:Thevalueisalwayswrittenbacktothepropertyattheendofthefunction.Foradetaileddiscussionofthebehaviorofin-outparameters,seeIn-OutParameters.

PropertyWrappers

Apropertywrapperaddsalayerofseparationbetweencodethatmanageshowapropertyisstoredandthecodethatdefinesaproperty.Forexample,ifyouhavepropertiesthatprovidethread-safetychecksorstoretheirunderlyingdatainadatabase,youhavetowritethatcodeoneveryproperty.Whenyouuseapropertywrapper,youwritethemanagementcodeoncewhenyoudefinethewrapper,andthenreusethatmanagementcodebyapplyingittomultipleproperties.

Todefineapropertywrapper,youmakeastructure,enumeration,orclassthatdefinesawrappedValueproperty.Inthecodebelow,theTwelveOrLessstructureensuresthatthevalueitwrapsalwayscontainsanumberlessthanorequalto12.

Ifyouaskittostorealargernumber,itstores12instead.

1 @propertyWrapper

2 structTwelveOrLess{

3 privatevarnumber=0

4 varwrappedValue:Int{

5 get{returnnumber}

6 set{number=min(newValue,12)}

7 }

8 }

Thesetterensuresthatnewvaluesarelessthan12,andthegetterreturnsthestoredvalue.

NOTE

Thedeclarationfornumberintheexampleabovemarksthevariableasprivate,whichensuresnumberisusedonlyintheimplementationofTwelveOrLess.Codethat’swrittenanywhereelseaccessesthevalueusingthegetterandsetterforwrappedValue,andcan’tusenumberdirectly.Forinformationaboutprivate,seeAccessControl.

Youapplyawrappertoapropertybywritingthewrapper’snamebeforethepropertyasanattribute.Here’sastructurethatstoresasmallrectangle,usingthesame(ratherarbitrary)definitionof“small”that’simplementedbytheTwelveOrLesspropertywrapper:

1 structSmallRectangle{

2 @TwelveOrLessvarheight:Int

3 @TwelveOrLessvarwidth:Int

4 }

5

6 varrectangle=SmallRectangle()

7 print(rectangle.height)

8 //Prints"0"

9

10 rectangle.height=10

11 print(rectangle.height)

12 //Prints"10"

13

14 rectangle.height=24

15 print(rectangle.height)

16 //Prints"12"

TheheightandwidthpropertiesgettheirinitialvaluesfromthedefinitionofTwelveOrLess,whichsetsTwelveOrLess.numbertozero.Storingthenumber10intorectangle.heightsucceedsbecauseit’sasmallnumber.Tryingtostore24actuallystoresavalueof12instead,because24istoolargeforthepropertysetter’srule.

Whenyouapplyawrappertoaproperty,thecompilersynthesizescodethatprovidesstorageforthewrapperandcodethatprovidesaccesstothepropertythroughthewrapper.(Thepropertywrapperisresponsibleforstoringthewrappedvalue,sothere’snosynthesizedcodeforthat.)Youcouldwritecodethatusesthebehaviorofapropertywrapper,withouttakingadvantageofthespecialattributesyntax.Forexample,here’saversionofSmallRectanglefromthepreviouscodelistingthatwrapsitspropertiesintheTwelveOrLessstructureexplicitly,insteadofwriting@TwelveOrLessasanattribute:

1 structSmallRectangle{

2 privatevar_height=TwelveOrLess()

3 privatevar_width=TwelveOrLess()

4 varheight:Int{

5 get{return_height.wrappedValue}

6 set{_height.wrappedValue=newValue}

7 }

8 varwidth:Int{

9 get{return_width.wrappedValue}

10 set{_width.wrappedValue=newValue}

11 }

12 }

The_heightand_widthpropertiesstoreaninstanceofthepropertywrapper,TwelveOrLess.ThegetterandsetterforheightandwidthwrapaccesstothewrappedValueproperty.

SettingInitialValuesforWrappedPropertiesThecodeintheexamplesabovesetstheinitialvalueforthewrappedpropertybygivingnumberaninitialvalueinthedefinitionofTwelveOrLess.Codethatusesthispropertywrapper,can’tspecifyadifferentinitialvalueforapropertythat’swrappedbyTwelveOrLess—forexample,thedefinitionofSmallRectanglecan’tgiveheightorwidthinitialvalues.Tosupportsettinganinitialvalueorothercustomization,thepropertywrapperneedstoaddaninitializer.Here’sanexpandedversionofTwelveOrLesscalledSmallNumberthatdefinesinitializersthatsetthewrappedandmaximumvalue:

1 @propertyWrapper

2 structSmallNumber{

3 privatevarmaximum:Int

4 privatevarnumber:Int

5

6 varwrappedValue:Int{

7 get{returnnumber}

8 set{number=min(newValue,maximum)}

9 }

10

11 init(){

12 maximum=12

13 number=0

14 }

15 init(wrappedValue:Int){

16 maximum=12

17 number=min(wrappedValue,maximum)

18 }

19 init(wrappedValue:Int,maximum:Int){

20 self.maximum=maximum

21 number=min(wrappedValue,maximum)

22 }

23 }

ThedefinitionofSmallNumberincludesthreeinitializers—init(),init(wrappedValue:),andinit(wrappedValue:maximum:)—whichtheexamplesbelowusetosetthewrappedvalueandthemaximumvalue.Forinformationaboutinitializationandinitializersyntax,seeInitialization.

Whenyouapplyawrappertoapropertyandyoudon’tspecifyaninitialvalue,Swiftusestheinit()initializertosetupthewrapper.Forexample:

1 structZeroRectangle{

2 @SmallNumbervarheight:Int

3 @SmallNumbervarwidth:Int

4 }

5

6 varzeroRectangle=ZeroRectangle()

7 print(zeroRectangle.height,zeroRectangle.width)

8 //Prints"00"

TheinstancesofSmallNumberthatwrapheightandwidtharecreatedbycallingSmallNumber().Thecodeinsidethatinitializersetstheinitialwrappedvalueandtheinitialmaximumvalue,usingthedefaultvaluesofzeroand12.Thepropertywrapperstillprovidesalloftheinitialvalues,liketheearlierexamplethatusedTwelveOrLessinSmallRectangle.Unlikethatexample,SmallNumberalso

supportswritingthoseinitialvaluesaspartofdeclaringtheproperty.

Whenyouspecifyaninitialvaluefortheproperty,Swiftusestheinit(wrappedValue:)initializertosetupthewrapper.Forexample:

1 structUnitRectangle{

2 @SmallNumbervarheight:Int=1

3 @SmallNumbervarwidth:Int=1

4 }

5

6 varunitRectangle=UnitRectangle()

7 print(unitRectangle.height,unitRectangle.width)

8 //Prints"11"

Whenyouwrite=1onapropertywithawrapper,that’stranslatedintoacalltotheinit(wrappedValue:)initializer.TheinstancesofSmallNumberthatwrapheightandwidtharecreatedbycallingSmallNumber(wrappedValue:1).Theinitializerusesthewrappedvaluethat’sspecifiedhere,anditusesthedefaultmaximumvalueof12.

Whenyouwriteargumentsinparenthesesafterthecustomattribute,Swiftusestheinitializerthatacceptsthoseargumentstosetupthewrapper.Forexample,ifyouprovideaninitialvalueandamaximumvalue,Swiftusestheinit(wrappedValue:maximum:)initializer:

1 structNarrowRectangle{

2 @SmallNumber(wrappedValue:2,maximum:5)varheight:Int

3 @SmallNumber(wrappedValue:3,maximum:4)varwidth:Int

4 }

5

6 varnarrowRectangle=NarrowRectangle()

7 print(narrowRectangle.height,narrowRectangle.width)

8 //Prints"23"

9

10 narrowRectangle.height=100

11 narrowRectangle.width=100

12 print(narrowRectangle.height,narrowRectangle.width)

13 //Prints"54"

TheinstanceofSmallNumberthatwrapsheightiscreatedbycallingSmallNumber(wrappedValue:2,maximum:5),andtheinstancethatwrapswidthiscreatedbycallingSmallNumber(wrappedValue:3,maximum:4).

Byincludingargumentstothepropertywrapper,youcansetuptheinitialstateinthewrapperorpassotheroptionstothewrapperwhenit’screated.Thissyntaxisthemostgeneralwaytouseapropertywrapper.Youcanprovidewhateverargumentsyouneedtotheattribute,andthey’repassedtotheinitializer.

Whenyouincludepropertywrapperarguments,youcanalsospecifyaninitialvalueusingassignment.SwifttreatstheassignmentlikeawrappedValueargumentandusestheinitializerthatacceptstheargumentsyouinclude.Forexample:

1 structMixedRectangle{

2 @SmallNumbervarheight:Int=1

3 @SmallNumber(maximum:9)varwidth:Int=2

4 }

5

6 varmixedRectangle=MixedRectangle()

7 print(mixedRectangle.height)

8 //Prints"1"

9

10 mixedRectangle.height=20

11 print(mixedRectangle.height)

12 //Prints"12"

TheinstanceofSmallNumberthatwrapsheightiscreatedbycallingSmallNumber(wrappedValue:1),whichusesthedefaultmaximumvalueof12.TheinstancethatwrapswidthiscreatedbycallingSmallNumber(wrappedValue:2,maximum:9).

ProjectingaValueFromaPropertyWrapperInadditiontothewrappedvalue,apropertywrappercanexposeadditionalfunctionalitybydefiningaprojectedvalue—forexample,apropertywrapperthatmanagesaccesstoadatabasecanexposeaflushDatabaseConnection()methodonitsprojectedvalue.Thenameoftheprojectedvalueisthesameasthewrappedvalue,exceptitbeginswithadollarsign($).Becauseyourcodecan’tdefinepropertiesthatstartwith$theprojectedvalueneverinterfereswithpropertiesyoudefine.

IntheSmallNumberexampleabove,ifyoutrytosetthepropertytoanumberthat’stoolarge,thepropertywrapperadjuststhenumberbeforestoringit.ThecodebelowaddsaprojectedValuepropertytotheSmallNumberstructuretokeeptrackofwhetherthepropertywrapperadjustedthenewvalueforthepropertybeforestoringthatnewvalue.

1 @propertyWrapper

2 structSmallNumber{

3 privatevarnumber=0

4 varprojectedValue=false

5 varwrappedValue:Int{

6 get{returnnumber}

7 set{

8 ifnewValue>12{

9 number=12

10 projectedValue=true

11 }else{

12 number=newValue

13 projectedValue=false

14 }

15 }

16 }

17 }

18 structSomeStructure{

19 @SmallNumbervarsomeNumber:Int

20 }

21 varsomeStructure=SomeStructure()

22

23 someStructure.someNumber=4

24 print(someStructure.$someNumber)

25 //Prints"false"

26

27 someStructure.someNumber=55

28 print(someStructure.$someNumber)

29 //Prints"true"

Writings.$someNumberaccessesthewrapper’sprojectedvalue.Afterstoringasmallnumberlikefour,thevalueofs.$someNumberisfalse.However,theprojectedvalueistrueaftertryingtostoreanumberthat’stoolarge,like55.

Apropertywrappercanreturnavalueofanytypeasitsprojectedvalue.Inthisexample,thepropertywrapperexposesonlyonepieceofinformation—whetherthenumberwasadjusted—soitexposesthatBooleanvalueasitsprojectedvalue.Awrapperthatneedstoexposemoreinformationcanreturnaninstanceofsomeotherdatatype,oritcanreturnselftoexposetheinstanceofthewrapperasitsprojectedvalue.

Whenyouaccessaprojectedvaluefromcodethat’spartofthetype,likeapropertygetteroraninstancemethod,youcanomitself.beforethepropertyname,justlikeaccessingotherproperties.Thecodeinthefollowingexamplereferstotheprojectedvalueofthewrapperaroundheightandwidthas$height

and$width:

1 enumSize{

2 casesmall,large

3 }

4

5 structSizedRectangle{

6 @SmallNumbervarheight:Int

7 @SmallNumbervarwidth:Int

8

9 mutatingfuncresize(tosize:Size)->Bool{

10 switchsize{

11 case.small:

12 height=10

13 width=20

14 case.large:

15 height=100

16 width=100

17 }

18 return$height||$width

19 }

20 }

Becausepropertywrappersyntaxisjustsyntacticsugarforapropertywithagetterandasetter,accessingheightandwidthbehavesthesameasaccessinganyotherproperty.Forexample,thecodeinresize(to:)accessesheightandwidthusingtheirpropertywrapper.Ifyoucallresize(to:.large),theswitchcasefor.largesetstherectangle’sheightandwidthto100.Thewrapperpreventsthevalueofthosepropertiesfrombeinglargerthan12,anditsetstheprojectedvaluetotrue,torecordthefactthatitadjustedtheirvalues.Attheendofresize(to:),thereturnstatementchecks$heightand$widthtodeterminewhetherthepropertywrapperadjustedeitherheightorwidth.

GlobalandLocalVariables

Thecapabilitiesdescribedaboveforcomputingandobservingpropertiesarealsoavailabletoglobalvariablesandlocalvariables.Globalvariablesarevariablesthataredefinedoutsideofanyfunction,method,closure,ortypecontext.Localvariablesarevariablesthataredefinedwithinafunction,method,orclosurecontext.

Theglobalandlocalvariablesyouhaveencounteredinpreviouschaptershaveallbeenstoredvariables.Storedvariables,likestoredproperties,providestorageforavalueofacertaintypeandallowthatvaluetobesetandretrieved.

However,youcanalsodefinecomputedvariablesanddefineobserversforstoredvariables,ineitheraglobalorlocalscope.Computedvariablescalculatetheirvalue,ratherthanstoringit,andtheyarewritteninthesamewayascomputedproperties.

NOTE

Globalconstantsandvariablesarealwayscomputedlazily,inasimilarmannertoLazyStoredProperties.Unlikelazystoredproperties,globalconstantsandvariablesdonotneedtobemarkedwiththelazymodifier.

Localconstantsandvariablesarenevercomputedlazily.

TypeProperties

Instancepropertiesarepropertiesthatbelongtoaninstanceofaparticulartype.Everytimeyoucreateanewinstanceofthattype,ithasitsownsetofpropertyvalues,separatefromanyotherinstance.

Youcanalsodefinepropertiesthatbelongtothetypeitself,nottoanyoneinstanceofthattype.Therewillonlyeverbeonecopyoftheseproperties,nomatterhowmanyinstancesofthattypeyoucreate.Thesekindsofpropertiesarecalledtypeproperties.

Typepropertiesareusefulfordefiningvaluesthatareuniversaltoallinstances

ofaparticulartype,suchasaconstantpropertythatallinstancescanuse(likeastaticconstantinC),oravariablepropertythatstoresavaluethatisglobaltoallinstancesofthattype(likeastaticvariableinC).

Storedtypepropertiescanbevariablesorconstants.Computedtypepropertiesarealwaysdeclaredasvariableproperties,inthesamewayascomputedinstanceproperties.

NOTE

Unlikestoredinstanceproperties,youmustalwaysgivestoredtypepropertiesadefaultvalue.Thisisbecausethetypeitselfdoesnothaveaninitializerthatcanassignavaluetoastoredtypepropertyatinitializationtime.

Storedtypepropertiesarelazilyinitializedontheirfirstaccess.Theyareguaranteedtobeinitializedonlyonce,evenwhenaccessedbymultiplethreadssimultaneously,andtheydonotneedtobemarkedwiththelazymodifier.

TypePropertySyntaxInCandObjective-C,youdefinestaticconstantsandvariablesassociatedwithatypeasglobalstaticvariables.InSwift,however,typepropertiesarewrittenaspartofthetype’sdefinition,withinthetype’soutercurlybraces,andeachtypepropertyisexplicitlyscopedtothetypeitsupports.

Youdefinetypepropertieswiththestatickeyword.Forcomputedtypepropertiesforclasstypes,youcanusetheclasskeywordinsteadtoallowsubclassestooverridethesuperclass’simplementation.Theexamplebelowshowsthesyntaxforstoredandcomputedtypeproperties:

1 structSomeStructure{

2 staticvarstoredTypeProperty="Somevalue."

3 staticvarcomputedTypeProperty:Int{

4 return1

5 }

6 }

7 enumSomeEnumeration{

8 staticvarstoredTypeProperty="Somevalue."

9 staticvarcomputedTypeProperty:Int{

10 return6

11 }

12 }

13 classSomeClass{

14 staticvarstoredTypeProperty="Somevalue."

15 staticvarcomputedTypeProperty:Int{

16 return27

17 }

18 classvaroverrideableComputedTypeProperty:Int{

19 return107

20 }

21 }

NOTE

Thecomputedtypepropertyexamplesaboveareforread-onlycomputedtypeproperties,butyoucanalsodefineread-writecomputedtypepropertieswiththesamesyntaxasforcomputedinstanceproperties.

QueryingandSettingTypePropertiesTypepropertiesarequeriedandsetwithdotsyntax,justlikeinstanceproperties.However,typepropertiesarequeriedandsetonthetype,notonaninstanceofthattype.Forexample:

1 print(SomeStructure.storedTypeProperty)

2 //Prints"Somevalue."

3 SomeStructure.storedTypeProperty="Anothervalue."

4 print(SomeStructure.storedTypeProperty)

5 //Prints"Anothervalue."

6 print(SomeEnumeration.computedTypeProperty)

7 //Prints"6"

8 print(SomeClass.computedTypeProperty)

9 //Prints"27"

Theexamplesthatfollowusetwostoredtypepropertiesaspartofastructurethatmodelsanaudiolevelmeterforanumberofaudiochannels.Eachchannelhasanintegeraudiolevelbetween0and10inclusive.

Thefigurebelowillustrateshowtwooftheseaudiochannelscanbecombinedtomodelastereoaudiolevelmeter.Whenachannel’saudiolevelis0,noneofthelightsforthatchannelarelit.Whentheaudiolevelis10,allofthelightsforthatchannelarelit.Inthisfigure,theleftchannelhasacurrentlevelof9,andtherightchannelhasacurrentlevelof7:

TheaudiochannelsdescribedabovearerepresentedbyinstancesoftheAudioChannelstructure:

1 structAudioChannel{

2 staticletthresholdLevel=10

3 staticvarmaxInputLevelForAllChannels=0

4 varcurrentLevel:Int=0{

5 didSet{

6 ifcurrentLevel>AudioChannel.thresholdLevel{

7 //capthenewaudioleveltothethreshold

level

8 currentLevel=AudioChannel.thresholdLevel

9 }

10 ifcurrentLevel>

AudioChannel.maxInputLevelForAllChannels{

11 //storethisasthenewoverallmaximuminput

level

12 AudioChannel.maxInputLevelForAllChannels=

currentLevel

13 }

14 }

15 }

16 }

TheAudioChannelstructuredefinestwostoredtypepropertiestosupportitsfunctionality.Thefirst,thresholdLevel,definesthemaximumthresholdvalueanaudiolevelcantake.Thisisaconstantvalueof10forallAudioChannelinstances.Ifanaudiosignalcomesinwithahighervaluethan10,itwillbecappedtothisthresholdvalue(asdescribedbelow).

ThesecondtypepropertyisavariablestoredpropertycalledmaxInputLevelForAllChannels.ThiskeepstrackofthemaximuminputvaluethathasbeenreceivedbyanyAudioChannelinstance.Itstartswithaninitialvalueof0.

TheAudioChannelstructurealsodefinesastoredinstancepropertycalledcurrentLevel,whichrepresentsthechannel’scurrentaudiolevelonascaleof0to10.

ThecurrentLevelpropertyhasadidSetpropertyobservertocheckthevalueofcurrentLevelwheneveritisset.Thisobserverperformstwochecks:

IfthenewvalueofcurrentLevelisgreaterthantheallowedthresholdLevel,thepropertyobservercapscurrentLeveltothresholdLevel.

IfthenewvalueofcurrentLevel(afteranycapping)ishigherthananyvaluepreviouslyreceivedbyanyAudioChannelinstance,thepropertyobserverstoresthenewcurrentLevelvalueinthemaxInputLevelForAllChannelstypeproperty.

NOTE

Inthefirstofthesetwochecks,thedidSetobserversetscurrentLeveltoadifferentvalue.Thisdoesnot,however,causetheobservertobecalledagain.

YoucanusetheAudioChannelstructuretocreatetwonewaudiochannelscalledleftChannelandrightChannel,torepresenttheaudiolevelsofastereosoundsystem:

1 varleftChannel=AudioChannel()

2 varrightChannel=AudioChannel()

IfyousetthecurrentLeveloftheleftchannelto7,youcanseethatthemaxInputLevelForAllChannelstypepropertyisupdatedtoequal7:

1 leftChannel.currentLevel=7

2 print(leftChannel.currentLevel)

3 //Prints"7"

4 print(AudioChannel.maxInputLevelForAllChannels)

5 //Prints"7"

IfyoutrytosetthecurrentLeveloftherightchannelto11,youcanseethattherightchannel’scurrentLevelpropertyiscappedtothemaximumvalueof10,andthemaxInputLevelForAllChannelstypepropertyisupdatedtoequal10:

1 rightChannel.currentLevel=11

2 print(rightChannel.currentLevel)

3 //Prints"10"

4 print(AudioChannel.maxInputLevelForAllChannels)

5 //Prints"10"

Methods

Methodsarefunctionsthatareassociatedwithaparticulartype.Classes,structures,andenumerationscanalldefineinstancemethods,whichencapsulatespecifictasksandfunctionalityforworkingwithaninstanceofagiventype.Classes,structures,andenumerationscanalsodefinetypemethods,whichareassociatedwiththetypeitself.TypemethodsaresimilartoclassmethodsinObjective-C.

ThefactthatstructuresandenumerationscandefinemethodsinSwiftisamajordifferencefromCandObjective-C.InObjective-C,classesaretheonlytypesthatcandefinemethods.InSwift,youcanchoosewhethertodefineaclass,structure,orenumeration,andstillhavetheflexibilitytodefinemethodsonthetypeyoucreate.

InstanceMethods

Instancemethodsarefunctionsthatbelongtoinstancesofaparticularclass,structure,orenumeration.Theysupportthefunctionalityofthoseinstances,eitherbyprovidingwaystoaccessandmodifyinstanceproperties,orbyprovidingfunctionalityrelatedtotheinstance’spurpose.Instancemethodshaveexactlythesamesyntaxasfunctions,asdescribedinFunctions.

Youwriteaninstancemethodwithintheopeningandclosingbracesofthetypeitbelongsto.Aninstancemethodhasimplicitaccesstoallotherinstancemethodsandpropertiesofthattype.Aninstancemethodcanbecalledonlyonaspecificinstanceofthetypeitbelongsto.Itcannotbecalledinisolationwithoutanexistinginstance.

Here’sanexamplethatdefinesasimpleCounterclass,whichcanbeusedtocountthenumberoftimesanactionoccurs:

1 classCounter{

2 varcount=0

3 funcincrement(){

4 count+=1

5 }

6 funcincrement(byamount:Int){

7 count+=amount

8 }

9 funcreset(){

10 count=0

11 }

12 }

TheCounterclassdefinesthreeinstancemethods:

increment()incrementsthecounterby1.

increment(by:Int)incrementsthecounterbyaspecifiedintegeramount.

reset()resetsthecountertozero.

TheCounterclassalsodeclaresavariableproperty,count,tokeeptrackofthecurrentcountervalue.

Youcallinstancemethodswiththesamedotsyntaxasproperties:

1 letcounter=Counter()

2 //theinitialcountervalueis0

3 counter.increment()

4 //thecounter'svalueisnow1

5 counter.increment(by:5)

6 //thecounter'svalueisnow6

7 counter.reset()

8 //thecounter'svalueisnow0

Functionparameterscanhavebothaname(forusewithinthefunction’sbody)andanargumentlabel(forusewhencallingthefunction),asdescribedinFunctionArgumentLabelsandParameterNames.Thesameistrueformethodparameters,becausemethodsarejustfunctionsthatareassociatedwithatype.

TheselfPropertyEveryinstanceofatypehasanimplicitpropertycalledself,whichisexactlyequivalenttotheinstanceitself.Youusetheselfpropertytorefertothecurrentinstancewithinitsowninstancemethods.

Theincrement()methodintheexampleabovecouldhavebeenwrittenlikethis:

1 funcincrement(){

2 self.count+=1

3 }

Inpractice,youdon’tneedtowriteselfinyourcodeveryoften.Ifyoudon’texplicitlywriteself,Swiftassumesthatyouarereferringtoapropertyormethodofthecurrentinstancewheneveryouuseaknownpropertyormethodnamewithinamethod.Thisassumptionisdemonstratedbytheuseofcount(ratherthanself.count)insidethethreeinstancemethodsforCounter.

Themainexceptiontothisruleoccurswhenaparameternameforaninstancemethodhasthesamenameasapropertyofthatinstance.Inthissituation,theparameternametakesprecedence,anditbecomesnecessarytorefertothepropertyinamorequalifiedway.Youusetheselfpropertytodistinguishbetweentheparameternameandthepropertyname.

Here,selfdisambiguatesbetweenamethodparametercalledxandaninstancepropertythatisalsocalledx:

1 structPoint{

2 varx=0.0,y=0.0

3 funcisToTheRightOf(x:Double)->Bool{

4 returnself.x>x

5 }

6 }

7 letsomePoint=Point(x:4.0,y:5.0)

8 ifsomePoint.isToTheRightOf(x:1.0){

9 print("Thispointistotherightofthelinewherex==

1.0")

10 }

11 //Prints"Thispointistotherightofthelinewherex==

1.0"

Withouttheselfprefix,Swiftwouldassumethatbothusesofxreferredtothemethodparametercalledx.

ModifyingValueTypesfromWithinInstanceMethodsStructuresandenumerationsarevaluetypes.Bydefault,thepropertiesofavaluetypecannotbemodifiedfromwithinitsinstancemethods.

However,ifyouneedtomodifythepropertiesofyourstructureorenumerationwithinaparticularmethod,youcanoptintomutatingbehaviorforthatmethod.Themethodcanthenmutate(thatis,change)itspropertiesfromwithinthemethod,andanychangesthatitmakesarewrittenbacktotheoriginalstructurewhenthemethodends.Themethodcanalsoassignacompletelynewinstancetoitsimplicitselfproperty,andthisnewinstancewillreplacetheexistingonewhenthemethodends.

Youcanoptintothisbehaviorbyplacingthemutatingkeywordbeforethefunckeywordforthatmethod:

1 structPoint{

2 varx=0.0,y=0.0

3 mutatingfuncmoveBy(xdeltaX:Double,ydeltaY:Double){

4 x+=deltaX

5 y+=deltaY

6 }

7 }

8 varsomePoint=Point(x:1.0,y:1.0)

9 somePoint.moveBy(x:2.0,y:3.0)

10 print("Thepointisnowat(\(somePoint.x),\(somePoint.y))")

11 //Prints"Thepointisnowat(3.0,4.0)"

ThePointstructureabovedefinesamutatingmoveBy(x:y:)method,whichmovesaPointinstancebyacertainamount.Insteadofreturninganewpoint,thismethodactuallymodifiesthepointonwhichitiscalled.Themutatingkeywordisaddedtoitsdefinitiontoenableittomodifyitsproperties.

Notethatyoucannotcallamutatingmethodonaconstantofstructuretype,becauseitspropertiescannotbechanged,eveniftheyarevariableproperties,asdescribedinStoredPropertiesofConstantStructureInstances:

1 letfixedPoint=Point(x:3.0,y:3.0)

2 fixedPoint.moveBy(x:2.0,y:3.0)

3 //thiswillreportanerror

AssigningtoselfWithinaMutatingMethodMutatingmethodscanassignanentirelynewinstancetotheimplicitselfproperty.ThePointexampleshownabovecouldhavebeenwritteninthefollowingwayinstead:

1 structPoint{

2 varx=0.0,y=0.0

3 mutatingfuncmoveBy(xdeltaX:Double,ydeltaY:Double){

4 self=Point(x:x+deltaX,y:y+deltaY)

5 }

6 }

ThisversionofthemutatingmoveBy(x:y:)methodcreatesanewstructurewhosexandyvaluesaresettothetargetlocation.Theendresultofcallingthisalternativeversionofthemethodwillbeexactlythesameasforcallingtheearlierversion.

Mutatingmethodsforenumerationscansettheimplicitselfparametertobeadifferentcasefromthesameenumeration:

1 enumTriStateSwitch{

2 caseoff,low,high

3 mutatingfuncnext(){

4 switchself{

5 case.off:

6 self=.low

7 case.low:

8 self=.high

9 case.high:

10 self=.off

11 }

12 }

13 }

14 varovenLight=TriStateSwitch.low

15 ovenLight.next()

16 //ovenLightisnowequalto.high

17 ovenLight.next()

18 //ovenLightisnowequalto.off

Thisexampledefinesanenumerationforathree-stateswitch.Theswitchcyclesbetweenthreedifferentpowerstates(off,lowandhigh)everytimeitsnext()

methodiscalled.

TypeMethods

Instancemethods,asdescribedabove,aremethodsthatyoucallonaninstanceofaparticulartype.Youcanalsodefinemethodsthatarecalledonthetypeitself.Thesekindsofmethodsarecalledtypemethods.Youindicatetypemethodsbywritingthestatickeywordbeforethemethod’sfunckeyword.Classescanusetheclasskeywordinstead,toallowsubclassestooverridethesuperclass’simplementationofthatmethod.

NOTE

InObjective-C,youcandefinetype-levelmethodsonlyforObjective-Cclasses.InSwift,youcandefinetype-levelmethodsforallclasses,structures,andenumerations.Eachtypemethodisexplicitlyscopedtothetypeitsupports.

Typemethodsarecalledwithdotsyntax,likeinstancemethods.However,youcalltypemethodsonthetype,notonaninstanceofthattype.Here’showyoucallatypemethodonaclasscalledSomeClass:

1 classSomeClass{

2 classfuncsomeTypeMethod(){

3 //typemethodimplementationgoeshere

4 }

5 }

6 SomeClass.someTypeMethod()

Withinthebodyofatypemethod,theimplicitselfpropertyreferstothetypeitself,ratherthananinstanceofthattype.Thismeansthatyoucanuseselftodisambiguatebetweentypepropertiesandtypemethodparameters,justasyoudoforinstancepropertiesandinstancemethodparameters.

Moregenerally,anyunqualifiedmethodandpropertynamesthatyouusewithin

thebodyofatypemethodwillrefertoothertype-levelmethodsandproperties.Atypemethodcancallanothertypemethodwiththeothermethod’sname,withoutneedingtoprefixitwiththetypename.Similarly,typemethodsonstructuresandenumerationscanaccesstypepropertiesbyusingthetypeproperty’snamewithoutatypenameprefix.

TheexamplebelowdefinesastructurecalledLevelTracker,whichtracksaplayer’sprogressthroughthedifferentlevelsorstagesofagame.Itisasingle-playergame,butcanstoreinformationformultipleplayersonasingledevice.

Allofthegame’slevels(apartfromlevelone)arelockedwhenthegameisfirstplayed.Everytimeaplayerfinishesalevel,thatlevelisunlockedforallplayersonthedevice.TheLevelTrackerstructureusestypepropertiesandmethodstokeeptrackofwhichlevelsofthegamehavebeenunlocked.Italsotracksthecurrentlevelforanindividualplayer.

1 structLevelTracker{

2 staticvarhighestUnlockedLevel=1

3 varcurrentLevel=1

4

5 staticfuncunlock(_level:Int){

6 iflevel>highestUnlockedLevel{highestUnlockedLevel

=level}

7 }

8

9 staticfuncisUnlocked(_level:Int)->Bool{

10 returnlevel<=highestUnlockedLevel

11 }

12

13 @discardableResult

14 mutatingfuncadvance(tolevel:Int)->Bool{

15 ifLevelTracker.isUnlocked(level){

16 currentLevel=level

17 returntrue

18 }else{

19 returnfalse

20 }

21 }

22 }

TheLevelTrackerstructurekeepstrackofthehighestlevelthatanyplayerhasunlocked.ThisvalueisstoredinatypepropertycalledhighestUnlockedLevel.

LevelTrackeralsodefinestwotypefunctionstoworkwiththehighestUnlockedLevelproperty.Thefirstisatypefunctioncalledunlock(_:),whichupdatesthevalueofhighestUnlockedLevelwheneveranewlevelisunlocked.ThesecondisaconveniencetypefunctioncalledisUnlocked(_:),whichreturnstrueifaparticularlevelnumberisalreadyunlocked.(NotethatthesetypemethodscanaccessthehighestUnlockedLeveltypepropertywithoutyourneedingtowriteitasLevelTracker.highestUnlockedLevel.)

Inadditiontoitstypepropertyandtypemethods,LevelTrackertracksanindividualplayer’sprogressthroughthegame.ItusesaninstancepropertycalledcurrentLeveltotrackthelevelthataplayeriscurrentlyplaying.

TohelpmanagethecurrentLevelproperty,LevelTrackerdefinesaninstancemethodcalledadvance(to:).BeforeupdatingcurrentLevel,thismethodcheckswhethertherequestednewlevelisalreadyunlocked.Theadvance(to:)methodreturnsaBooleanvaluetoindicatewhetherornotitwasactuallyabletosetcurrentLevel.Becauseit’snotnecessarilyamistakeforcodethatcallstheadvance(to:)methodtoignorethereturnvalue,thisfunctionismarkedwiththe@discardableResultattribute.Formoreinformationaboutthisattribute,seeAttributes.

TheLevelTrackerstructureisusedwiththePlayerclass,shownbelow,totrackandupdatetheprogressofanindividualplayer:

1 classPlayer{

2 vartracker=LevelTracker()

3 letplayerName:String

4 funccomplete(level:Int){

5 LevelTracker.unlock(level+1)

6 tracker.advance(to:level+1)

7 }

8 init(name:String){

9 playerName=name

10 }

11 }

ThePlayerclasscreatesanewinstanceofLevelTrackertotrackthatplayer’sprogress.Italsoprovidesamethodcalledcomplete(level:),whichiscalledwheneveraplayercompletesaparticularlevel.Thismethodunlocksthenextlevelforallplayersandupdatestheplayer’sprogresstomovethemtothenextlevel.(TheBooleanreturnvalueofadvance(to:)isignored,becausethelevelisknowntohavebeenunlockedbythecalltoLevelTracker.unlock(_:)onthepreviousline.)

YoucancreateaninstanceofthePlayerclassforanewplayer,andseewhathappenswhentheplayercompleteslevelone:

1 varplayer=Player(name:"Argyrios")

2 player.complete(level:1)

3 print("highestunlockedlevelisnow\

(LevelTracker.highestUnlockedLevel)")

4 //Prints"highestunlockedlevelisnow2"

Ifyoucreateasecondplayer,whomyoutrytomovetoalevelthatisnotyetunlockedbyanyplayerinthegame,theattempttosettheplayer’scurrentlevelfails:

1 player=Player(name:"Beto")

2 ifplayer.tracker.advance(to:6){

3 print("playerisnowonlevel6")

4 }else{

5 print("level6hasnotyetbeenunlocked")

6 }

7 //Prints"level6hasnotyetbeenunlocked"

Subscripts

Classes,structures,andenumerationscandefinesubscripts,whichareshortcutsforaccessingthememberelementsofacollection,list,orsequence.Youusesubscriptstosetandretrievevaluesbyindexwithoutneedingseparatemethodsforsettingandretrieval.Forexample,youaccesselementsinanArrayinstanceassomeArray[index]andelementsinaDictionaryinstanceassomeDictionary[key].

Youcandefinemultiplesubscriptsforasingletype,andtheappropriatesubscriptoverloadtouseisselectedbasedonthetypeofindexvalueyoupasstothesubscript.Subscriptsarenotlimitedtoasingledimension,andyoucandefinesubscriptswithmultipleinputparameterstosuityourcustomtype’sneeds.

SubscriptSyntax

Subscriptsenableyoutoqueryinstancesofatypebywritingoneormorevaluesinsquarebracketsaftertheinstancename.Theirsyntaxissimilartobothinstancemethodsyntaxandcomputedpropertysyntax.Youwritesubscriptdefinitionswiththesubscriptkeyword,andspecifyoneormoreinputparametersandareturntype,inthesamewayasinstancemethods.Unlikeinstancemethods,subscriptscanberead-writeorread-only.Thisbehavioriscommunicatedbyagetterandsetterinthesamewayasforcomputedproperties:

1 subscript(index:Int)->Int{

2 get{

3 //Returnanappropriatesubscriptvaluehere.

4 }

5 set(newValue){

6 //Performasuitablesettingactionhere.

7 }

8 }

ThetypeofnewValueisthesameasthereturnvalueofthesubscript.Aswithcomputedproperties,youcanchoosenottospecifythesetter’s(newValue)parameter.AdefaultparametercallednewValueisprovidedtoyoursetterifyoudonotprovideoneyourself.

Aswithread-onlycomputedproperties,youcansimplifythedeclarationofaread-onlysubscriptbyremovingthegetkeywordanditsbraces:

1 subscript(index:Int)->Int{

2 //Returnanappropriatesubscriptvaluehere.

3 }

Here’sanexampleofaread-onlysubscriptimplementation,whichdefinesaTimesTablestructuretorepresentann-times-tableofintegers:

1 structTimesTable{

2 letmultiplier:Int

3 subscript(index:Int)->Int{

4 returnmultiplier*index

5 }

6 }

7 letthreeTimesTable=TimesTable(multiplier:3)

8 print("sixtimesthreeis\(threeTimesTable[6])")

9 //Prints"sixtimesthreeis18"

Inthisexample,anewinstanceofTimesTableiscreatedtorepresentthethree-times-table.Thisisindicatedbypassingavalueof3tothestructure’sinitializerasthevaluetousefortheinstance’smultiplierparameter.

YoucanquerythethreeTimesTableinstancebycallingitssubscript,asshowninthecalltothreeTimesTable[6].Thisrequeststhesixthentryinthethree-times-

table,whichreturnsavalueof18,or3times6.

NOTE

Ann-times-tableisbasedonafixedmathematicalrule.ItisnotappropriatetosetthreeTimesTable[someIndex]toanewvalue,andsothesubscriptforTimesTableisdefinedasaread-onlysubscript.

SubscriptUsage

Theexactmeaningof“subscript”dependsonthecontextinwhichitisused.Subscriptsaretypicallyusedasashortcutforaccessingthememberelementsinacollection,list,orsequence.Youarefreetoimplementsubscriptsinthemostappropriatewayforyourparticularclassorstructure’sfunctionality.

Forexample,Swift’sDictionarytypeimplementsasubscripttosetandretrievethevaluesstoredinaDictionaryinstance.Youcansetavalueinadictionarybyprovidingakeyofthedictionary’skeytypewithinsubscriptbrackets,andassigningavalueofthedictionary’svaluetypetothesubscript:

1 varnumberOfLegs=["spider":8,"ant":6,"cat":4]

2 numberOfLegs["bird"]=2

TheexampleabovedefinesavariablecallednumberOfLegsandinitializesitwithadictionaryliteralcontainingthreekey-valuepairs.ThetypeofthenumberOfLegsdictionaryisinferredtobe[String:Int].Aftercreatingthedictionary,thisexampleusessubscriptassignmenttoaddaStringkeyof"bird"andanIntvalueof2tothedictionary.

FormoreinformationaboutDictionarysubscripting,seeAccessingandModifyingaDictionary.

NOTE

Swift’sDictionarytypeimplementsitskey-valuesubscriptingasasubscriptthattakesandreturnsanoptionaltype.ForthenumberOfLegsdictionaryabove,thekey-valuesubscripttakesandreturnsavalueoftypeInt?,or“optionalint”.TheDictionarytypeusesanoptional

subscripttypetomodelthefactthatnoteverykeywillhaveavalue,andtogiveawaytodeleteavalueforakeybyassigninganilvalueforthatkey.

SubscriptOptions

Subscriptscantakeanynumberofinputparameters,andtheseinputparameterscanbeofanytype.Subscriptscanalsoreturnanytype.Subscriptscanusevariadicparameters,buttheycan’tusein-outparametersorprovidedefaultparametervalues.

Aclassorstructurecanprovideasmanysubscriptimplementationsasitneeds,andtheappropriatesubscripttobeusedwillbeinferredbasedonthetypesofthevalueorvaluesthatarecontainedwithinthesubscriptbracketsatthepointthatthesubscriptisused.Thisdefinitionofmultiplesubscriptsisknownassubscriptoverloading.

Whileitismostcommonforasubscripttotakeasingleparameter,youcanalsodefineasubscriptwithmultipleparametersifitisappropriateforyourtype.ThefollowingexampledefinesaMatrixstructure,whichrepresentsatwo-dimensionalmatrixofDoublevalues.TheMatrixstructure’ssubscripttakestwointegerparameters:

1 structMatrix{

2 letrows:Int,columns:Int

3 vargrid:[Double]

4 init(rows:Int,columns:Int){

5 self.rows=rows

6 self.columns=columns

7 grid=Array(repeating:0.0,count:rows*columns)

8 }

9 funcindexIsValid(row:Int,column:Int)->Bool{

10 returnrow>=0&&row<rows&&column>=0&&column

<columns

11 }

12 subscript(row:Int,column:Int)->Double{

13 get{

14 assert(indexIsValid(row:row,column:column),

"Indexoutofrange")

15 returngrid[(row*columns)+column]

16 }

17 set{

18 assert(indexIsValid(row:row,column:column),

"Indexoutofrange")

19 grid[(row*columns)+column]=newValue

20 }

21 }

22 }

Matrixprovidesaninitializerthattakestwoparameterscalledrowsandcolumns,andcreatesanarraythatislargeenoughtostorerows*columnsvaluesoftypeDouble.Eachpositioninthematrixisgivenaninitialvalueof0.0.Toachievethis,thearray’ssize,andaninitialcellvalueof0.0,arepassedtoanarrayinitializerthatcreatesandinitializesanewarrayofthecorrectsize.ThisinitializerisdescribedinmoredetailinCreatinganArraywithaDefaultValue.

YoucanconstructanewMatrixinstancebypassinganappropriaterowandcolumncounttoitsinitializer:

varmatrix=Matrix(rows:2,columns:2)

TheexampleabovecreatesanewMatrixinstancewithtworowsandtwocolumns.ThegridarrayforthisMatrixinstanceiseffectivelyaflattenedversionofthematrix,asreadfromtoplefttobottomright:

Valuesinthematrixcanbesetbypassingrowandcolumnvaluesintothesubscript,separatedbyacomma:

1 matrix[0,1]=1.5

2 matrix[1,0]=3.2

Thesetwostatementscallthesubscript’ssettertosetavalueof1.5inthetoprightpositionofthematrix(whererowis0andcolumnis1),and3.2inthebottomleftposition(whererowis1andcolumnis0):

TheMatrixsubscript’sgetterandsetterbothcontainanassertiontocheckthatthesubscript’srowandcolumnvaluesarevalid.Toassistwiththeseassertions,MatrixincludesaconveniencemethodcalledindexIsValid(row:column:),whichcheckswhethertherequestedrowandcolumnareinsidetheboundsofthematrix:

1 funcindexIsValid(row:Int,column:Int)->Bool{

2 returnrow>=0&&row<rows&&column>=0&&column<

columns

3 }

Anassertionistriggeredifyoutrytoaccessasubscriptthatisoutsideofthematrixbounds:

1 letsomeValue=matrix[2,2]

2 //Thistriggersanassert,because[2,2]isoutsideofthe

matrixbounds.

TypeSubscripts

Instancesubscripts,asdescribedabove,aresubscriptsthatyoucallonaninstanceofaparticulartype.Youcanalsodefinesubscriptsthatarecalledonthetypeitself.Thiskindofsubscriptiscalledatypesubscript.Youindicateatypesubscriptbywritingthestatickeywordbeforethesubscriptkeyword.Classescanusetheclasskeywordinstead,toallowsubclassestooverridethesuperclass’simplementationofthatsubscript.Theexamplebelowshowshowyoudefineandcallatypesubscript:

1 enumPlanet:Int{

2 casemercury=1,venus,earth,mars,jupiter,saturn,

uranus,neptune

3 staticsubscript(n:Int)->Planet{

4 returnPlanet(rawValue:n)!

5 }

6 }

7 letmars=Planet[4]

8 print(mars)

Inheritance

Aclasscaninheritmethods,properties,andothercharacteristicsfromanotherclass.Whenoneclassinheritsfromanother,theinheritingclassisknownasasubclass,andtheclassitinheritsfromisknownasitssuperclass.InheritanceisafundamentalbehaviorthatdifferentiatesclassesfromothertypesinSwift.

ClassesinSwiftcancallandaccessmethods,properties,andsubscriptsbelongingtotheirsuperclassandcanprovidetheirownoverridingversionsofthosemethods,properties,andsubscriptstorefineormodifytheirbehavior.Swifthelpstoensureyouroverridesarecorrectbycheckingthattheoverridedefinitionhasamatchingsuperclassdefinition.

Classescanalsoaddpropertyobserverstoinheritedpropertiesinordertobenotifiedwhenthevalueofapropertychanges.Propertyobserverscanbeaddedtoanyproperty,regardlessofwhetheritwasoriginallydefinedasastoredorcomputedproperty.

DefiningaBaseClass

Anyclassthatdoesnotinheritfromanotherclassisknownasabaseclass.

NOTE

Swiftclassesdonotinheritfromauniversalbaseclass.Classesyoudefinewithoutspecifyingasuperclassautomaticallybecomebaseclassesforyoutobuildupon.

TheexamplebelowdefinesabaseclasscalledVehicle.ThisbaseclassdefinesastoredpropertycalledcurrentSpeed,withadefaultvalueof0.0(inferringapropertytypeofDouble).ThecurrentSpeedproperty’svalueisusedbyaread-onlycomputedStringpropertycalleddescriptiontocreateadescriptionofthevehicle.

TheVehiclebaseclassalsodefinesamethodcalledmakeNoise.Thismethod

doesnotactuallydoanythingforabaseVehicleinstance,butwillbecustomizedbysubclassesofVehiclelateron:

1 classVehicle{

2 varcurrentSpeed=0.0

3 vardescription:String{

4 return"travelingat\(currentSpeed)milesperhour"

5 }

6 funcmakeNoise(){

7 //donothing-anarbitraryvehicledoesn't

necessarilymakeanoise

8 }

9 }

YoucreateanewinstanceofVehiclewithinitializersyntax,whichiswrittenasatypenamefollowedbyemptyparentheses:

letsomeVehicle=Vehicle()

HavingcreatedanewVehicleinstance,youcanaccessitsdescriptionpropertytoprintahuman-readabledescriptionofthevehicle’scurrentspeed:

1 print("Vehicle:\(someVehicle.description)")

2 //Vehicle:travelingat0.0milesperhour

TheVehicleclassdefinescommoncharacteristicsforanarbitraryvehicle,butisnotmuchuseinitself.Tomakeitmoreuseful,youneedtorefineittodescribemorespecifickindsofvehicles.

Subclassing

Subclassingistheactofbasinganewclassonanexistingclass.Thesubclass

inheritscharacteristicsfromtheexistingclass,whichyoucanthenrefine.Youcanalsoaddnewcharacteristicstothesubclass.

Toindicatethatasubclasshasasuperclass,writethesubclassnamebeforethesuperclassname,separatedbyacolon:

1 classSomeSubclass:SomeSuperclass{

2 //subclassdefinitiongoeshere

3 }

ThefollowingexampledefinesasubclasscalledBicycle,withasuperclassofVehicle:

1 classBicycle:Vehicle{

2 varhasBasket=false

3 }

ThenewBicycleclassautomaticallygainsallofthecharacteristicsofVehicle,suchasitscurrentSpeedanddescriptionpropertiesanditsmakeNoise()method.

Inadditiontothecharacteristicsitinherits,theBicycleclassdefinesanewstoredproperty,hasBasket,withadefaultvalueoffalse(inferringatypeofBoolfortheproperty).

Bydefault,anynewBicycleinstanceyoucreatewillnothaveabasket.YoucansetthehasBasketpropertytotrueforaparticularBicycleinstanceafterthatinstanceiscreated:

1 letbicycle=Bicycle()

2 bicycle.hasBasket=true

YoucanalsomodifytheinheritedcurrentSpeedpropertyofaBicycleinstance,andquerytheinstance’sinheriteddescriptionproperty:

1 bicycle.currentSpeed=15.0

2 print("Bicycle:\(bicycle.description)")

3 //Bicycle:travelingat15.0milesperhour

Subclassescanthemselvesbesubclassed.ThenextexamplecreatesasubclassofBicycleforatwo-seaterbicycleknownasa“tandem”:

1 classTandem:Bicycle{

2 varcurrentNumberOfPassengers=0

3 }

TandeminheritsallofthepropertiesandmethodsfromBicycle,whichinturninheritsallofthepropertiesandmethodsfromVehicle.TheTandemsubclassalsoaddsanewstoredpropertycalledcurrentNumberOfPassengers,withadefaultvalueof0.

IfyoucreateaninstanceofTandem,youcanworkwithanyofitsnewandinheritedproperties,andquerytheread-onlydescriptionpropertyitinheritsfromVehicle:

1 lettandem=Tandem()

2 tandem.hasBasket=true

3 tandem.currentNumberOfPassengers=2

4 tandem.currentSpeed=22.0

5 print("Tandem:\(tandem.description)")

6 //Tandem:travelingat22.0milesperhour

Overriding

Asubclasscanprovideitsowncustomimplementationofaninstancemethod,typemethod,instanceproperty,typeproperty,orsubscriptthatitwouldotherwiseinheritfromasuperclass.Thisisknownasoverriding.

Tooverrideacharacteristicthatwouldotherwisebeinherited,youprefixyouroverridingdefinitionwiththeoverridekeyword.Doingsoclarifiesthatyouintendtoprovideanoverrideandhavenotprovidedamatchingdefinitionbymistake.Overridingbyaccidentcancauseunexpectedbehavior,andanyoverrideswithouttheoverridekeywordarediagnosedasanerrorwhenyourcodeiscompiled.

TheoverridekeywordalsopromptstheSwiftcompilertocheckthatyouroverridingclass’ssuperclass(oroneofitsparents)hasadeclarationthatmatchestheoneyouprovidedfortheoverride.Thischeckensuresthatyouroverridingdefinitioniscorrect.

AccessingSuperclassMethods,Properties,andSubscriptsWhenyouprovideamethod,property,orsubscriptoverrideforasubclass,itissometimesusefultousetheexistingsuperclassimplementationaspartofyouroverride.Forexample,youcanrefinethebehaviorofthatexistingimplementation,orstoreamodifiedvalueinanexistinginheritedvariable.

Wherethisisappropriate,youaccessthesuperclassversionofamethod,property,orsubscriptbyusingthesuperprefix:

AnoverriddenmethodnamedsomeMethod()cancallthesuperclassversionofsomeMethod()bycallingsuper.someMethod()withintheoverridingmethodimplementation.

AnoverriddenpropertycalledsomePropertycanaccessthesuperclassversionofsomePropertyassuper.somePropertywithintheoverridinggetterorsetterimplementation.

AnoverriddensubscriptforsomeIndexcanaccessthesuperclassversionofthesamesubscriptassuper[someIndex]fromwithintheoverridingsubscriptimplementation.

OverridingMethods

Youcanoverrideaninheritedinstanceortypemethodtoprovideatailoredoralternativeimplementationofthemethodwithinyoursubclass.

ThefollowingexampledefinesanewsubclassofVehiclecalledTrain,whichoverridesthemakeNoise()methodthatTraininheritsfromVehicle:

1 classTrain:Vehicle{

2 overridefuncmakeNoise(){

3 print("ChooChoo")

4 }

5 }

IfyoucreateanewinstanceofTrainandcallitsmakeNoise()method,youcanseethattheTrainsubclassversionofthemethodiscalled:

1 lettrain=Train()

2 train.makeNoise()

3 //Prints"ChooChoo"

OverridingPropertiesYoucanoverrideaninheritedinstanceortypepropertytoprovideyourowncustomgetterandsetterforthatproperty,ortoaddpropertyobserverstoenabletheoverridingpropertytoobservewhentheunderlyingpropertyvaluechanges.

OverridingPropertyGettersandSetters

Youcanprovideacustomgetter(andsetter,ifappropriate)tooverrideanyinheritedproperty,regardlessofwhethertheinheritedpropertyisimplementedasastoredorcomputedpropertyatsource.Thestoredorcomputednatureofaninheritedpropertyisnotknownbyasubclass—itonlyknowsthattheinheritedpropertyhasacertainnameandtype.Youmustalwaysstateboththenameandthetypeofthepropertyyouareoverriding,toenablethecompilertocheckthatyouroverridematchesasuperclasspropertywiththesamenameandtype.

Youcanpresentaninheritedread-onlypropertyasaread-writepropertybyprovidingbothagetterandasetterinyoursubclasspropertyoverride.Youcannot,however,presentaninheritedread-writepropertyasaread-onlyproperty.

NOTE

Ifyouprovideasetteraspartofapropertyoverride,youmustalsoprovideagetterforthatoverride.Ifyoudon’twanttomodifytheinheritedproperty’svaluewithintheoverridinggetter,youcansimplypassthroughtheinheritedvaluebyreturningsuper.somePropertyfromthegetter,wheresomePropertyisthenameofthepropertyyouareoverriding.

ThefollowingexampledefinesanewclasscalledCar,whichisasubclassofVehicle.TheCarclassintroducesanewstoredpropertycalledgear,withadefaultintegervalueof1.TheCarclassalsooverridesthedescriptionpropertyitinheritsfromVehicle,toprovideacustomdescriptionthatincludesthecurrentgear:

1 classCar:Vehicle{

2 vargear=1

3 overridevardescription:String{

4 returnsuper.description+"ingear\(gear)"

5 }

6 }

Theoverrideofthedescriptionpropertystartsbycallingsuper.description,whichreturnstheVehicleclass’sdescriptionproperty.TheCarclass’sversionofdescriptionthenaddssomeextratextontotheendofthisdescriptiontoprovideinformationaboutthecurrentgear.

IfyoucreateaninstanceoftheCarclassandsetitsgearandcurrentSpeedproperties,youcanseethatitsdescriptionpropertyreturnsthetailoreddescriptiondefinedwithintheCarclass:

1 letcar=Car()

2 car.currentSpeed=25.0

3 car.gear=3

4 print("Car:\(car.description)")

5 //Car:travelingat25.0milesperhouringear3

OverridingPropertyObservers

Youcanusepropertyoverridingtoaddpropertyobserverstoaninheritedproperty.Thisenablesyoutobenotifiedwhenthevalueofaninheritedpropertychanges,regardlessofhowthatpropertywasoriginallyimplemented.Formoreinformationonpropertyobservers,seePropertyObservers.

NOTE

Youcannotaddpropertyobserverstoinheritedconstantstoredpropertiesorinheritedread-onlycomputedproperties.Thevalueofthesepropertiescannotbeset,andsoitisnotappropriatetoprovideawillSetordidSetimplementationaspartofanoverride.

Notealsothatyoucannotprovidebothanoverridingsetterandanoverridingpropertyobserverforthesameproperty.Ifyouwanttoobservechangestoaproperty’svalue,andyouarealreadyprovidingacustomsetterforthatproperty,youcansimplyobserveanyvaluechangesfromwithinthecustomsetter.

ThefollowingexampledefinesanewclasscalledAutomaticCar,whichisasubclassofCar.TheAutomaticCarclassrepresentsacarwithanautomaticgearbox,whichautomaticallyselectsanappropriategeartousebasedonthecurrentspeed:

1 classAutomaticCar:Car{

2 overridevarcurrentSpeed:Double{

3 didSet{

4 gear=Int(currentSpeed/10.0)+1

5 }

6 }

7 }

WheneveryousetthecurrentSpeedpropertyofanAutomaticCarinstance,theproperty’sdidSetobserversetstheinstance’sgearpropertytoanappropriate

choiceofgearforthenewspeed.Specifically,thepropertyobserverchoosesagearthatisthenewcurrentSpeedvaluedividedby10,roundeddowntothenearestinteger,plus1.Aspeedof35.0producesagearof4:

1 letautomatic=AutomaticCar()

2 automatic.currentSpeed=35.0

3 print("AutomaticCar:\(automatic.description)")

4 //AutomaticCar:travelingat35.0milesperhouringear4

PreventingOverrides

Youcanpreventamethod,property,orsubscriptfrombeingoverriddenbymarkingitasfinal.Dothisbywritingthefinalmodifierbeforethemethod,property,orsubscript’sintroducerkeyword(suchasfinalvar,finalfunc,finalclassfunc,andfinalsubscript).

Anyattempttooverrideafinalmethod,property,orsubscriptinasubclassisreportedasacompile-timeerror.Methods,properties,orsubscriptsthatyouaddtoaclassinanextensioncanalsobemarkedasfinalwithintheextension’sdefinition.

Youcanmarkanentireclassasfinalbywritingthefinalmodifierbeforetheclasskeywordinitsclassdefinition(finalclass).Anyattempttosubclassafinalclassisreportedasacompile-timeerror.

Initialization

Initializationistheprocessofpreparinganinstanceofaclass,structure,orenumerationforuse.Thisprocessinvolvessettinganinitialvalueforeachstoredpropertyonthatinstanceandperforminganyothersetuporinitializationthatisrequiredbeforethenewinstanceisreadyforuse.

Youimplementthisinitializationprocessbydefininginitializers,whicharelikespecialmethodsthatcanbecalledtocreateanewinstanceofaparticulartype.UnlikeObjective-Cinitializers,Swiftinitializersdonotreturnavalue.Theirprimaryroleistoensurethatnewinstancesofatypearecorrectlyinitializedbeforetheyareusedforthefirsttime.

Instancesofclasstypescanalsoimplementadeinitializer,whichperformsanycustomcleanupjustbeforeaninstanceofthatclassisdeallocated.Formoreinformationaboutdeinitializers,seeDeinitialization.

SettingInitialValuesforStoredProperties

Classesandstructuresmustsetalloftheirstoredpropertiestoanappropriateinitialvaluebythetimeaninstanceofthatclassorstructureiscreated.Storedpropertiescannotbeleftinanindeterminatestate.

Youcansetaninitialvalueforastoredpropertywithinaninitializer,orbyassigningadefaultpropertyvalueaspartoftheproperty’sdefinition.Theseactionsaredescribedinthefollowingsections.

NOTE

Whenyouassignadefaultvaluetoastoredproperty,orsetitsinitialvaluewithinaninitializer,thevalueofthatpropertyissetdirectly,withoutcallinganypropertyobservers.

InitializersInitializersarecalledtocreateanewinstanceofaparticulartype.Initssimplestform,aninitializerislikeaninstancemethodwithnoparameters,writtenusingtheinitkeyword:

1 init(){

2 //performsomeinitializationhere

3 }

TheexamplebelowdefinesanewstructurecalledFahrenheittostoretemperaturesexpressedintheFahrenheitscale.TheFahrenheitstructurehasonestoredproperty,temperature,whichisoftypeDouble:

1 structFahrenheit{

2 vartemperature:Double

3 init(){

4 temperature=32.0

5 }

6 }

7 varf=Fahrenheit()

8 print("Thedefaulttemperatureis\(f.temperature)°

Fahrenheit")

9 //Prints"Thedefaulttemperatureis32.0°Fahrenheit"

Thestructuredefinesasingleinitializer,init,withnoparameters,whichinitializesthestoredtemperaturewithavalueof32.0(thefreezingpointofwaterindegreesFahrenheit).

DefaultPropertyValuesYoucansettheinitialvalueofastoredpropertyfromwithinaninitializer,asshownabove.Alternatively,specifyadefaultpropertyvalueaspartoftheproperty’sdeclaration.Youspecifyadefaultpropertyvaluebyassigningan

initialvaluetothepropertywhenitisdefined.

NOTE

Ifapropertyalwaystakesthesameinitialvalue,provideadefaultvalueratherthansettingavaluewithinaninitializer.Theendresultisthesame,butthedefaultvaluetiestheproperty’sinitializationmorecloselytoitsdeclaration.Itmakesforshorter,clearerinitializersandenablesyoutoinferthetypeofthepropertyfromitsdefaultvalue.Thedefaultvaluealsomakesiteasierforyoutotakeadvantageofdefaultinitializersandinitializerinheritance,asdescribedlaterinthischapter.

YoucanwritetheFahrenheitstructurefromaboveinasimplerformbyprovidingadefaultvalueforitstemperaturepropertyatthepointthatthepropertyisdeclared:

1 structFahrenheit{

2 vartemperature=32.0

3 }

CustomizingInitialization

Youcancustomizetheinitializationprocesswithinputparametersandoptionalpropertytypes,orbyassigningconstantpropertiesduringinitialization,asdescribedinthefollowingsections.

InitializationParametersYoucanprovideinitializationparametersaspartofaninitializer’sdefinition,todefinethetypesandnamesofvaluesthatcustomizetheinitializationprocess.Initializationparametershavethesamecapabilitiesandsyntaxasfunctionandmethodparameters.

ThefollowingexampledefinesastructurecalledCelsius,whichstorestemperaturesexpressedindegreesCelsius.TheCelsiusstructureimplementstwocustominitializerscalledinit(fromFahrenheit:)andinit(fromKelvin:),whichinitializeanewinstanceofthestructurewithavaluefromadifferent

temperaturescale:

1 structCelsius{

2 vartemperatureInCelsius:Double

3 init(fromFahrenheitfahrenheit:Double){

4 temperatureInCelsius=(fahrenheit-32.0)/1.8

5 }

6 init(fromKelvinkelvin:Double){

7 temperatureInCelsius=kelvin-273.15

8 }

9 }

10 letboilingPointOfWater=Celsius(fromFahrenheit:212.0)

11 //boilingPointOfWater.temperatureInCelsiusis100.0

12 letfreezingPointOfWater=Celsius(fromKelvin:273.15)

13 //freezingPointOfWater.temperatureInCelsiusis0.0

ThefirstinitializerhasasingleinitializationparameterwithanargumentlabeloffromFahrenheitandaparameternameoffahrenheit.ThesecondinitializerhasasingleinitializationparameterwithanargumentlabeloffromKelvinandaparameternameofkelvin.BothinitializersconverttheirsingleargumentintothecorrespondingCelsiusvalueandstorethisvalueinapropertycalledtemperatureInCelsius.

ParameterNamesandArgumentLabelsAswithfunctionandmethodparameters,initializationparameterscanhavebothaparameternameforusewithintheinitializer’sbodyandanargumentlabelforusewhencallingtheinitializer.

However,initializersdonothaveanidentifyingfunctionnamebeforetheirparenthesesinthewaythatfunctionsandmethodsdo.Therefore,thenamesandtypesofaninitializer’sparametersplayaparticularlyimportantroleinidentifyingwhichinitializershouldbecalled.Becauseofthis,Swiftprovidesan

automaticargumentlabelforeveryparameterinaninitializerifyoudon’tprovideone.

ThefollowingexampledefinesastructurecalledColor,withthreeconstantpropertiescalledred,green,andblue.Thesepropertiesstoreavaluebetween0.0and1.0toindicatetheamountofred,green,andblueinthecolor.

ColorprovidesaninitializerwiththreeappropriatelynamedparametersoftypeDoubleforitsred,green,andbluecomponents.Coloralsoprovidesasecondinitializerwithasinglewhiteparameter,whichisusedtoprovidethesamevalueforallthreecolorcomponents.

1 structColor{

2 letred,green,blue:Double

3 init(red:Double,green:Double,blue:Double){

4 self.red=red

5 self.green=green

6 self.blue=blue

7 }

8 init(white:Double){

9 red=white

10 green=white

11 blue=white

12 }

13 }

BothinitializerscanbeusedtocreateanewColorinstance,byprovidingnamedvaluesforeachinitializerparameter:

1 letmagenta=Color(red:1.0,green:0.0,blue:1.0)

2 lethalfGray=Color(white:0.5)

Notethatitisnotpossibletocalltheseinitializerswithoutusingargumentlabels.Argumentlabelsmustalwaysbeusedinaninitializeriftheyaredefined,

andomittingthemisacompile-timeerror:

1 letveryGreen=Color(0.0,1.0,0.0)

2 //thisreportsacompile-timeerror-argumentlabelsare

required

InitializerParametersWithoutArgumentLabelsIfyoudonotwanttouseanargumentlabelforaninitializerparameter,writeanunderscore(_)insteadofanexplicitargumentlabelforthatparametertooverridethedefaultbehavior.

Here’sanexpandedversionoftheCelsiusexamplefromInitializationParametersabove,withanadditionalinitializertocreateanewCelsiusinstancefromaDoublevaluethatisalreadyintheCelsiusscale:

1 structCelsius{

2 vartemperatureInCelsius:Double

3 init(fromFahrenheitfahrenheit:Double){

4 temperatureInCelsius=(fahrenheit-32.0)/1.8

5 }

6 init(fromKelvinkelvin:Double){

7 temperatureInCelsius=kelvin-273.15

8 }

9 init(_celsius:Double){

10 temperatureInCelsius=celsius

11 }

12 }

13 letbodyTemperature=Celsius(37.0)

14 //bodyTemperature.temperatureInCelsiusis37.0

TheinitializercallCelsius(37.0)isclearinitsintentwithouttheneedforan

argumentlabel.Itisthereforeappropriatetowritethisinitializerasinit(_celsius:Double)sothatitcanbecalledbyprovidinganunnamedDoublevalue.

OptionalPropertyTypesIfyourcustomtypehasastoredpropertythatislogicallyallowedtohave“novalue”—perhapsbecauseitsvaluecannotbesetduringinitialization,orbecauseitisallowedtohave“novalue”atsomelaterpoint—declarethepropertywithanoptionaltype.Propertiesofoptionaltypeareautomaticallyinitializedwithavalueofnil,indicatingthatthepropertyisdeliberatelyintendedtohave“novalueyet”duringinitialization.

ThefollowingexampledefinesaclasscalledSurveyQuestion,withanoptionalStringpropertycalledresponse:

1 classSurveyQuestion{

2 vartext:String

3 varresponse:String?

4 init(text:String){

5 self.text=text

6 }

7 funcask(){

8 print(text)

9 }

10 }

11 letcheeseQuestion=SurveyQuestion(text:"Doyoulike

cheese?")

12 cheeseQuestion.ask()

13 //Prints"Doyoulikecheese?"

14 cheeseQuestion.response="Yes,Idolikecheese."

Theresponsetoasurveyquestioncannotbeknownuntilitisasked,andsotheresponsepropertyisdeclaredwithatypeofString?,or“optionalString”.Itis

automaticallyassignedadefaultvalueofnil,meaning“nostringyet”,whenanewinstanceofSurveyQuestionisinitialized.

AssigningConstantPropertiesDuringInitializationYoucanassignavaluetoaconstantpropertyatanypointduringinitialization,aslongasitissettoadefinitevaluebythetimeinitializationfinishes.Onceaconstantpropertyisassignedavalue,itcan’tbefurthermodified.

NOTE

Forclassinstances,aconstantpropertycanbemodifiedduringinitializationonlybytheclassthatintroducesit.Itcannotbemodifiedbyasubclass.

YoucanrevisetheSurveyQuestionexamplefromabovetouseaconstantpropertyratherthanavariablepropertyforthetextpropertyofthequestion,toindicatethatthequestiondoesnotchangeonceaninstanceofSurveyQuestioniscreated.Eventhoughthetextpropertyisnowaconstant,itcanstillbesetwithintheclass’sinitializer:

1 classSurveyQuestion{

2 lettext:String

3 varresponse:String?

4 init(text:String){

5 self.text=text

6 }

7 funcask(){

8 print(text)

9 }

10 }

11 letbeetsQuestion=SurveyQuestion(text:"Howaboutbeets?")

12 beetsQuestion.ask()

13 //Prints"Howaboutbeets?"

14 beetsQuestion.response="Ialsolikebeets.(Butnotwith

cheese.)"

DefaultInitializers

Swiftprovidesadefaultinitializerforanystructureorclassthatprovidesdefaultvaluesforallofitspropertiesanddoesnotprovideatleastoneinitializeritself.Thedefaultinitializersimplycreatesanewinstancewithallofitspropertiessettotheirdefaultvalues.

ThisexampledefinesaclasscalledShoppingListItem,whichencapsulatesthename,quantity,andpurchasestateofaniteminashoppinglist:

1 classShoppingListItem{

2 varname:String?

3 varquantity=1

4 varpurchased=false

5 }

6 varitem=ShoppingListItem()

BecauseallpropertiesoftheShoppingListItemclasshavedefaultvalues,andbecauseitisabaseclasswithnosuperclass,ShoppingListItemautomaticallygainsadefaultinitializerimplementationthatcreatesanewinstancewithallofitspropertiessettotheirdefaultvalues.(ThenamepropertyisanoptionalStringproperty,andsoitautomaticallyreceivesadefaultvalueofnil,eventhoughthisvalueisnotwritteninthecode.)TheexampleaboveusesthedefaultinitializerfortheShoppingListItemclasstocreateanewinstanceoftheclasswithinitializersyntax,writtenasShoppingListItem(),andassignsthisnewinstancetoavariablecalleditem.

MemberwiseInitializersforStructureTypes

Structuretypesautomaticallyreceiveamemberwiseinitializeriftheydon’tdefineanyoftheirowncustominitializers.Unlikeadefaultinitializer,thestructurereceivesamemberwiseinitializerevenifithasstoredpropertiesthatdon’thavedefaultvalues.

Thememberwiseinitializerisashorthandwaytoinitializethememberpropertiesofnewstructureinstances.Initialvaluesforthepropertiesofthenewinstancecanbepassedtothememberwiseinitializerbyname.

TheexamplebelowdefinesastructurecalledSizewithtwopropertiescalledwidthandheight.BothpropertiesareinferredtobeoftypeDoublebyassigningadefaultvalueof0.0.

TheSizestructureautomaticallyreceivesaninit(width:height:)memberwiseinitializer,whichyoucanusetoinitializeanewSizeinstance:

1 structSize{

2 varwidth=0.0,height=0.0

3 }

4 lettwoByTwo=Size(width:2.0,height:2.0)

Whenyoucallamemberwiseinitializer,youcanomitvaluesforanypropertiesthathavedefaultvalues.Intheexampleabove,theSizestructurehasadefaultvalueforbothitsheightandwidthproperties.Youcanomiteitherpropertyorbothproperties,andtheinitializerusesthedefaultvalueforanythingyouomit—forexample:

1 letzeroByTwo=Size(height:2.0)

2 print(zeroByTwo.width,zeroByTwo.height)

3 //Prints"0.02.0"

4

5 letzeroByZero=Size()

6 print(zeroByZero.width,zeroByZero.height)

7 //Prints"0.00.0"

InitializerDelegationforValueTypes

Initializerscancallotherinitializerstoperformpartofaninstance’sinitialization.Thisprocess,knownasinitializerdelegation,avoidsduplicatingcodeacrossmultipleinitializers.

Therulesforhowinitializerdelegationworks,andforwhatformsofdelegationareallowed,aredifferentforvaluetypesandclasstypes.Valuetypes(structuresandenumerations)donotsupportinheritance,andsotheirinitializerdelegationprocessisrelativelysimple,becausetheycanonlydelegatetoanotherinitializerthattheyprovidethemselves.Classes,however,caninheritfromotherclasses,asdescribedinInheritance.Thismeansthatclasseshaveadditionalresponsibilitiesforensuringthatallstoredpropertiestheyinheritareassignedasuitablevalueduringinitialization.TheseresponsibilitiesaredescribedinClassInheritanceandInitializationbelow.

Forvaluetypes,youuseself.inittorefertootherinitializersfromthesamevaluetypewhenwritingyourowncustominitializers.Youcancallself.initonlyfromwithinaninitializer.

Notethatifyoudefineacustominitializerforavaluetype,youwillnolongerhaveaccesstothedefaultinitializer(orthememberwiseinitializer,ifitisastructure)forthattype.Thisconstraintpreventsasituationinwhichadditionalessentialsetupprovidedinamorecomplexinitializerisaccidentallycircumventedbysomeoneusingoneoftheautomaticinitializers.

NOTE

Ifyouwantyourcustomvaluetypetobeinitializablewiththedefaultinitializerandmemberwiseinitializer,andalsowithyourowncustominitializers,writeyourcustominitializersinanextensionratherthanaspartofthevaluetype’soriginalimplementation.Formoreinformation,seeExtensions.

ThefollowingexampledefinesacustomRectstructuretorepresentageometricrectangle.TheexamplerequirestwosupportingstructurescalledSizeandPoint,bothofwhichprovidedefaultvaluesof0.0foralloftheirproperties:

1 structSize{

2 varwidth=0.0,height=0.0

3 }

4 structPoint{

5 varx=0.0,y=0.0

6 }

YoucaninitializetheRectstructurebelowinoneofthreeways—byusingitsdefaultzero-initializedoriginandsizepropertyvalues,byprovidingaspecificoriginpointandsize,orbyprovidingaspecificcenterpointandsize.TheseinitializationoptionsarerepresentedbythreecustominitializersthatarepartoftheRectstructure’sdefinition:

1 structRect{

2 varorigin=Point()

3 varsize=Size()

4 init(){}

5 init(origin:Point,size:Size){

6 self.origin=origin

7 self.size=size

8 }

9 init(center:Point,size:Size){

10 letoriginX=center.x-(size.width/2)

11 letoriginY=center.y-(size.height/2)

12 self.init(origin:Point(x:originX,y:originY),size:

size)

13 }

14 }

ThefirstRectinitializer,init(),isfunctionallythesameasthedefaultinitializerthatthestructurewouldhavereceivedifitdidnothaveitsowncustominitializers.Thisinitializerhasanemptybody,representedbyanemptypairofcurlybraces{}.CallingthisinitializerreturnsaRectinstancewhoseoriginandsizepropertiesarebothinitializedwiththedefaultvaluesofPoint(x:0.0,y:

0.0)andSize(width:0.0,height:0.0)fromtheirpropertydefinitions:

1 letbasicRect=Rect()

2 //basicRect'soriginis(0.0,0.0)anditssizeis(0.0,0.0)

ThesecondRectinitializer,init(origin:size:),isfunctionallythesameasthememberwiseinitializerthatthestructurewouldhavereceivedifitdidnothaveitsowncustominitializers.Thisinitializersimplyassignstheoriginandsizeargumentvaluestotheappropriatestoredproperties:

1 letoriginRect=Rect(origin:Point(x:2.0,y:2.0),

2 size:Size(width:5.0,height:5.0))

3 //originRect'soriginis(2.0,2.0)anditssizeis(5.0,5.0)

ThethirdRectinitializer,init(center:size:),isslightlymorecomplex.Itstartsbycalculatinganappropriateoriginpointbasedonacenterpointandasizevalue.Itthencalls(ordelegates)totheinit(origin:size:)initializer,whichstorestheneworiginandsizevaluesintheappropriateproperties:

1 letcenterRect=Rect(center:Point(x:4.0,y:4.0),

2 size:Size(width:3.0,height:3.0))

3 //centerRect'soriginis(2.5,2.5)anditssizeis(3.0,3.0)

Theinit(center:size:)initializercouldhaveassignedthenewvaluesoforiginandsizetotheappropriatepropertiesitself.However,itismoreconvenient(andclearerinintent)fortheinit(center:size:)initializertotakeadvantageofanexistinginitializerthatalreadyprovidesexactlythatfunctionality.

NOTE

Foranalternativewaytowritethisexamplewithoutdefiningtheinit()andinit(origin:size:)initializersyourself,seeExtensions.

ClassInheritanceandInitialization

Allofaclass’sstoredproperties—includinganypropertiestheclassinheritsfromitssuperclass—mustbeassignedaninitialvalueduringinitialization.

Swiftdefinestwokindsofinitializersforclasstypestohelpensureallstoredpropertiesreceiveaninitialvalue.Theseareknownasdesignatedinitializersandconvenienceinitializers.

DesignatedInitializersandConvenienceInitializersDesignatedinitializersaretheprimaryinitializersforaclass.Adesignatedinitializerfullyinitializesallpropertiesintroducedbythatclassandcallsanappropriatesuperclassinitializertocontinuetheinitializationprocessupthesuperclasschain.

Classestendtohaveveryfewdesignatedinitializers,anditisquitecommonforaclasstohaveonlyone.Designatedinitializersare“funnel”pointsthroughwhichinitializationtakesplace,andthroughwhichtheinitializationprocesscontinuesupthesuperclasschain.

Everyclassmusthaveatleastonedesignatedinitializer.Insomecases,thisrequirementissatisfiedbyinheritingoneormoredesignatedinitializersfromasuperclass,asdescribedinAutomaticInitializerInheritancebelow.

Convenienceinitializersaresecondary,supportinginitializersforaclass.Youcandefineaconvenienceinitializertocalladesignatedinitializerfromthesameclassastheconvenienceinitializerwithsomeofthedesignatedinitializer’sparameterssettodefaultvalues.Youcanalsodefineaconvenienceinitializertocreateaninstanceofthatclassforaspecificusecaseorinputvaluetype.

Youdonothavetoprovideconvenienceinitializersifyourclassdoesnotrequirethem.Createconvenienceinitializerswheneverashortcuttoacommoninitializationpatternwillsavetimeormakeinitializationoftheclassclearerinintent.

SyntaxforDesignatedandConvenienceInitializersDesignatedinitializersforclassesarewritteninthesamewayassimpleinitializersforvaluetypes:

init( parameters ){

statements

}

Convenienceinitializersarewritteninthesamestyle,butwiththeconveniencemodifierplacedbeforetheinitkeyword,separatedbyaspace:

convenienceinit( parameters ){

statements

}

InitializerDelegationforClassTypesTosimplifytherelationshipsbetweendesignatedandconvenienceinitializers,Swiftappliesthefollowingthreerulesfordelegationcallsbetweeninitializers:

Rule1

Adesignatedinitializermustcalladesignatedinitializerfromitsimmediatesuperclass.

Rule2

Aconvenienceinitializermustcallanotherinitializerfromthesameclass.

Rule3

Aconvenienceinitializermustultimatelycalladesignatedinitializer.

Asimplewaytorememberthisis:

Designatedinitializersmustalwaysdelegateup.

Convenienceinitializersmustalwaysdelegateacross.

Theserulesareillustratedinthefigurebelow:

Here,thesuperclasshasasingledesignatedinitializerandtwoconvenienceinitializers.Oneconvenienceinitializercallsanotherconvenienceinitializer,whichinturncallsthesingledesignatedinitializer.Thissatisfiesrules2and3fromabove.Thesuperclassdoesnotitselfhaveafurthersuperclass,andsorule1doesnotapply.

Thesubclassinthisfigurehastwodesignatedinitializersandoneconvenienceinitializer.Theconvenienceinitializermustcalloneofthetwodesignatedinitializers,becauseitcanonlycallanotherinitializerfromthesameclass.Thissatisfiesrules2and3fromabove.Bothdesignatedinitializersmustcallthesingledesignatedinitializerfromthesuperclass,tosatisfyrule1fromabove.

NOTE

Theserulesdon’taffecthowusersofyourclassescreateinstancesofeachclass.Anyinitializerinthediagramabovecanbeusedtocreateafullyinitializedinstanceoftheclasstheybelongto.Therulesonlyaffecthowyouwritetheimplementationoftheclass’sinitializers.

Thefigurebelowshowsamorecomplexclasshierarchyforfourclasses.Itillustrateshowthedesignatedinitializersinthishierarchyactas“funnel”pointsforclassinitialization,simplifyingtheinterrelationshipsamongclassesinthechain:

Two-PhaseInitializationClassinitializationinSwiftisatwo-phaseprocess.Inthefirstphase,eachstoredpropertyisassignedaninitialvaluebytheclassthatintroducedit.Oncetheinitialstateforeverystoredpropertyhasbeendetermined,thesecondphasebegins,andeachclassisgiventheopportunitytocustomizeitsstoredpropertiesfurtherbeforethenewinstanceisconsideredreadyforuse.

Theuseofatwo-phaseinitializationprocessmakesinitializationsafe,whilestillgivingcompleteflexibilitytoeachclassinaclasshierarchy.Two-phaseinitializationpreventspropertyvaluesfrombeingaccessedbeforetheyareinitialized,andpreventspropertyvaluesfrombeingsettoadifferentvaluebyanotherinitializerunexpectedly.

NOTE

Swift’stwo-phaseinitializationprocessissimilartoinitializationinObjective-C.Themaindifferenceisthatduringphase1,Objective-Cassignszeroornullvalues(suchas0ornil)toevery

property.Swift’sinitializationflowismoreflexibleinthatitletsyousetcustominitialvalues,andcancopewithtypesforwhich0ornilisnotavaliddefaultvalue.

Swift’scompilerperformsfourhelpfulsafety-checkstomakesurethattwo-phaseinitializationiscompletedwithouterror:

Safetycheck1

Adesignatedinitializermustensurethatallofthepropertiesintroducedbyitsclassareinitializedbeforeitdelegatesuptoasuperclassinitializer.

Asmentionedabove,thememoryforanobjectisonlyconsideredfullyinitializedoncetheinitialstateofallofitsstoredpropertiesisknown.Inorderforthisruletobesatisfied,adesignatedinitializermustmakesurethatallofitsownpropertiesareinitializedbeforeithandsoffupthechain.

Safetycheck2

Adesignatedinitializermustdelegateuptoasuperclassinitializerbeforeassigningavaluetoaninheritedproperty.Ifitdoesn’t,thenewvaluethedesignatedinitializerassignswillbeoverwrittenbythesuperclassaspartofitsowninitialization.

Safetycheck3

Aconvenienceinitializermustdelegatetoanotherinitializerbeforeassigningavaluetoanyproperty(includingpropertiesdefinedbythesameclass).Ifitdoesn’t,thenewvaluetheconvenienceinitializerassignswillbeoverwrittenbyitsownclass’sdesignatedinitializer.

Safetycheck4

Aninitializercannotcallanyinstancemethods,readthevaluesofanyinstanceproperties,orrefertoselfasavalueuntilafterthefirstphaseofinitializationiscomplete.

Theclassinstanceisnotfullyvaliduntilthefirstphaseends.Propertiescanonlybeaccessed,andmethodscanonlybecalled,oncetheclassinstanceisknowntobevalidattheendofthefirstphase.

Here’showtwo-phaseinitializationplaysout,basedonthefoursafetychecksabove:

Phase1

Adesignatedorconvenienceinitializeriscalledonaclass.

Memoryforanewinstanceofthatclassisallocated.Thememoryisnotyetinitialized.

Adesignatedinitializerforthatclassconfirmsthatallstoredpropertiesintroducedbythatclasshaveavalue.Thememoryforthesestoredpropertiesisnowinitialized.

Thedesignatedinitializerhandsofftoasuperclassinitializertoperformthesametaskforitsownstoredproperties.

Thiscontinuesuptheclassinheritancechainuntilthetopofthechainisreached.

Oncethetopofthechainisreached,andthefinalclassinthechainhasensuredthatallofitsstoredpropertieshaveavalue,theinstance’smemoryisconsideredtobefullyinitialized,andphase1iscomplete.

Phase2

Workingbackdownfromthetopofthechain,eachdesignatedinitializerinthechainhastheoptiontocustomizetheinstancefurther.Initializersarenowabletoaccessselfandcanmodifyitsproperties,callitsinstancemethods,andsoon.

Finally,anyconvenienceinitializersinthechainhavetheoptiontocustomizetheinstanceandtoworkwithself.

Here’showphase1looksforaninitializationcallforahypotheticalsubclassandsuperclass:

Inthisexample,initializationbeginswithacalltoaconvenienceinitializeronthesubclass.Thisconvenienceinitializercannotyetmodifyanyproperties.Itdelegatesacrosstoadesignatedinitializerfromthesameclass.

Thedesignatedinitializermakessurethatallofthesubclass’spropertieshaveavalue,aspersafetycheck1.Itthencallsadesignatedinitializeronitssuperclasstocontinuetheinitializationupthechain.

Thesuperclass’sdesignatedinitializermakessurethatallofthesuperclasspropertieshaveavalue.Therearenofurthersuperclassestoinitialize,andsonofurtherdelegationisneeded.

Assoonasallpropertiesofthesuperclasshaveaninitialvalue,itsmemoryisconsideredfullyinitialized,andphase1iscomplete.

Here’showphase2looksforthesameinitializationcall:

Thesuperclass’sdesignatedinitializernowhasanopportunitytocustomizethe

instancefurther(althoughitdoesnothaveto).

Oncethesuperclass’sdesignatedinitializerisfinished,thesubclass’sdesignatedinitializercanperformadditionalcustomization(althoughagain,itdoesnothaveto).

Finally,oncethesubclass’sdesignatedinitializerisfinished,theconvenienceinitializerthatwasoriginallycalledcanperformadditionalcustomization.

InitializerInheritanceandOverridingUnlikesubclassesinObjective-C,Swiftsubclassesdonotinherittheirsuperclassinitializersbydefault.Swift’sapproachpreventsasituationinwhichasimpleinitializerfromasuperclassisinheritedbyamorespecializedsubclassandisusedtocreateanewinstanceofthesubclassthatisnotfullyorcorrectlyinitialized.

NOTE

Superclassinitializersareinheritedincertaincircumstances,butonlywhenitissafeandappropriatetodoso.Formoreinformation,seeAutomaticInitializerInheritancebelow.

Ifyouwantacustomsubclasstopresentoneormoreofthesameinitializersasitssuperclass,youcanprovideacustomimplementationofthoseinitializerswithinthesubclass.

Whenyouwriteasubclassinitializerthatmatchesasuperclassdesignatedinitializer,youareeffectivelyprovidinganoverrideofthatdesignatedinitializer.Therefore,youmustwritetheoverridemodifierbeforethesubclass’sinitializerdefinition.Thisistrueevenifyouareoverridinganautomaticallyprovideddefaultinitializer,asdescribedinDefaultInitializers.

Aswithanoverriddenproperty,methodorsubscript,thepresenceoftheoverridemodifierpromptsSwifttocheckthatthesuperclasshasamatchingdesignatedinitializertobeoverridden,andvalidatesthattheparametersforyouroverridinginitializerhavebeenspecifiedasintended.

NOTE

Youalwayswritetheoverridemodifierwhenoverridingasuperclassdesignatedinitializer,evenifyoursubclass’simplementationoftheinitializerisaconvenienceinitializer.

Conversely,ifyouwriteasubclassinitializerthatmatchesasuperclassconvenienceinitializer,thatsuperclassconvenienceinitializercanneverbecalleddirectlybyyoursubclass,aspertherulesdescribedaboveinInitializerDelegationforClassTypes.Therefore,yoursubclassisnot(strictlyspeaking)providinganoverrideofthesuperclassinitializer.Asaresult,youdonotwritetheoverridemodifierwhenprovidingamatchingimplementationofasuperclassconvenienceinitializer.

TheexamplebelowdefinesabaseclasscalledVehicle.ThisbaseclassdeclaresastoredpropertycallednumberOfWheels,withadefaultIntvalueof0.ThenumberOfWheelspropertyisusedbyacomputedpropertycalleddescriptiontocreateaStringdescriptionofthevehicle’scharacteristics:

1 classVehicle{

2 varnumberOfWheels=0

3 vardescription:String{

4 return"\(numberOfWheels)wheel(s)"

5 }

6 }

TheVehicleclassprovidesadefaultvalueforitsonlystoredproperty,anddoesnotprovideanycustominitializersitself.Asaresult,itautomaticallyreceivesadefaultinitializer,asdescribedinDefaultInitializers.Thedefaultinitializer(whenavailable)isalwaysadesignatedinitializerforaclass,andcanbeusedtocreateanewVehicleinstancewithanumberOfWheelsof0:

1 letvehicle=Vehicle()

2 print("Vehicle:\(vehicle.description)")

3 //Vehicle:0wheel(s)

ThenextexampledefinesasubclassofVehiclecalledBicycle:

1 classBicycle:Vehicle{

2 overrideinit(){

3 super.init()

4 numberOfWheels=2

5 }

6 }

TheBicyclesubclassdefinesacustomdesignatedinitializer,init().ThisdesignatedinitializermatchesadesignatedinitializerfromthesuperclassofBicycle,andsotheBicycleversionofthisinitializerismarkedwiththeoverridemodifier.

Theinit()initializerforBicyclestartsbycallingsuper.init(),whichcallsthedefaultinitializerfortheBicycleclass’ssuperclass,Vehicle.ThisensuresthatthenumberOfWheelsinheritedpropertyisinitializedbyVehiclebeforeBicyclehastheopportunitytomodifytheproperty.Aftercallingsuper.init(),theoriginalvalueofnumberOfWheelsisreplacedwithanewvalueof2.

IfyoucreateaninstanceofBicycle,youcancallitsinheriteddescriptioncomputedpropertytoseehowitsnumberOfWheelspropertyhasbeenupdated:

1 letbicycle=Bicycle()

2 print("Bicycle:\(bicycle.description)")

3 //Bicycle:2wheel(s)

Ifasubclassinitializerperformsnocustomizationinphase2oftheinitializationprocess,andthesuperclasshasazero-argumentdesignatedinitializer,youcanomitacalltosuper.init()afterassigningvaluestoallofthesubclass’sstoredproperties.

ThisexampledefinesanothersubclassofVehicle,calledHoverboard.Initsinitializer,theHoverboardclasssetsonlyitscolorproperty.Insteadofmakinganexplicitcalltosuper.init(),thisinitializerreliesonanimplicitcalltoits

superclass’sinitializertocompletetheprocess.

1 classHoverboard:Vehicle{

2 varcolor:String

3 init(color:String){

4 self.color=color

5 //super.init()implicitlycalledhere

6 }

7 overridevardescription:String{

8 return"\(super.description)inabeautiful\(color)"

9 }

10 }

AninstanceofHoverboardusesthedefaultnumberofwheelssuppliedbytheVehicleinitializer.

1 lethoverboard=Hoverboard(color:"silver")

2 print("Hoverboard:\(hoverboard.description)")

3 //Hoverboard:0wheel(s)inabeautifulsilver

NOTE

Subclassescanmodifyinheritedvariablepropertiesduringinitialization,butcannotmodifyinheritedconstantproperties.

AutomaticInitializerInheritanceAsmentionedabove,subclassesdonotinherittheirsuperclassinitializersbydefault.However,superclassinitializersareautomaticallyinheritedifcertainconditionsaremet.Inpractice,thismeansthatyoudonotneedtowriteinitializeroverridesinmanycommonscenarios,andcaninherityoursuperclassinitializerswithminimaleffortwheneveritissafetodoso.

Assumingthatyouprovidedefaultvaluesforanynewpropertiesyouintroduce

inasubclass,thefollowingtworulesapply:

Rule1

Ifyoursubclassdoesn’tdefineanydesignatedinitializers,itautomaticallyinheritsallofitssuperclassdesignatedinitializers.

Rule2

Ifyoursubclassprovidesanimplementationofallofitssuperclassdesignatedinitializers—eitherbyinheritingthemasperrule1,orbyprovidingacustomimplementationaspartofitsdefinition—thenitautomaticallyinheritsallofthesuperclassconvenienceinitializers.

Theserulesapplyevenifyoursubclassaddsfurtherconvenienceinitializers.

NOTE

Asubclasscanimplementasuperclassdesignatedinitializerasasubclassconvenienceinitializeraspartofsatisfyingrule2.

DesignatedandConvenienceInitializersinActionThefollowingexampleshowsdesignatedinitializers,convenienceinitializers,andautomaticinitializerinheritanceinaction.ThisexampledefinesahierarchyofthreeclassescalledFood,RecipeIngredient,andShoppingListItem,anddemonstrateshowtheirinitializersinteract.

ThebaseclassinthehierarchyiscalledFood,whichisasimpleclasstoencapsulatethenameofafoodstuff.TheFoodclassintroducesasingleStringpropertycallednameandprovidestwoinitializersforcreatingFoodinstances:

1 classFood{

2 varname:String

3 init(name:String){

4 self.name=name

5 }

6 convenienceinit(){

7 self.init(name:"[Unnamed]")

8 }

9 }

ThefigurebelowshowstheinitializerchainfortheFoodclass:

Classesdonothaveadefaultmemberwiseinitializer,andsotheFoodclassprovidesadesignatedinitializerthattakesasingleargumentcalledname.ThisinitializercanbeusedtocreateanewFoodinstancewithaspecificname:

1 letnamedMeat=Food(name:"Bacon")

2 //namedMeat'snameis"Bacon"

Theinit(name:String)initializerfromtheFoodclassisprovidedasadesignatedinitializer,becauseitensuresthatallstoredpropertiesofanewFoodinstancearefullyinitialized.TheFoodclassdoesnothaveasuperclass,andsotheinit(name:String)initializerdoesnotneedtocallsuper.init()tocompleteitsinitialization.

TheFoodclassalsoprovidesaconvenienceinitializer,init(),withnoarguments.Theinit()initializerprovidesadefaultplaceholdernameforanewfoodbydelegatingacrosstotheFoodclass’sinit(name:String)withanamevalueof[Unnamed]:

1 letmysteryMeat=Food()

2 //mysteryMeat'snameis"[Unnamed]"

ThesecondclassinthehierarchyisasubclassofFoodcalledRecipeIngredient.TheRecipeIngredientclassmodelsaningredientinacookingrecipe.ItintroducesanIntpropertycalledquantity(inadditiontothenamepropertyitinheritsfromFood)anddefinestwoinitializersforcreatingRecipeIngredientinstances:

1 classRecipeIngredient:Food{

2 varquantity:Int

3 init(name:String,quantity:Int){

4 self.quantity=quantity

5 super.init(name:name)

6 }

7 overrideconvenienceinit(name:String){

8 self.init(name:name,quantity:1)

9 }

10 }

ThefigurebelowshowstheinitializerchainfortheRecipeIngredientclass:

TheRecipeIngredientclasshasasingledesignatedinitializer,init(name:String,quantity:Int),whichcanbeusedtopopulateallofthepropertiesofanewRecipeIngredientinstance.Thisinitializerstartsbyassigningthepassed

quantityargumenttothequantityproperty,whichistheonlynewpropertyintroducedbyRecipeIngredient.Afterdoingso,theinitializerdelegatesuptotheinit(name:String)initializeroftheFoodclass.Thisprocesssatisfiessafetycheck1fromTwo-PhaseInitializationabove.

RecipeIngredientalsodefinesaconvenienceinitializer,init(name:String),whichisusedtocreateaRecipeIngredientinstancebynamealone.Thisconvenienceinitializerassumesaquantityof1foranyRecipeIngredientinstancethatiscreatedwithoutanexplicitquantity.ThedefinitionofthisconvenienceinitializermakesRecipeIngredientinstancesquickerandmoreconvenienttocreate,andavoidscodeduplicationwhencreatingseveralsingle-quantityRecipeIngredientinstances.Thisconvenienceinitializersimplydelegatesacrosstotheclass’sdesignatedinitializer,passinginaquantityvalueof1.

Theinit(name:String)convenienceinitializerprovidedbyRecipeIngredienttakesthesameparametersastheinit(name:String)designatedinitializerfromFood.Becausethisconvenienceinitializeroverridesadesignatedinitializerfromitssuperclass,itmustbemarkedwiththeoverridemodifier(asdescribedinInitializerInheritanceandOverriding).

EventhoughRecipeIngredientprovidestheinit(name:String)initializerasaconvenienceinitializer,RecipeIngredienthasnonethelessprovidedanimplementationofallofitssuperclass’sdesignatedinitializers.Therefore,RecipeIngredientautomaticallyinheritsallofitssuperclass’sconvenienceinitializerstoo.

Inthisexample,thesuperclassforRecipeIngredientisFood,whichhasasingleconvenienceinitializercalledinit().ThisinitializeristhereforeinheritedbyRecipeIngredient.Theinheritedversionofinit()functionsinexactlythesamewayastheFoodversion,exceptthatitdelegatestotheRecipeIngredientversionofinit(name:String)ratherthantheFoodversion.

AllthreeoftheseinitializerscanbeusedtocreatenewRecipeIngredientinstances:

1 letoneMysteryItem=RecipeIngredient()

2 letoneBacon=RecipeIngredient(name:"Bacon")

3 letsixEggs=RecipeIngredient(name:"Eggs",quantity:6)

ThethirdandfinalclassinthehierarchyisasubclassofRecipeIngredientcalledShoppingListItem.TheShoppingListItemclassmodelsarecipeingredientasitappearsinashoppinglist.

Everyitemintheshoppingliststartsoutas“unpurchased”.Torepresentthisfact,ShoppingListItemintroducesaBooleanpropertycalledpurchased,withadefaultvalueoffalse.ShoppingListItemalsoaddsacomputeddescriptionproperty,whichprovidesatextualdescriptionofaShoppingListIteminstance:

1 classShoppingListItem:RecipeIngredient{

2 varpurchased=false

3 vardescription:String{

4 varoutput="\(quantity)x\(name)"

5 output+=purchased?"✔":"✘"

6 returnoutput

7 }

8 }

NOTE

ShoppingListItemdoesnotdefineaninitializertoprovideaninitialvalueforpurchased,becauseitemsinashoppinglist(asmodeledhere)alwaysstartoutunpurchased.

Becauseitprovidesadefaultvalueforallofthepropertiesitintroducesanddoesnotdefineanyinitializersitself,ShoppingListItemautomaticallyinheritsallofthedesignatedandconvenienceinitializersfromitssuperclass.

Thefigurebelowshowstheoverallinitializerchainforallthreeclasses:

YoucanuseallthreeoftheinheritedinitializerstocreateanewShoppingListIteminstance:

1 varbreakfastList=[

2 ShoppingListItem(),

3 ShoppingListItem(name:"Bacon"),

4 ShoppingListItem(name:"Eggs",quantity:6),

5 ]

6 breakfastList[0].name="Orangejuice"

7 breakfastList[0].purchased=true

8 foriteminbreakfastList{

9 print(item.description)

10 }

11 //1xOrangejuice✔

12 //1xBacon✘

13 //6xEggs✘

Here,anewarraycalledbreakfastListiscreatedfromanarrayliteralcontainingthreenewShoppingListIteminstances.Thetypeofthearrayisinferredtobe[ShoppingListItem].Afterthearrayiscreated,thenameoftheShoppingListItematthestartofthearrayischangedfrom"[Unnamed]"to"Orangejuice"anditismarkedashavingbeenpurchased.Printingthedescriptionofeachiteminthearrayshowsthattheirdefaultstateshavebeensetasexpected.

FailableInitializers

Itissometimesusefultodefineaclass,structure,orenumerationforwhichinitializationcanfail.Thisfailuremightbetriggeredbyinvalidinitializationparametervalues,theabsenceofarequiredexternalresource,orsomeotherconditionthatpreventsinitializationfromsucceeding.

Tocopewithinitializationconditionsthatcanfail,defineoneormorefailableinitializersaspartofaclass,structure,orenumerationdefinition.Youwriteafailableinitializerbyplacingaquestionmarkaftertheinitkeyword(init?).

NOTE

Youcannotdefineafailableandanonfailableinitializerwiththesameparametertypesandnames.

Afailableinitializercreatesanoptionalvalueofthetypeitinitializes.Youwritereturnnilwithinafailableinitializertoindicateapointatwhichinitializationfailurecanbetriggered.

NOTE

Strictlyspeaking,initializersdonotreturnavalue.Rather,theirroleistoensurethatselfisfullyandcorrectlyinitializedbythetimethatinitializationends.Althoughyouwritereturnniltotriggeraninitializationfailure,youdonotusethereturnkeywordtoindicateinitializationsuccess.

Forinstance,failableinitializersareimplementedfornumerictypeconversions.

Toensureconversionbetweennumerictypesmaintainsthevalueexactly,usetheinit(exactly:)initializer.Ifthetypeconversioncannotmaintainthevalue,theinitializerfails.

1 letwholeNumber:Double=12345.0

2 letpi=3.14159

3

4 ifletvalueMaintained=Int(exactly:wholeNumber){

5 print("\(wholeNumber)conversiontoIntmaintainsvalueof

\(valueMaintained)")

6 }

7 //Prints"12345.0conversiontoIntmaintainsvalueof12345"

8

9 letvalueChanged=Int(exactly:pi)

10 //valueChangedisoftypeInt?,notInt

11

12 ifvalueChanged==nil{

13 print("\(pi)conversiontoIntdoesnotmaintainvalue")

14 }

15 //Prints"3.14159conversiontoIntdoesnotmaintainvalue"

TheexamplebelowdefinesastructurecalledAnimal,withaconstantStringpropertycalledspecies.TheAnimalstructurealsodefinesafailableinitializerwithasingleparametercalledspecies.Thisinitializerchecksifthespeciesvaluepassedtotheinitializerisanemptystring.Ifanemptystringisfound,aninitializationfailureistriggered.Otherwise,thespeciesproperty’svalueisset,andinitializationsucceeds:

1 structAnimal{

2 letspecies:String

3 init?(species:String){

4 ifspecies.isEmpty{returnnil}

5 self.species=species

6 }

7 }

YoucanusethisfailableinitializertotrytoinitializeanewAnimalinstanceandtocheckifinitializationsucceeded:

1 letsomeCreature=Animal(species:"Giraffe")

2 //someCreatureisoftypeAnimal?,notAnimal

3

4 ifletgiraffe=someCreature{

5 print("Ananimalwasinitializedwithaspeciesof\

(giraffe.species)")

6 }

7 //Prints"AnanimalwasinitializedwithaspeciesofGiraffe"

Ifyoupassanemptystringvaluetothefailableinitializer’sspeciesparameter,theinitializertriggersaninitializationfailure:

1 letanonymousCreature=Animal(species:"")

2 //anonymousCreatureisoftypeAnimal?,notAnimal

3

4 ifanonymousCreature==nil{

5 print("Theanonymouscreaturecouldnotbeinitialized")

6 }

7 //Prints"Theanonymouscreaturecouldnotbeinitialized"

NOTE

Checkingforanemptystringvalue(suchas""ratherthan"Giraffe")isnotthesameascheckingforniltoindicatetheabsenceofanoptionalStringvalue.Intheexampleabove,anemptystring("")isavalid,non-optionalString.However,itisnotappropriateforananimaltohaveanemptystringasthevalueofitsspeciesproperty.Tomodelthisrestriction,thefailableinitializertriggersaninitializationfailureifanemptystringisfound.

FailableInitializersforEnumerationsYoucanuseafailableinitializertoselectanappropriateenumerationcasebasedononeormoreparameters.Theinitializercanthenfailiftheprovidedparametersdonotmatchanappropriateenumerationcase.

TheexamplebelowdefinesanenumerationcalledTemperatureUnit,withthreepossiblestates(kelvin,celsius,andfahrenheit).AfailableinitializerisusedtofindanappropriateenumerationcaseforaCharactervaluerepresentingatemperaturesymbol:

1 enumTemperatureUnit{

2 casekelvin,celsius,fahrenheit

3 init?(symbol:Character){

4 switchsymbol{

5 case"K":

6 self=.kelvin

7 case"C":

8 self=.celsius

9 case"F":

10 self=.fahrenheit

11 default:

12 returnnil

13 }

14 }

15 }

Youcanusethisfailableinitializertochooseanappropriateenumerationcaseforthethreepossiblestatesandtocauseinitializationtofailiftheparameterdoesnotmatchoneofthesestates:

1 letfahrenheitUnit=TemperatureUnit(symbol:"F")

2 iffahrenheitUnit!=nil{

3 print("Thisisadefinedtemperatureunit,so

initializationsucceeded.")

4 }

5 //Prints"Thisisadefinedtemperatureunit,so

initializationsucceeded."

6

7 letunknownUnit=TemperatureUnit(symbol:"X")

8 ifunknownUnit==nil{

9 print("Thisisnotadefinedtemperatureunit,so

initializationfailed.")

10 }

11 //Prints"Thisisnotadefinedtemperatureunit,so

initializationfailed."

FailableInitializersforEnumerationswithRawValuesEnumerationswithrawvaluesautomaticallyreceiveafailableinitializer,init?(rawValue:),thattakesaparametercalledrawValueoftheappropriateraw-valuetypeandselectsamatchingenumerationcaseifoneisfound,ortriggersaninitializationfailureifnomatchingvalueexists.

YoucanrewritetheTemperatureUnitexamplefromabovetouserawvaluesoftypeCharacterandtotakeadvantageoftheinit?(rawValue:)initializer:

1 enumTemperatureUnit:Character{

2 casekelvin="K",celsius="C",fahrenheit="F"

3 }

4

5 letfahrenheitUnit=TemperatureUnit(rawValue:"F")

6 iffahrenheitUnit!=nil{

7 print("Thisisadefinedtemperatureunit,so

initializationsucceeded.")

8 }

9 //Prints"Thisisadefinedtemperatureunit,so

initializationsucceeded."

10

11 letunknownUnit=TemperatureUnit(rawValue:"X")

12 ifunknownUnit==nil{

13 print("Thisisnotadefinedtemperatureunit,so

initializationfailed.")

14 }

15 //Prints"Thisisnotadefinedtemperatureunit,so

initializationfailed."

PropagationofInitializationFailureAfailableinitializerofaclass,structure,orenumerationcandelegateacrosstoanotherfailableinitializerfromthesameclass,structure,orenumeration.Similarly,asubclassfailableinitializercandelegateuptoasuperclassfailableinitializer.

Ineithercase,ifyoudelegatetoanotherinitializerthatcausesinitializationtofail,theentireinitializationprocessfailsimmediately,andnofurtherinitializationcodeisexecuted.

NOTE

Afailableinitializercanalsodelegatetoanonfailableinitializer.Usethisapproachifyouneedtoaddapotentialfailurestatetoanexistinginitializationprocessthatdoesnototherwisefail.

TheexamplebelowdefinesasubclassofProductcalledCartItem.TheCartItemclassmodelsaniteminanonlineshoppingcart.CartItemintroducesastoredconstantpropertycalledquantityandensuresthatthispropertyalwayshasavalueofatleast1:

1 classProduct{

2 letname:String

3 init?(name:String){

4 ifname.isEmpty{returnnil}

5 self.name=name

6 }

7 }

8

9 classCartItem:Product{

10 letquantity:Int

11 init?(name:String,quantity:Int){

12 ifquantity<1{returnnil}

13 self.quantity=quantity

14 super.init(name:name)

15 }

16 }

ThefailableinitializerforCartItemstartsbyvalidatingthatithasreceivedaquantityvalueof1ormore.Ifthequantityisinvalid,theentireinitializationprocessfailsimmediatelyandnofurtherinitializationcodeisexecuted.Likewise,thefailableinitializerforProductchecksthenamevalue,andtheinitializerprocessfailsimmediatelyifnameistheemptystring.

IfyoucreateaCartIteminstancewithanonemptynameandaquantityof1ormore,initializationsucceeds:

1 iflettwoSocks=CartItem(name:"sock",quantity:2){

2 print("Item:\(twoSocks.name),quantity:\

(twoSocks.quantity)")

3 }

4 //Prints"Item:sock,quantity:2"

IfyoutrytocreateaCartIteminstancewithaquantityvalueof0,theCartItem

initializercausesinitializationtofail:

1 ifletzeroShirts=CartItem(name:"shirt",quantity:0){

2 print("Item:\(zeroShirts.name),quantity:\

(zeroShirts.quantity)")

3 }else{

4 print("Unabletoinitializezeroshirts")

5 }

6 //Prints"Unabletoinitializezeroshirts"

Similarly,ifyoutrytocreateaCartIteminstancewithanemptynamevalue,thesuperclassProductinitializercausesinitializationtofail:

1 ifletoneUnnamed=CartItem(name:"",quantity:1){

2 print("Item:\(oneUnnamed.name),quantity:\

(oneUnnamed.quantity)")

3 }else{

4 print("Unabletoinitializeoneunnamedproduct")

5 }

6 //Prints"Unabletoinitializeoneunnamedproduct"

OverridingaFailableInitializerYoucanoverrideasuperclassfailableinitializerinasubclass,justlikeanyotherinitializer.Alternatively,youcanoverrideasuperclassfailableinitializerwithasubclassnonfailableinitializer.Thisenablesyoutodefineasubclassforwhichinitializationcannotfail,eventhoughinitializationofthesuperclassisallowedtofail.

Notethatifyouoverrideafailablesuperclassinitializerwithanonfailablesubclassinitializer,theonlywaytodelegateuptothesuperclassinitializeristoforce-unwraptheresultofthefailablesuperclassinitializer.

NOTE

Youcanoverrideafailableinitializerwithanonfailableinitializerbutnottheotherwayaround.

TheexamplebelowdefinesaclasscalledDocument.Thisclassmodelsadocumentthatcanbeinitializedwithanamepropertythatiseitheranonemptystringvalueornil,butcannotbeanemptystring:

1 classDocument{

2 varname:String?

3 //thisinitializercreatesadocumentwithanilname

value

4 init(){}

5 //thisinitializercreatesadocumentwithanonemptyname

value

6 init?(name:String){

7 ifname.isEmpty{returnnil}

8 self.name=name

9 }

10 }

ThenextexampledefinesasubclassofDocumentcalledAutomaticallyNamedDocument.TheAutomaticallyNamedDocumentsubclassoverridesbothofthedesignatedinitializersintroducedbyDocument.TheseoverridesensurethatanAutomaticallyNamedDocumentinstancehasaninitialnamevalueof"[Untitled]"iftheinstanceisinitializedwithoutaname,orifanemptystringispassedtotheinit(name:)initializer:

1 classAutomaticallyNamedDocument:Document{

2 overrideinit(){

3 super.init()

4 self.name="[Untitled]"

5 }

6 overrideinit(name:String){

7 super.init()

8 ifname.isEmpty{

9 self.name="[Untitled]"

10 }else{

11 self.name=name

12 }

13 }

14 }

TheAutomaticallyNamedDocumentoverridesitssuperclass’sfailableinit?(name:)initializerwithanonfailableinit(name:)initializer.BecauseAutomaticallyNamedDocumentcopeswiththeemptystringcaseinadifferentwaythanitssuperclass,itsinitializerdoesnotneedtofail,andsoitprovidesanonfailableversionoftheinitializerinstead.

Youcanuseforcedunwrappinginaninitializertocallafailableinitializerfromthesuperclassaspartoftheimplementationofasubclass’snonfailableinitializer.Forexample,theUntitledDocumentsubclassbelowisalwaysnamed"[Untitled]",anditusesthefailableinit(name:)initializerfromitssuperclassduringinitialization.

1 classUntitledDocument:Document{

2 overrideinit(){

3 super.init(name:"[Untitled]")!

4 }

5 }

Inthiscase,iftheinit(name:)initializerofthesuperclasswereevercalledwithanemptystringasthename,theforcedunwrappingoperationwouldresultinaruntimeerror.However,becauseit’scalledwithastringconstant,youcanseethattheinitializerwon’tfail,sonoruntimeerrorcanoccurinthiscase.

Theinit!FailableInitializerYoutypicallydefineafailableinitializerthatcreatesanoptionalinstanceoftheappropriatetypebyplacingaquestionmarkaftertheinitkeyword(init?).Alternatively,youcandefineafailableinitializerthatcreatesanimplicitlyunwrappedoptionalinstanceoftheappropriatetype.Dothisbyplacinganexclamationmarkaftertheinitkeyword(init!)insteadofaquestionmark.

Youcandelegatefrominit?toinit!andviceversa,andyoucanoverrideinit?withinit!andviceversa.Youcanalsodelegatefrominittoinit!,althoughdoingsowilltriggeranassertioniftheinit!initializercausesinitializationtofail.

RequiredInitializers

Writetherequiredmodifierbeforethedefinitionofaclassinitializertoindicatethateverysubclassoftheclassmustimplementthatinitializer:

1 classSomeClass{

2 requiredinit(){

3 //initializerimplementationgoeshere

4 }

5 }

Youmustalsowritetherequiredmodifierbeforeeverysubclassimplementationofarequiredinitializer,toindicatethattheinitializerrequirementappliestofurthersubclassesinthechain.Youdonotwritetheoverridemodifierwhenoverridingarequireddesignatedinitializer:

1 classSomeSubclass:SomeClass{

2 requiredinit(){

3 //subclassimplementationoftherequiredinitializer

goeshere

4 }

5 }

NOTE

Youdonothavetoprovideanexplicitimplementationofarequiredinitializerifyoucansatisfytherequirementwithaninheritedinitializer.

SettingaDefaultPropertyValuewithaClosureorFunction

Ifastoredproperty’sdefaultvaluerequiressomecustomizationorsetup,youcanuseaclosureorglobalfunctiontoprovideacustomizeddefaultvalueforthatproperty.Wheneveranewinstanceofthetypethatthepropertybelongstoisinitialized,theclosureorfunctioniscalled,anditsreturnvalueisassignedastheproperty’sdefaultvalue.

Thesekindsofclosuresorfunctionstypicallycreateatemporaryvalueofthesametypeastheproperty,tailorthatvaluetorepresentthedesiredinitialstate,andthenreturnthattemporaryvaluetobeusedastheproperty’sdefaultvalue.

Here’saskeletonoutlineofhowaclosurecanbeusedtoprovideadefaultpropertyvalue:

1 classSomeClass{

2 letsomeProperty:SomeType={

3 //createadefaultvalueforsomePropertyinsidethis

closure

4 //someValuemustbeofthesametypeasSomeType

5 returnsomeValue

6 }()

7 }

Notethattheclosure’sendcurlybraceisfollowedbyanemptypairof

parentheses.ThistellsSwifttoexecutetheclosureimmediately.Ifyouomittheseparentheses,youaretryingtoassigntheclosureitselftotheproperty,andnotthereturnvalueoftheclosure.

NOTE

Ifyouuseaclosuretoinitializeaproperty,rememberthattherestoftheinstancehasnotyetbeeninitializedatthepointthattheclosureisexecuted.Thismeansthatyoucannotaccessanyotherpropertyvaluesfromwithinyourclosure,evenifthosepropertieshavedefaultvalues.Youalsocannotusetheimplicitselfproperty,orcallanyoftheinstance’smethods.

TheexamplebelowdefinesastructurecalledChessboard,whichmodelsaboardforthegameofchess.Chessisplayedonan8x8board,withalternatingblackandwhitesquares.

Torepresentthisgameboard,theChessboardstructurehasasinglepropertycalledboardColors,whichisanarrayof64Boolvalues.Avalueoftrueinthearrayrepresentsablacksquareandavalueoffalserepresentsawhitesquare.Thefirstiteminthearrayrepresentsthetopleftsquareontheboardandthelastiteminthearrayrepresentsthebottomrightsquareontheboard.

TheboardColorsarrayisinitializedwithaclosuretosetupitscolorvalues:

1 structChessboard{

2 letboardColors:[Bool]={

3 vartemporaryBoard=[Bool]()

4 varisBlack=false

5 foriin1...8{

6 forjin1...8{

7 temporaryBoard.append(isBlack)

8 isBlack=!isBlack

9 }

10 isBlack=!isBlack

11 }

12 returntemporaryBoard

13 }()

14 funcsquareIsBlackAt(row:Int,column:Int)->Bool{

15 returnboardColors[(row*8)+column]

16 }

17 }

WheneveranewChessboardinstanceiscreated,theclosureisexecuted,andthedefaultvalueofboardColorsiscalculatedandreturned.TheclosureintheexampleabovecalculatesandsetstheappropriatecolorforeachsquareontheboardinatemporaryarraycalledtemporaryBoard,andreturnsthistemporaryarrayastheclosure’sreturnvalueonceitssetupiscomplete.ThereturnedarrayvalueisstoredinboardColorsandcanbequeriedwiththesquareIsBlackAt(row:column:)utilityfunction:

1 letboard=Chessboard()

2 print(board.squareIsBlackAt(row:0,column:1))

3 //Prints"true"

4 print(board.squareIsBlackAt(row:7,column:7))

5 //Prints"false"

Deinitialization

Adeinitializeriscalledimmediatelybeforeaclassinstanceisdeallocated.Youwritedeinitializerswiththedeinitkeyword,similartohowinitializersarewrittenwiththeinitkeyword.Deinitializersareonlyavailableonclasstypes.

HowDeinitializationWorks

Swiftautomaticallydeallocatesyourinstanceswhentheyarenolongerneeded,tofreeupresources.Swifthandlesthememorymanagementofinstancesthroughautomaticreferencecounting(ARC),asdescribedinAutomaticReferenceCounting.Typicallyyoudon’tneedtoperformmanualcleanupwhenyourinstancesaredeallocated.However,whenyouareworkingwithyourownresources,youmightneedtoperformsomeadditionalcleanupyourself.Forexample,ifyoucreateacustomclasstoopenafileandwritesomedatatoit,youmightneedtoclosethefilebeforetheclassinstanceisdeallocated.

Classdefinitionscanhaveatmostonedeinitializerperclass.Thedeinitializerdoesnottakeanyparametersandiswrittenwithoutparentheses:

1 deinit{

2 //performthedeinitialization

3 }

Deinitializersarecalledautomatically,justbeforeinstancedeallocationtakesplace.Youarenotallowedtocalladeinitializeryourself.Superclassdeinitializersareinheritedbytheirsubclasses,andthesuperclassdeinitializeriscalledautomaticallyattheendofasubclassdeinitializerimplementation.Superclassdeinitializersarealwayscalled,evenifasubclassdoesnotprovideitsowndeinitializer.

Becauseaninstanceisnotdeallocateduntilafteritsdeinitializeriscalled,adeinitializercanaccessallpropertiesoftheinstanceitiscalledonandcan

modifyitsbehaviorbasedonthoseproperties(suchaslookingupthenameofafilethatneedstobeclosed).

DeinitializersinAction

Here’sanexampleofadeinitializerinaction.Thisexampledefinestwonewtypes,BankandPlayer,forasimplegame.TheBankclassmanagesamade-upcurrency,whichcanneverhavemorethan10,000coinsincirculation.TherecanonlyeverbeoneBankinthegame,andsotheBankisimplementedasaclasswithtypepropertiesandmethodstostoreandmanageitscurrentstate:

1 classBank{

2 staticvarcoinsInBank=10_000

3 staticfuncdistribute(coinsnumberOfCoinsRequested:Int)-

>Int{

4 letnumberOfCoinsToVend=min(numberOfCoinsRequested,

coinsInBank)

5 coinsInBank-=numberOfCoinsToVend

6 returnnumberOfCoinsToVend

7 }

8 staticfuncreceive(coins:Int){

9 coinsInBank+=coins

10 }

11 }

BankkeepstrackofthecurrentnumberofcoinsitholdswithitscoinsInBankproperty.Italsoofferstwomethods—distribute(coins:)andreceive(coins:)—tohandlethedistributionandcollectionofcoins.

Thedistribute(coins:)methodchecksthatthereareenoughcoinsinthebankbeforedistributingthem.Iftherearenotenoughcoins,Bankreturnsasmallernumberthanthenumberthatwasrequested(andreturnszeroifnocoinsareleft

inthebank).Itreturnsanintegervaluetoindicatetheactualnumberofcoinsthatwereprovided.

Thereceive(coins:)methodsimplyaddsthereceivednumberofcoinsbackintothebank’scoinstore.

ThePlayerclassdescribesaplayerinthegame.Eachplayerhasacertainnumberofcoinsstoredintheirpurseatanytime.Thisisrepresentedbytheplayer’scoinsInPurseproperty:

1 classPlayer{

2 varcoinsInPurse:Int

3 init(coins:Int){

4 coinsInPurse=Bank.distribute(coins:coins)

5 }

6 funcwin(coins:Int){

7 coinsInPurse+=Bank.distribute(coins:coins)

8 }

9 deinit{

10 Bank.receive(coins:coinsInPurse)

11 }

12 }

EachPlayerinstanceisinitializedwithastartingallowanceofaspecifiednumberofcoinsfromthebankduringinitialization,althoughaPlayerinstancemayreceivefewerthanthatnumberifnotenoughcoinsareavailable.

ThePlayerclassdefinesawin(coins:)method,whichretrievesacertainnumberofcoinsfromthebankandaddsthemtotheplayer’spurse.ThePlayerclassalsoimplementsadeinitializer,whichiscalledjustbeforeaPlayerinstanceisdeallocated.Here,thedeinitializersimplyreturnsalloftheplayer’scoinstothebank:

1 varplayerOne:Player?=Player(coins:100)

2 print("Anewplayerhasjoinedthegamewith\

(playerOne!.coinsInPurse)coins")

3 //Prints"Anewplayerhasjoinedthegamewith100coins"

4 print("Therearenow\(Bank.coinsInBank)coinsleftinthe

bank")

5 //Prints"Therearenow9900coinsleftinthebank"

AnewPlayerinstanceiscreated,witharequestfor100coinsiftheyareavailable.ThisPlayerinstanceisstoredinanoptionalPlayervariablecalledplayerOne.Anoptionalvariableisusedhere,becauseplayerscanleavethegameatanypoint.Theoptionalletsyoutrackwhetherthereiscurrentlyaplayerinthegame.

BecauseplayerOneisanoptional,itisqualifiedwithanexclamationmark(!)whenitscoinsInPursepropertyisaccessedtoprintitsdefaultnumberofcoins,andwheneveritswin(coins:)methodiscalled:

1 playerOne!.win(coins:2_000)

2 print("PlayerOnewon2000coins&nowhas\

(playerOne!.coinsInPurse)coins")

3 //Prints"PlayerOnewon2000coins&nowhas2100coins"

4 print("Thebanknowonlyhas\(Bank.coinsInBank)coinsleft")

5 //Prints"Thebanknowonlyhas7900coinsleft"

Here,theplayerhaswon2,000coins.Theplayer’spursenowcontains2,100coins,andthebankhasonly7,900coinsleft.

1 playerOne=nil

2 print("PlayerOnehasleftthegame")

3 //Prints"PlayerOnehasleftthegame"

4 print("Thebanknowhas\(Bank.coinsInBank)coins")

5 //Prints"Thebanknowhas10000coins"

Theplayerhasnowleftthegame.ThisisindicatedbysettingtheoptionalplayerOnevariabletonil,meaning“noPlayerinstance.”Atthepointthatthishappens,theplayerOnevariable’sreferencetothePlayerinstanceisbroken.NootherpropertiesorvariablesarestillreferringtothePlayerinstance,andsoitisdeallocatedinordertofreeupitsmemory.Justbeforethishappens,itsdeinitializeriscalledautomatically,anditscoinsarereturnedtothebank.

OptionalChaining

Optionalchainingisaprocessforqueryingandcallingproperties,methods,andsubscriptsonanoptionalthatmightcurrentlybenil.Iftheoptionalcontainsavalue,theproperty,method,orsubscriptcallsucceeds;iftheoptionalisnil,theproperty,method,orsubscriptcallreturnsnil.Multiplequeriescanbechainedtogether,andtheentirechainfailsgracefullyifanylinkinthechainisnil.

NOTE

OptionalchaininginSwiftissimilartomessagingnilinObjective-C,butinawaythatworksforanytype,andthatcanbecheckedforsuccessorfailure.

OptionalChainingasanAlternativetoForcedUnwrapping

Youspecifyoptionalchainingbyplacingaquestionmark(?)aftertheoptionalvalueonwhichyouwishtocallaproperty,methodorsubscriptiftheoptionalisnon-nil.Thisisverysimilartoplacinganexclamationmark(!)afteranoptionalvaluetoforcetheunwrappingofitsvalue.Themaindifferenceisthatoptionalchainingfailsgracefullywhentheoptionalisnil,whereasforcedunwrappingtriggersaruntimeerrorwhentheoptionalisnil.

Toreflectthefactthatoptionalchainingcanbecalledonanilvalue,theresultofanoptionalchainingcallisalwaysanoptionalvalue,eveniftheproperty,method,orsubscriptyouarequeryingreturnsanon-optionalvalue.Youcanusethisoptionalreturnvaluetocheckwhethertheoptionalchainingcallwassuccessful(thereturnedoptionalcontainsavalue),ordidnotsucceedduetoanilvalueinthechain(thereturnedoptionalvalueisnil).

Specifically,theresultofanoptionalchainingcallisofthesametypeastheexpectedreturnvalue,butwrappedinanoptional.ApropertythatnormallyreturnsanIntwillreturnanInt?whenaccessedthroughoptionalchaining.

Thenextseveralcodesnippetsdemonstratehowoptionalchainingdiffersfrom

forcedunwrappingandenablesyoutocheckforsuccess.

First,twoclassescalledPersonandResidencearedefined:

1 classPerson{

2 varresidence:Residence?

3 }

4

5 classResidence{

6 varnumberOfRooms=1

7 }

ResidenceinstanceshaveasingleIntpropertycallednumberOfRooms,withadefaultvalueof1.PersoninstanceshaveanoptionalresidencepropertyoftypeResidence?.

IfyoucreateanewPersoninstance,itsresidencepropertyisdefaultinitializedtonil,byvirtueofbeingoptional.Inthecodebelow,johnhasaresidencepropertyvalueofnil:

letjohn=Person()

IfyoutrytoaccessthenumberOfRoomspropertyofthisperson’sresidence,byplacinganexclamationmarkafterresidencetoforcetheunwrappingofitsvalue,youtriggeraruntimeerror,becausethereisnoresidencevaluetounwrap:

1 letroomCount=john.residence!.numberOfRooms

2 //thistriggersaruntimeerror

Thecodeabovesucceedswhenjohn.residencehasanon-nilvalueandwillsetroomCounttoanIntvaluecontainingtheappropriatenumberofrooms.However,thiscodealwaystriggersaruntimeerrorwhenresidenceisnil,asillustratedabove.

OptionalchainingprovidesanalternativewaytoaccessthevalueofnumberOfRooms.Touseoptionalchaining,useaquestionmarkinplaceoftheexclamationmark:

1 ifletroomCount=john.residence?.numberOfRooms{

2 print("John'sresidencehas\(roomCount)room(s).")

3 }else{

4 print("Unabletoretrievethenumberofrooms.")

5 }

6 //Prints"Unabletoretrievethenumberofrooms."

ThistellsSwiftto“chain”ontheoptionalresidencepropertyandtoretrievethevalueofnumberOfRoomsifresidenceexists.

BecausetheattempttoaccessnumberOfRoomshasthepotentialtofail,theoptionalchainingattemptreturnsavalueoftypeInt?,or“optionalInt”.Whenresidenceisnil,asintheexampleabove,thisoptionalIntwillalsobenil,toreflectthefactthatitwasnotpossibletoaccessnumberOfRooms.TheoptionalIntisaccessedthroughoptionalbindingtounwraptheintegerandassignthenon-optionalvaluetotheroomCountvariable.

NotethatthisistrueeventhoughnumberOfRoomsisanon-optionalInt.ThefactthatitisqueriedthroughanoptionalchainmeansthatthecalltonumberOfRoomswillalwaysreturnanInt?insteadofanInt.

YoucanassignaResidenceinstancetojohn.residence,sothatitnolongerhasanilvalue:

john.residence=Residence()

john.residencenowcontainsanactualResidenceinstance,ratherthannil.IfyoutrytoaccessnumberOfRoomswiththesameoptionalchainingasbefore,itwillnowreturnanInt?thatcontainsthedefaultnumberOfRoomsvalueof1:

1 ifletroomCount=john.residence?.numberOfRooms{

2 print("John'sresidencehas\(roomCount)room(s).")

3 }else{

4 print("Unabletoretrievethenumberofrooms.")

5 }

6 //Prints"John'sresidencehas1room(s)."

DefiningModelClassesforOptionalChaining

Youcanuseoptionalchainingwithcallstoproperties,methods,andsubscriptsthataremorethanoneleveldeep.Thisenablesyoutodrilldownintosubpropertieswithincomplexmodelsofinterrelatedtypes,andtocheckwhetheritispossibletoaccessproperties,methods,andsubscriptsonthosesubproperties.

Thecodesnippetsbelowdefinefourmodelclassesforuseinseveralsubsequentexamples,includingexamplesofmultileveloptionalchaining.TheseclassesexpanduponthePersonandResidencemodelfromabovebyaddingaRoomandAddressclass,withassociatedproperties,methods,andsubscripts.

ThePersonclassisdefinedinthesamewayasbefore:

1 classPerson{

2 varresidence:Residence?

3 }

TheResidenceclassismorecomplexthanbefore.Thistime,theResidenceclassdefinesavariablepropertycalledrooms,whichisinitializedwithanemptyarrayoftype[Room]:

1 classResidence{

2 varrooms=[Room]()

3 varnumberOfRooms:Int{

4 returnrooms.count

5 }

6 subscript(i:Int)->Room{

7 get{

8 returnrooms[i]

9 }

10 set{

11 rooms[i]=newValue

12 }

13 }

14 funcprintNumberOfRooms(){

15 print("Thenumberofroomsis\(numberOfRooms)")

16 }

17 varaddress:Address?

18 }

BecausethisversionofResidencestoresanarrayofRoominstances,itsnumberOfRoomspropertyisimplementedasacomputedproperty,notastoredproperty.ThecomputednumberOfRoomspropertysimplyreturnsthevalueofthecountpropertyfromtheroomsarray.

Asashortcuttoaccessingitsroomsarray,thisversionofResidenceprovidesaread-writesubscriptthatprovidesaccesstotheroomattherequestedindexintheroomsarray.

ThisversionofResidencealsoprovidesamethodcalledprintNumberOfRooms,whichsimplyprintsthenumberofroomsintheresidence.

Finally,Residencedefinesanoptionalpropertycalledaddress,withatypeofAddress?.TheAddressclasstypeforthispropertyisdefinedbelow.

TheRoomclassusedfortheroomsarrayisasimpleclasswithonepropertycalledname,andaninitializertosetthatpropertytoasuitableroomname:

1 classRoom{

2 letname:String

3 init(name:String){self.name=name}

4 }

ThefinalclassinthismodeliscalledAddress.ThisclasshasthreeoptionalpropertiesoftypeString?.Thefirsttwoproperties,buildingNameandbuildingNumber,arealternativewaystoidentifyaparticularbuildingaspartofanaddress.Thethirdproperty,street,isusedtonamethestreetforthataddress:

1 classAddress{

2 varbuildingName:String?

3 varbuildingNumber:String?

4 varstreet:String?

5 funcbuildingIdentifier()->String?{

6 ifletbuildingNumber=buildingNumber,letstreet=

street{

7 return"\(buildingNumber)\(street)"

8 }elseifbuildingName!=nil{

9 returnbuildingName

10 }else{

11 returnnil

12 }

13 }

14 }

TheAddressclassalsoprovidesamethodcalledbuildingIdentifier(),whichhasareturntypeofString?.ThismethodchecksthepropertiesoftheaddressandreturnsbuildingNameifithasavalue,orbuildingNumberconcatenatedwithstreetifbothhavevalues,ornilotherwise.

AccessingPropertiesThroughOptionalChaining

AsdemonstratedinOptionalChainingasanAlternativetoForcedUnwrapping,youcanuseoptionalchainingtoaccessapropertyonanoptionalvalue,andtocheckifthatpropertyaccessissuccessful.

UsetheclassesdefinedabovetocreateanewPersoninstance,andtrytoaccessitsnumberOfRoomspropertyasbefore:

1 letjohn=Person()

2 ifletroomCount=john.residence?.numberOfRooms{

3 print("John'sresidencehas\(roomCount)room(s).")

4 }else{

5 print("Unabletoretrievethenumberofrooms.")

6 }

7 //Prints"Unabletoretrievethenumberofrooms."

Becausejohn.residenceisnil,thisoptionalchainingcallfailsinthesamewayasbefore.

Youcanalsoattempttosetaproperty’svaluethroughoptionalchaining:

1 letsomeAddress=Address()

2 someAddress.buildingNumber="29"

3 someAddress.street="AcaciaRoad"

4 john.residence?.address=someAddress

Inthisexample,theattempttosettheaddresspropertyofjohn.residencewillfail,becausejohn.residenceiscurrentlynil.

Theassignmentispartoftheoptionalchaining,whichmeansnoneofthecodeontheright-handsideofthe=operatorisevaluated.Inthepreviousexample,it’snoteasytoseethatsomeAddressisneverevaluated,becauseaccessingaconstantdoesn’thaveanysideeffects.Thelistingbelowdoesthesameassignment,butitusesafunctiontocreatetheaddress.Thefunctionprints“Functionwascalled”

beforereturningavalue,whichletsyouseewhethertheright-handsideofthe=operatorwasevaluated.

1 funccreateAddress()->Address{

2 print("Functionwascalled.")

3

4 letsomeAddress=Address()

5 someAddress.buildingNumber="29"

6 someAddress.street="AcaciaRoad"

7

8 returnsomeAddress

9 }

10 john.residence?.address=createAddress()

YoucantellthatthecreateAddress()functionisn’tcalled,becausenothingisprinted.

CallingMethodsThroughOptionalChaining

Youcanuseoptionalchainingtocallamethodonanoptionalvalue,andtocheckwhetherthatmethodcallissuccessful.Youcandothisevenifthatmethoddoesnotdefineareturnvalue.

TheprintNumberOfRooms()methodontheResidenceclassprintsthecurrentvalueofnumberOfRooms.Here’showthemethodlooks:

1 funcprintNumberOfRooms(){

2 print("Thenumberofroomsis\(numberOfRooms)")

3 }

Thismethoddoesnotspecifyareturntype.However,functionsandmethodswithnoreturntypehaveanimplicitreturntypeofVoid,asdescribedin

FunctionsWithoutReturnValues.Thismeansthattheyreturnavalueof(),oranemptytuple.

Ifyoucallthismethodonanoptionalvaluewithoptionalchaining,themethod’sreturntypewillbeVoid?,notVoid,becausereturnvaluesarealwaysofanoptionaltypewhencalledthroughoptionalchaining.ThisenablesyoutouseanifstatementtocheckwhetheritwaspossibletocalltheprintNumberOfRooms()method,eventhoughthemethoddoesnotitselfdefineareturnvalue.ComparethereturnvaluefromtheprintNumberOfRoomscallagainstniltoseeifthemethodcallwassuccessful:

1 ifjohn.residence?.printNumberOfRooms()!=nil{

2 print("Itwaspossibletoprintthenumberofrooms.")

3 }else{

4 print("Itwasnotpossibletoprintthenumberofrooms.")

5 }

6 //Prints"Itwasnotpossibletoprintthenumberofrooms."

Thesameistrueifyouattempttosetapropertythroughoptionalchaining.TheexampleaboveinAccessingPropertiesThroughOptionalChainingattemptstosetanaddressvalueforjohn.residence,eventhoughtheresidencepropertyisnil.AnyattempttosetapropertythroughoptionalchainingreturnsavalueoftypeVoid?,whichenablesyoutocompareagainstniltoseeifthepropertywassetsuccessfully:

1 if(john.residence?.address=someAddress)!=nil{

2 print("Itwaspossibletosettheaddress.")

3 }else{

4 print("Itwasnotpossibletosettheaddress.")

5 }

6 //Prints"Itwasnotpossibletosettheaddress."

AccessingSubscriptsThroughOptionalChaining

Youcanuseoptionalchainingtotrytoretrieveandsetavaluefromasubscriptonanoptionalvalue,andtocheckwhetherthatsubscriptcallissuccessful.

NOTE

Whenyouaccessasubscriptonanoptionalvaluethroughoptionalchaining,youplacethequestionmarkbeforethesubscript’sbrackets,notafter.Theoptionalchainingquestionmarkalwaysfollowsimmediatelyafterthepartoftheexpressionthatisoptional.

Theexamplebelowtriestoretrievethenameofthefirstroomintheroomsarrayofthejohn.residencepropertyusingthesubscriptdefinedontheResidenceclass.Becausejohn.residenceiscurrentlynil,thesubscriptcallfails:

1 ifletfirstRoomName=john.residence?[0].name{

2 print("Thefirstroomnameis\(firstRoomName).")

3 }else{

4 print("Unabletoretrievethefirstroomname.")

5 }

6 //Prints"Unabletoretrievethefirstroomname."

Theoptionalchainingquestionmarkinthissubscriptcallisplacedimmediatelyafterjohn.residence,beforethesubscriptbrackets,becausejohn.residenceistheoptionalvalueonwhichoptionalchainingisbeingattempted.

Similarly,youcantrytosetanewvaluethroughasubscriptwithoptionalchaining:

john.residence?[0]=Room(name:"Bathroom")

Thissubscriptsettingattemptalsofails,becauseresidenceiscurrentlynil.

IfyoucreateandassignanactualResidenceinstancetojohn.residence,withoneormoreRoominstancesinitsroomsarray,youcanusetheResidencesubscripttoaccesstheactualitemsintheroomsarraythroughoptionalchaining:

1 letjohnsHouse=Residence()

2 johnsHouse.rooms.append(Room(name:"LivingRoom"))

3 johnsHouse.rooms.append(Room(name:"Kitchen"))

4 john.residence=johnsHouse

5

6 ifletfirstRoomName=john.residence?[0].name{

7 print("Thefirstroomnameis\(firstRoomName).")

8 }else{

9 print("Unabletoretrievethefirstroomname.")

10 }

11 //Prints"ThefirstroomnameisLivingRoom."

AccessingSubscriptsofOptionalTypeIfasubscriptreturnsavalueofoptionaltype—suchasthekeysubscriptofSwift’sDictionarytype—placeaquestionmarkafterthesubscript’sclosingbrackettochainonitsoptionalreturnvalue:

1 vartestScores=["Dave":[86,82,84],"Bev":[79,94,81]]

2 testScores["Dave"]?[0]=91

3 testScores["Bev"]?[0]+=1

4 testScores["Brian"]?[0]=72

5 //the"Dave"arrayisnow[91,82,84]andthe"Bev"arrayis

now[80,94,81]

TheexampleabovedefinesadictionarycalledtestScores,whichcontainstwokey-valuepairsthatmapaStringkeytoanarrayofIntvalues.Theexampleusesoptionalchainingtosetthefirstiteminthe"Dave"arrayto91;toincrementthefirstiteminthe"Bev"arrayby1;andtotrytosetthefirstiteminanarrayforakeyof"Brian".Thefirsttwocallssucceed,becausethetestScoresdictionarycontainskeysfor"Dave"and"Bev".Thethirdcallfails,becausethetestScoresdictionarydoesnotcontainakeyfor"Brian".

LinkingMultipleLevelsofChaining

Youcanlinktogethermultiplelevelsofoptionalchainingtodrilldowntoproperties,methods,andsubscriptsdeeperwithinamodel.However,multiplelevelsofoptionalchainingdonotaddmorelevelsofoptionalitytothereturnedvalue.

Toputitanotherway:

Ifthetypeyouaretryingtoretrieveisnotoptional,itwillbecomeoptionalbecauseoftheoptionalchaining.

Ifthetypeyouaretryingtoretrieveisalreadyoptional,itwillnotbecomemoreoptionalbecauseofthechaining.

Therefore:

IfyoutrytoretrieveanIntvaluethroughoptionalchaining,anInt?isalwaysreturned,nomatterhowmanylevelsofchainingareused.

Similarly,ifyoutrytoretrieveanInt?valuethroughoptionalchaining,anInt?isalwaysreturned,nomatterhowmanylevelsofchainingareused.

Theexamplebelowtriestoaccessthestreetpropertyoftheaddresspropertyoftheresidencepropertyofjohn.Therearetwolevelsofoptionalchaininginusehere,tochainthroughtheresidenceandaddressproperties,bothofwhichareofoptionaltype:

1 ifletjohnsStreet=john.residence?.address?.street{

2 print("John'sstreetnameis\(johnsStreet).")

3 }else{

4 print("Unabletoretrievetheaddress.")

5 }

6 //Prints"Unabletoretrievetheaddress."

Thevalueofjohn.residencecurrentlycontainsavalidResidenceinstance.However,thevalueofjohn.residence.addressiscurrentlynil.Becauseofthis,

thecalltojohn.residence?.address?.streetfails.

Notethatintheexampleabove,youaretryingtoretrievethevalueofthestreetproperty.ThetypeofthispropertyisString?.Thereturnvalueofjohn.residence?.address?.streetisthereforealsoString?,eventhoughtwolevelsofoptionalchainingareappliedinadditiontotheunderlyingoptionaltypeoftheproperty.

IfyousetanactualAddressinstanceasthevalueforjohn.residence.address,andsetanactualvaluefortheaddress’sstreetproperty,youcanaccessthevalueofthestreetpropertythroughmultileveloptionalchaining:

1 letjohnsAddress=Address()

2 johnsAddress.buildingName="TheLarches"

3 johnsAddress.street="LaurelStreet"

4 john.residence?.address=johnsAddress

5

6 ifletjohnsStreet=john.residence?.address?.street{

7 print("John'sstreetnameis\(johnsStreet).")

8 }else{

9 print("Unabletoretrievetheaddress.")

10 }

11 //Prints"John'sstreetnameisLaurelStreet."

Inthisexample,theattempttosettheaddresspropertyofjohn.residencewillsucceed,becausethevalueofjohn.residencecurrentlycontainsavalidResidenceinstance.

ChainingonMethodswithOptionalReturnValues

Thepreviousexampleshowshowtoretrievethevalueofapropertyofoptionaltypethroughoptionalchaining.Youcanalsouseoptionalchainingtocalla

methodthatreturnsavalueofoptionaltype,andtochainonthatmethod’sreturnvalueifneeded.

TheexamplebelowcallstheAddressclass’sbuildingIdentifier()methodthroughoptionalchaining.ThismethodreturnsavalueoftypeString?.Asdescribedabove,theultimatereturntypeofthismethodcallafteroptionalchainingisalsoString?:

1 ifletbuildingIdentifier=

john.residence?.address?.buildingIdentifier(){

2 print("John'sbuildingidentifieris\

(buildingIdentifier).")

3 }

4 //Prints"John'sbuildingidentifierisTheLarches."

Ifyouwanttoperformfurtheroptionalchainingonthismethod’sreturnvalue,placetheoptionalchainingquestionmarkafterthemethod’sparentheses:

1 ifletbeginsWithThe=

2

john.residence?.address?.buildingIdentifier()?.hasPrefix("The"

{

3 ifbeginsWithThe{

4 print("John'sbuildingidentifierbeginswith

\"The\".")

5 }else{

6 print("John'sbuildingidentifierdoesnotbeginwith

\"The\".")

7 }

8 }

9 //Prints"John'sbuildingidentifierbeginswith"The"."

NOTE

Intheexampleabove,youplacetheoptionalchainingquestionmarkaftertheparentheses,becausetheoptionalvalueyouarechainingonisthebuildingIdentifier()method’sreturnvalue,andnotthebuildingIdentifier()methoditself.

ErrorHandling

Errorhandlingistheprocessofrespondingtoandrecoveringfromerrorconditionsinyourprogram.Swiftprovidesfirst-classsupportforthrowing,catching,propagating,andmanipulatingrecoverableerrorsatruntime.

Someoperationsaren’tguaranteedtoalwayscompleteexecutionorproduceausefuloutput.Optionalsareusedtorepresenttheabsenceofavalue,butwhenanoperationfails,it’softenusefultounderstandwhatcausedthefailure,sothatyourcodecanrespondaccordingly.

Asanexample,considerthetaskofreadingandprocessingdatafromafileondisk.Thereareanumberofwaysthistaskcanfail,includingthefilenotexistingatthespecifiedpath,thefilenothavingreadpermissions,orthefilenotbeingencodedinacompatibleformat.Distinguishingamongthesedifferentsituationsallowsaprogramtoresolvesomeerrorsandtocommunicatetotheuseranyerrorsitcan’tresolve.

NOTE

ErrorhandlinginSwiftinteroperateswitherrorhandlingpatternsthatusetheNSErrorclassinCocoaandObjective-C.Formoreinformationaboutthisclass,seeHandlingCocoaErrorsinSwift.

RepresentingandThrowingErrors

InSwift,errorsarerepresentedbyvaluesoftypesthatconformtotheErrorprotocol.Thisemptyprotocolindicatesthatatypecanbeusedforerrorhandling.

Swiftenumerationsareparticularlywellsuitedtomodelingagroupofrelatederrorconditions,withassociatedvaluesallowingforadditionalinformationaboutthenatureofanerrortobecommunicated.Forexample,here’showyoumightrepresenttheerrorconditionsofoperatingavendingmachineinsideagame:

1 enumVendingMachineError:Error{

2 caseinvalidSelection

3 caseinsufficientFunds(coinsNeeded:Int)

4 caseoutOfStock

5 }

Throwinganerrorletsyouindicatethatsomethingunexpectedhappenedandthenormalflowofexecutioncan’tcontinue.Youuseathrowstatementtothrowanerror.Forexample,thefollowingcodethrowsanerrortoindicatethatfiveadditionalcoinsareneededbythevendingmachine:

throwVendingMachineError.insufficientFunds(coinsNeeded:5)

HandlingErrors

Whenanerroristhrown,somesurroundingpieceofcodemustberesponsibleforhandlingtheerror—forexample,bycorrectingtheproblem,tryinganalternativeapproach,orinformingtheuserofthefailure.

TherearefourwaystohandleerrorsinSwift.Youcanpropagatetheerrorfromafunctiontothecodethatcallsthatfunction,handletheerrorusingado-catchstatement,handletheerrorasanoptionalvalue,orassertthattheerrorwillnotoccur.Eachapproachisdescribedinasectionbelow.

Whenafunctionthrowsanerror,itchangestheflowofyourprogram,soit’simportantthatyoucanquicklyidentifyplacesinyourcodethatcanthrowerrors.Toidentifytheseplacesinyourcode,writethetrykeyword—orthetry?ortry!variation—beforeapieceofcodethatcallsafunction,method,orinitializerthatcanthrowanerror.Thesekeywordsaredescribedinthesectionsbelow.

NOTE

ErrorhandlinginSwiftresemblesexceptionhandlinginotherlanguages,withtheuseofthetry,catchandthrowkeywords.Unlikeexceptionhandlinginmanylanguages—includingObjective-C—errorhandlinginSwiftdoesnotinvolveunwindingthecallstack,aprocessthatcanbe

computationallyexpensive.Assuch,theperformancecharacteristicsofathrowstatementarecomparabletothoseofareturnstatement.

PropagatingErrorsUsingThrowingFunctionsToindicatethatafunction,method,orinitializercanthrowanerror,youwritethethrowskeywordinthefunction’sdeclarationafteritsparameters.Afunctionmarkedwiththrowsiscalledathrowingfunction.Ifthefunctionspecifiesareturntype,youwritethethrowskeywordbeforethereturnarrow(->).

1 funccanThrowErrors()throws->String

2

3 funccannotThrowErrors()->String

Athrowingfunctionpropagateserrorsthatarethrowninsideofittothescopefromwhichit’scalled.

NOTE

Onlythrowingfunctionscanpropagateerrors.Anyerrorsthrowninsideanonthrowingfunctionmustbehandledinsidethefunction.

Intheexamplebelow,theVendingMachineclasshasavend(itemNamed:)methodthatthrowsanappropriateVendingMachineErroriftherequesteditemisnotavailable,isoutofstock,orhasacostthatexceedsthecurrentdepositedamount:

1 structItem{

2 varprice:Int

3 varcount:Int

4 }

5

6 classVendingMachine{

7 varinventory=[

8 "CandyBar":Item(price:12,count:7),

9 "Chips":Item(price:10,count:4),

10 "Pretzels":Item(price:7,count:11)

11 ]

12 varcoinsDeposited=0

13

14 funcvend(itemNamedname:String)throws{

15 guardletitem=inventory[name]else{

16 throwVendingMachineError.invalidSelection

17 }

18

19 guarditem.count>0else{

20 throwVendingMachineError.outOfStock

21 }

22

23 guarditem.price<=coinsDepositedelse{

24 throw

VendingMachineError.insufficientFunds(coinsNeeded:

item.price-coinsDeposited)

25 }

26

27 coinsDeposited-=item.price

28

29 varnewItem=item

30 newItem.count-=1

31 inventory[name]=newItem

32

33 print("Dispensing\(name)")

34 }

35 }

Theimplementationofthevend(itemNamed:)methodusesguardstatementstoexitthemethodearlyandthrowappropriateerrorsifanyoftherequirementsforpurchasingasnackaren’tmet.Becauseathrowstatementimmediatelytransfersprogramcontrol,anitemwillbevendedonlyifalloftheserequirementsaremet.

Becausethevend(itemNamed:)methodpropagatesanyerrorsitthrows,anycodethatcallsthismethodmusteitherhandletheerrors—usingado-catchstatement,try?,ortry!—orcontinuetopropagatethem.Forexample,thebuyFavoriteSnack(person:vendingMachine:)intheexamplebelowisalsoathrowingfunction,andanyerrorsthatthevend(itemNamed:)methodthrowswillpropagateuptothepointwherethebuyFavoriteSnack(person:vendingMachine:)functioniscalled.

1 letfavoriteSnacks=[

2 "Alice":"Chips",

3 "Bob":"Licorice",

4 "Eve":"Pretzels",

5 ]

6 funcbuyFavoriteSnack(person:String,vendingMachine:

VendingMachine)throws{

7 letsnackName=favoriteSnacks[person]??"CandyBar"

8 tryvendingMachine.vend(itemNamed:snackName)

9 }

Inthisexample,thebuyFavoriteSnack(person:vendingMachine:)functionlooksupagivenperson’sfavoritesnackandtriestobuyitforthembycallingthevend(itemNamed:)method.Becausethevend(itemNamed:)methodcanthrowanerror,it’scalledwiththetrykeywordinfrontofit.

Throwinginitializerscanpropagateerrorsinthesamewayasthrowingfunctions.Forexample,theinitializerforthePurchasedSnackstructureinthelistingbelowcallsathrowingfunctionaspartoftheinitializationprocess,andithandlesanyerrorsthatitencountersbypropagatingthemtoitscaller.

1 structPurchasedSnack{

2 letname:String

3 init(name:String,vendingMachine:VendingMachine)throws{

4 tryvendingMachine.vend(itemNamed:name)

5 self.name=name

6 }

7 }

HandlingErrorsUsingDo-CatchYouuseado-catchstatementtohandleerrorsbyrunningablockofcode.Ifanerroristhrownbythecodeinthedoclause,itismatchedagainstthecatchclausestodeterminewhichoneofthemcanhandletheerror.

Hereisthegeneralformofado-catchstatement:

do{

try expression

statements

}catch pattern1 {

statements

}catch pattern2 where condition {

statements

}catch{

statements

}

Youwriteapatternaftercatchtoindicatewhaterrorsthatclausecanhandle.Ifacatchclausedoesn’thaveapattern,theclausematchesanyerrorandbindstheerrortoalocalconstantnamederror.Formoreinformationaboutpatternmatching,seePatterns.

Forexample,thefollowingcodematchesagainstallthreecasesofthe

VendingMachineErrorenumeration.

1 varvendingMachine=VendingMachine()

2 vendingMachine.coinsDeposited=8

3 do{

4 trybuyFavoriteSnack(person:"Alice",vendingMachine:

vendingMachine)

5 print("Success!Yum.")

6 }catchVendingMachineError.invalidSelection{

7 print("InvalidSelection.")

8 }catchVendingMachineError.outOfStock{

9 print("OutofStock.")

10 }catchVendingMachineError.insufficientFunds(letcoinsNeeded)

{

11 print("Insufficientfunds.Pleaseinsertanadditional\

(coinsNeeded)coins.")

12 }catch{

13 print("Unexpectederror:\(error).")

14 }

15 //Prints"Insufficientfunds.Pleaseinsertanadditional2

coins."

Intheaboveexample,thebuyFavoriteSnack(person:vendingMachine:)functioniscalledinatryexpression,becauseitcanthrowanerror.Ifanerroristhrown,executionimmediatelytransferstothecatchclauses,whichdecidewhethertoallowpropagationtocontinue.Ifnopatternismatched,theerrorgetscaughtbythefinalcatchclauseandisboundtoalocalerrorconstant.Ifnoerroristhrown,theremainingstatementsinthedostatementareexecuted.

Thecatchclausesdon’thavetohandleeverypossibleerrorthatthecodeinthedoclausecanthrow.Ifnoneofthecatchclauseshandletheerror,theerrorpropagatestothesurroundingscope.However,thepropagatederrormustbe

handledbysomesurroundingscope.Inanonthrowingfunction,anenclosingdo-catchclausemusthandletheerror.Inathrowingfunction,eitheranenclosingdo-catchclauseorthecallermusthandletheerror.Iftheerrorpropagatestothetop-levelscopewithoutbeinghandled,you’llgetaruntimeerror.

Forexample,theaboveexamplecanbewrittensoanyerrorthatisn’taVendingMachineErrorisinsteadcaughtbythecallingfunction:

1 funcnourish(withitem:String)throws{

2 do{

3 tryvendingMachine.vend(itemNamed:item)

4 }catchisVendingMachineError{

5 print("Invalidselection,outofstock,ornotenough

money.")

6 }

7 }

8

9 do{

10 trynourish(with:"Beet-FlavoredChips")

11 }catch{

12 print("Unexpectednon-vending-machine-relatederror:\

(error)")

13 }

14 //Prints"Invalidselection,outofstock,ornotenough

money."

Inthenourish(with:)function,ifvend(itemNamed:)throwsanerrorthat’soneofthecasesoftheVendingMachineErrorenumeration,nourish(with:)handlestheerrorbyprintingamessage.Otherwise,nourish(with:)propagatestheerrortoitscallsite.Theerroristhencaughtbythegeneralcatchclause.

ConvertingErrorstoOptionalValuesYouusetry?tohandleanerrorbyconvertingittoanoptionalvalue.Ifanerroristhrownwhileevaluatingthetry?expression,thevalueoftheexpressionisnil.Forexample,inthefollowingcodexandyhavethesamevalueandbehavior:

1 funcsomeThrowingFunction()throws->Int{

2 //...

3 }

4

5 letx=try?someThrowingFunction()

6

7 lety:Int?

8 do{

9 y=trysomeThrowingFunction()

10 }catch{

11 y=nil

12 }

IfsomeThrowingFunction()throwsanerror,thevalueofxandyisnil.Otherwise,thevalueofxandyisthevaluethatthefunctionreturned.NotethatxandyareanoptionalofwhatevertypesomeThrowingFunction()returns.Herethefunctionreturnsaninteger,soxandyareoptionalintegers.

Usingtry?letsyouwriteconciseerrorhandlingcodewhenyouwanttohandleallerrorsinthesameway.Forexample,thefollowingcodeusesseveralapproachestofetchdata,orreturnsnilifalloftheapproachesfail.

1 funcfetchData()->Data?{

2 ifletdata=try?fetchDataFromDisk(){returndata}

3 ifletdata=try?fetchDataFromServer(){returndata}

4 returnnil

5 }

DisablingErrorPropagationSometimesyouknowathrowingfunctionormethodwon’t,infact,throwanerroratruntime.Onthoseoccasions,youcanwritetry!beforetheexpressiontodisableerrorpropagationandwrapthecallinaruntimeassertionthatnoerrorwillbethrown.Ifanerroractuallyisthrown,you’llgetaruntimeerror.

Forexample,thefollowingcodeusesaloadImage(atPath:)function,whichloadstheimageresourceatagivenpathorthrowsanerroriftheimagecan’tbeloaded.Inthiscase,becausetheimageisshippedwiththeapplication,noerrorwillbethrownatruntime,soitisappropriatetodisableerrorpropagation.

letphoto=try!loadImage(atPath:"./Resources/John

Appleseed.jpg")

SpecifyingCleanupActions

Youuseadeferstatementtoexecuteasetofstatementsjustbeforecodeexecutionleavesthecurrentblockofcode.Thisstatementletsyoudoanynecessarycleanupthatshouldbeperformedregardlessofhowexecutionleavesthecurrentblockofcode—whetheritleavesbecauseanerrorwasthrownorbecauseofastatementsuchasreturnorbreak.Forexample,youcanuseadeferstatementtoensurethatfiledescriptorsareclosedandmanuallyallocatedmemoryisfreed.

Adeferstatementdefersexecutionuntilthecurrentscopeisexited.Thisstatementconsistsofthedeferkeywordandthestatementstobeexecutedlater.Thedeferredstatementsmaynotcontainanycodethatwouldtransfercontroloutofthestatements,suchasabreakorareturnstatement,orbythrowinganerror.Deferredactionsareexecutedinthereverseoftheorderthatthey’rewritteninyoursourcecode.Thatis,thecodeinthefirstdeferstatementexecuteslast,thecodeintheseconddeferstatementexecutessecondtolast,andsoon.Thelastdeferstatementinsourcecodeorderexecutesfirst.

1 funcprocessFile(filename:String)throws{

2 ifexists(filename){

3 letfile=open(filename)

4 defer{

5 close(file)

6 }

7 whileletline=tryfile.readline(){

8 //Workwiththefile.

9 }

10 //close(file)iscalledhere,attheendofthe

scope.

11 }

12 }

Theaboveexampleusesadeferstatementtoensurethattheopen(_:)functionhasacorrespondingcalltoclose(_:).

NOTE

Youcanuseadeferstatementevenwhennoerrorhandlingcodeisinvolved.

TypeCasting

Typecastingisawaytocheckthetypeofaninstance,ortotreatthatinstanceasadifferentsuperclassorsubclassfromsomewhereelseinitsownclasshierarchy.

TypecastinginSwiftisimplementedwiththeisandasoperators.Thesetwooperatorsprovideasimpleandexpressivewaytocheckthetypeofavalueorcastavaluetoadifferenttype.

Youcanalsousetypecastingtocheckwhetheratypeconformstoaprotocol,asdescribedinCheckingforProtocolConformance.

DefiningaClassHierarchyforTypeCasting

Youcanusetypecastingwithahierarchyofclassesandsubclassestocheckthetypeofaparticularclassinstanceandtocastthatinstancetoanotherclasswithinthesamehierarchy.Thethreecodesnippetsbelowdefineahierarchyofclassesandanarraycontaininginstancesofthoseclasses,foruseinanexampleoftypecasting.

ThefirstsnippetdefinesanewbaseclasscalledMediaItem.Thisclassprovidesbasicfunctionalityforanykindofitemthatappearsinadigitalmedialibrary.Specifically,itdeclaresanamepropertyoftypeString,andaninitnameinitializer.(Itisassumedthatallmediaitems,includingallmoviesandsongs,willhaveaname.)

1 classMediaItem{

2 varname:String

3 init(name:String){

4 self.name=name

5 }

6 }

ThenextsnippetdefinestwosubclassesofMediaItem.Thefirstsubclass,Movie,encapsulatesadditionalinformationaboutamovieorfilm.ItaddsadirectorpropertyontopofthebaseMediaItemclass,withacorrespondinginitializer.Thesecondsubclass,Song,addsanartistpropertyandinitializerontopofthebaseclass:

1 classMovie:MediaItem{

2 vardirector:String

3 init(name:String,director:String){

4 self.director=director

5 super.init(name:name)

6 }

7 }

8

9 classSong:MediaItem{

10 varartist:String

11 init(name:String,artist:String){

12 self.artist=artist

13 super.init(name:name)

14 }

15 }

Thefinalsnippetcreatesaconstantarraycalledlibrary,whichcontainstwoMovieinstancesandthreeSonginstances.Thetypeofthelibraryarrayisinferredbyinitializingitwiththecontentsofanarrayliteral.Swift’stypecheckerisabletodeducethatMovieandSonghaveacommonsuperclassofMediaItem,andsoitinfersatypeof[MediaItem]forthelibraryarray:

1 letlibrary=[

2 Movie(name:"Casablanca",director:"MichaelCurtiz"),

3 Song(name:"BlueSuedeShoes",artist:"ElvisPresley"),

4 Movie(name:"CitizenKane",director:"OrsonWelles"),

5 Song(name:"TheOneAndOnly",artist:"ChesneyHawkes"),

6 Song(name:"NeverGonnaGiveYouUp",artist:"Rick

Astley")

7 ]

8 //thetypeof"library"isinferredtobe[MediaItem]

TheitemsstoredinlibraryarestillMovieandSonginstancesbehindthescenes.However,ifyouiterateoverthecontentsofthisarray,theitemsyoureceivebackaretypedasMediaItem,andnotasMovieorSong.Inordertoworkwiththemastheirnativetype,youneedtochecktheirtype,ordowncastthemtoadifferenttype,asdescribedbelow.

CheckingType

Usethetypecheckoperator(is)tocheckwhetheraninstanceisofacertainsubclasstype.Thetypecheckoperatorreturnstrueiftheinstanceisofthatsubclasstypeandfalseifitisnot.

Theexamplebelowdefinestwovariables,movieCountandsongCount,whichcountthenumberofMovieandSonginstancesinthelibraryarray:

1 varmovieCount=0

2 varsongCount=0

3

4 foriteminlibrary{

5 ifitemisMovie{

6 movieCount+=1

7 }elseifitemisSong{

8 songCount+=1

9 }

10 }

11

12 print("Medialibrarycontains\(movieCount)moviesand\

(songCount)songs")

13 //Prints"Medialibrarycontains2moviesand3songs"

Thisexampleiteratesthroughallitemsinthelibraryarray.Oneachpass,thefor-inloopsetstheitemconstanttothenextMediaIteminthearray.

itemisMoviereturnstrueifthecurrentMediaItemisaMovieinstanceandfalseifitisnot.Similarly,itemisSongcheckswhethertheitemisaSonginstance.Attheendofthefor-inloop,thevaluesofmovieCountandsongCountcontainacountofhowmanyMediaIteminstanceswerefoundofeachtype.

Downcasting

Aconstantorvariableofacertainclasstypemayactuallyrefertoaninstanceofasubclassbehindthescenes.Whereyoubelievethisisthecase,youcantrytodowncasttothesubclasstypewithatypecastoperator(as?oras!).

Becausedowncastingcanfail,thetypecastoperatorcomesintwodifferentforms.Theconditionalform,as?,returnsanoptionalvalueofthetypeyouaretryingtodowncastto.Theforcedform,as!,attemptsthedowncastandforce-unwrapstheresultasasinglecompoundaction.

Usetheconditionalformofthetypecastoperator(as?)whenyouarenotsureifthedowncastwillsucceed.Thisformoftheoperatorwillalwaysreturnanoptionalvalue,andthevaluewillbenilifthedowncastwasnotpossible.Thisenablesyoutocheckforasuccessfuldowncast.

Usetheforcedformofthetypecastoperator(as!)onlywhenyouaresurethatthedowncastwillalwayssucceed.Thisformoftheoperatorwilltriggeraruntimeerrorifyoutrytodowncasttoanincorrectclasstype.

TheexamplebelowiteratesovereachMediaIteminlibrary,andprintsanappropriatedescriptionforeachitem.Todothis,itneedstoaccesseachitemasatrueMovieorSong,andnotjustasaMediaItem.ThisisnecessaryinorderforittobeabletoaccessthedirectororartistpropertyofaMovieorSongforuseinthedescription.

Inthisexample,eachiteminthearraymightbeaMovie,oritmightbeaSong.Youdon’tknowinadvancewhichactualclasstouseforeachitem,andsoitisappropriatetousetheconditionalformofthetypecastoperator(as?)tocheckthedowncasteachtimethroughtheloop:

1 foriteminlibrary{

2 ifletmovie=itemas?Movie{

3 print("Movie:\(movie.name),dir.\(movie.director)")

4 }elseifletsong=itemas?Song{

5 print("Song:\(song.name),by\(song.artist)")

6 }

7 }

8

9 //Movie:Casablanca,dir.MichaelCurtiz

10 //Song:BlueSuedeShoes,byElvisPresley

11 //Movie:CitizenKane,dir.OrsonWelles

12 //Song:TheOneAndOnly,byChesneyHawkes

13 //Song:NeverGonnaGiveYouUp,byRickAstley

TheexamplestartsbytryingtodowncastthecurrentitemasaMovie.BecauseitemisaMediaIteminstance,it’spossiblethatitmightbeaMovie;equally,it’salsopossiblethatitmightbeaSong,orevenjustabaseMediaItem.Becauseofthisuncertainty,theas?formofthetypecastoperatorreturnsanoptionalvaluewhenattemptingtodowncasttoasubclasstype.Theresultofitemas?MovieisoftypeMovie?,or“optionalMovie”.

DowncastingtoMoviefailswhenappliedtotheSonginstancesinthelibraryarray.Tocopewiththis,theexampleaboveusesoptionalbindingtocheck

whethertheoptionalMovieactuallycontainsavalue(thatis,tofindoutwhetherthedowncastsucceeded.)Thisoptionalbindingiswritten“ifletmovie=itemas?Movie”,whichcanbereadas:

“TrytoaccessitemasaMovie.Ifthisissuccessful,setanewtemporaryconstantcalledmovietothevaluestoredinthereturnedoptionalMovie.”

Ifthedowncastingsucceeds,thepropertiesofmoviearethenusedtoprintadescriptionforthatMovieinstance,includingthenameofitsdirector.AsimilarprincipleisusedtocheckforSonginstances,andtoprintanappropriatedescription(includingartistname)wheneveraSongisfoundinthelibrary.

NOTE

Castingdoesnotactuallymodifytheinstanceorchangeitsvalues.Theunderlyinginstanceremainsthesame;itissimplytreatedandaccessedasaninstanceofthetypetowhichithasbeencast.

TypeCastingforAnyandAnyObject

Swiftprovidestwospecialtypesforworkingwithnonspecifictypes:

Anycanrepresentaninstanceofanytypeatall,includingfunctiontypes.

AnyObjectcanrepresentaninstanceofanyclasstype.

UseAnyandAnyObjectonlywhenyouexplicitlyneedthebehaviorandcapabilitiestheyprovide.Itisalwaysbettertobespecificaboutthetypesyouexpecttoworkwithinyourcode.

Here’sanexampleofusingAnytoworkwithamixofdifferenttypes,includingfunctiontypesandnonclasstypes.Theexamplecreatesanarraycalledthings,whichcanstorevaluesoftypeAny:

1 varthings=[Any]()

2

3 things.append(0)

4 things.append(0.0)

5 things.append(42)

6 things.append(3.14159)

7 things.append("hello")

8 things.append((3.0,5.0))

9 things.append(Movie(name:"Ghostbusters",director:"Ivan

Reitman"))

10 things.append({(name:String)->Stringin"Hello,\(name)"

})

ThethingsarraycontainstwoIntvalues,twoDoublevalues,aStringvalue,atupleoftype(Double,Double),themovie“Ghostbusters”,andaclosureexpressionthattakesaStringvalueandreturnsanotherStringvalue.

TodiscoverthespecifictypeofaconstantorvariablethatisknownonlytobeoftypeAnyorAnyObject,youcanuseanisoraspatterninaswitchstatement’scases.Theexamplebelowiteratesovertheitemsinthethingsarrayandqueriesthetypeofeachitemwithaswitchstatement.Severaloftheswitchstatement’scasesbindtheirmatchedvaluetoaconstantofthespecifiedtypetoenableitsvaluetobeprinted:

1 forthinginthings{

2 switchthing{

3 case0asInt:

4 print("zeroasanInt")

5 case0asDouble:

6 print("zeroasaDouble")

7 caseletsomeIntasInt:

8 print("anintegervalueof\(someInt)")

9 caseletsomeDoubleasDoublewheresomeDouble>0:

10 print("apositivedoublevalueof\(someDouble)")

11 caseisDouble:

12 print("someotherdoublevaluethatIdon'twantto

print")

13 caseletsomeStringasString:

14 print("astringvalueof\"\(someString)\"")

15 caselet(x,y)as(Double,Double):

16 print("an(x,y)pointat\(x),\(y)")

17 caseletmovieasMovie:

18 print("amoviecalled\(movie.name),dir.\

(movie.director)")

19 caseletstringConverteras(String)->String:

20 print(stringConverter("Michael"))

21 default:

22 print("somethingelse")

23 }

24 }

25

26 //zeroasanInt

27 //zeroasaDouble

28 //anintegervalueof42

29 //apositivedoublevalueof3.14159

30 //astringvalueof"hello"

31 //an(x,y)pointat3.0,5.0

32 //amoviecalledGhostbusters,dir.IvanReitman

33 //Hello,Michael

NOTE

TheAnytyperepresentsvaluesofanytype,includingoptionaltypes.SwiftgivesyouawarningifyouuseanoptionalvaluewhereavalueoftypeAnyisexpected.IfyoureallydoneedtouseanoptionalvalueasanAnyvalue,youcanusetheasoperatortoexplicitlycasttheoptionaltoAny,asshownbelow.

1 letoptionalNumber:Int?=3

2 things.append(optionalNumber)//Warning

3 things.append(optionalNumberasAny)//Nowarning

NestedTypes

Enumerationsareoftencreatedtosupportaspecificclassorstructure’sfunctionality.Similarly,itcanbeconvenienttodefineutilityclassesandstructurespurelyforusewithinthecontextofamorecomplextype.Toaccomplishthis,Swiftenablesyoutodefinenestedtypes,wherebyyounestsupportingenumerations,classes,andstructureswithinthedefinitionofthetypetheysupport.

Tonestatypewithinanothertype,writeitsdefinitionwithintheouterbracesofthetypeitsupports.Typescanbenestedtoasmanylevelsasarerequired.

NestedTypesinAction

TheexamplebelowdefinesastructurecalledBlackjackCard,whichmodelsaplayingcardasusedinthegameofBlackjack.TheBlackjackCardstructurecontainstwonestedenumerationtypescalledSuitandRank.

InBlackjack,theAcecardshaveavalueofeitheroneoreleven.ThisfeatureisrepresentedbyastructurecalledValues,whichisnestedwithintheRankenumeration:

1 structBlackjackCard{

2

3 //nestedSuitenumeration

4 enumSuit:Character{

5 casespades="♠",hearts="♡",diamonds="♢",clubs

="♣"

6 }

7

8 //nestedRankenumeration

9 enumRank:Int{

10 casetwo=2,three,four,five,six,seven,eight,

nine,ten

11 casejack,queen,king,ace

12 structValues{

13 letfirst:Int,second:Int?

14 }

15 varvalues:Values{

16 switchself{

17 case.ace:

18 returnValues(first:1,second:11)

19 case.jack,.queen,.king:

20 returnValues(first:10,second:nil)

21 default:

22 returnValues(first:self.rawValue,second:

nil)

23 }

24 }

25 }

26

27 //BlackjackCardpropertiesandmethods

28 letrank:Rank,suit:Suit

29 vardescription:String{

30 varoutput="suitis\(suit.rawValue),"

31 output+="valueis\(rank.values.first)"

32 ifletsecond=rank.values.second{

33 output+="or\(second)"

34 }

35 returnoutput

36 }

37 }

TheSuitenumerationdescribesthefourcommonplayingcardsuits,togetherwitharawCharactervaluetorepresenttheirsymbol.

TheRankenumerationdescribesthethirteenpossibleplayingcardranks,togetherwitharawIntvaluetorepresenttheirfacevalue.(ThisrawIntvalueisnotusedfortheJack,Queen,King,andAcecards.)

Asmentionedabove,theRankenumerationdefinesafurthernestedstructureofitsown,calledValues.Thisstructureencapsulatesthefactthatmostcardshaveonevalue,buttheAcecardhastwovalues.TheValuesstructuredefinestwopropertiestorepresentthis:

first,oftypeInt

second,oftypeInt?,or“optionalInt”

Rankalsodefinesacomputedproperty,values,whichreturnsaninstanceoftheValuesstructure.ThiscomputedpropertyconsiderstherankofthecardandinitializesanewValuesinstancewithappropriatevaluesbasedonitsrank.Itusesspecialvaluesforjack,queen,king,andace.Forthenumericcards,itusestherank’srawIntvalue.

TheBlackjackCardstructureitselfhastwoproperties—rankandsuit.Italsodefinesacomputedpropertycalleddescription,whichusesthevaluesstoredinrankandsuittobuildadescriptionofthenameandvalueofthecard.Thedescriptionpropertyusesoptionalbindingtocheckwhetherthereisasecondvaluetodisplay,andifso,insertsadditionaldescriptiondetailforthatsecondvalue.

BecauseBlackjackCardisastructurewithnocustominitializers,ithasanimplicitmemberwiseinitializer,asdescribedinMemberwiseInitializersforStructureTypes.YoucanusethisinitializertoinitializeanewconstantcalledtheAceOfSpades:

1 lettheAceOfSpades=BlackjackCard(rank:.ace,suit:.spades)

2 print("theAceOfSpades:\(theAceOfSpades.description)")

3 //Prints"theAceOfSpades:suitis♠,valueis1or11"

EventhoughRankandSuitarenestedwithinBlackjackCard,theirtypecanbeinferredfromcontext,andsotheinitializationofthisinstanceisabletorefertotheenumerationcasesbytheircasenames(.aceand.spades)alone.Intheexampleabove,thedescriptionpropertycorrectlyreportsthattheAceofSpadeshasavalueof1or11.

ReferringtoNestedTypes

Touseanestedtypeoutsideofitsdefinitioncontext,prefixitsnamewiththenameofthetypeitisnestedwithin:

1 letheartsSymbol=BlackjackCard.Suit.hearts.rawValue

2 //heartsSymbolis"♡"

Fortheexampleabove,thisenablesthenamesofSuit,Rank,andValuestobekeptdeliberatelyshort,becausetheirnamesarenaturallyqualifiedbythecontextinwhichtheyaredefined.

Extensions

Extensionsaddnewfunctionalitytoanexistingclass,structure,enumeration,orprotocoltype.Thisincludestheabilitytoextendtypesforwhichyoudonothaveaccesstotheoriginalsourcecode(knownasretroactivemodeling).ExtensionsaresimilartocategoriesinObjective-C.(UnlikeObjective-Ccategories,Swiftextensionsdonothavenames.)

ExtensionsinSwiftcan:

Addcomputedinstancepropertiesandcomputedtypeproperties

Defineinstancemethodsandtypemethods

Providenewinitializers

Definesubscripts

Defineandusenewnestedtypes

Makeanexistingtypeconformtoaprotocol

InSwift,youcanevenextendaprotocoltoprovideimplementationsofitsrequirementsoraddadditionalfunctionalitythatconformingtypescantakeadvantageof.Formoredetails,seeProtocolExtensions.

NOTE

Extensionscanaddnewfunctionalitytoatype,buttheycannotoverrideexistingfunctionality.

ExtensionSyntax

Declareextensionswiththeextensionkeyword:

1 extensionSomeType{

2 //newfunctionalitytoaddtoSomeTypegoeshere

3 }

Anextensioncanextendanexistingtypetomakeitadoptoneormoreprotocols.Toaddprotocolconformance,youwritetheprotocolnamesthesamewayasyouwritethemforaclassorstructure:

1 extensionSomeType:SomeProtocol,AnotherProtocol{

2 //implementationofprotocolrequirementsgoeshere

3 }

AddingprotocolconformanceinthiswayisdescribedinAddingProtocolConformancewithanExtension.

Anextensioncanbeusedtoextendanexistinggenerictype,asdescribedinExtendingaGenericType.Youcanalsoextendagenerictypetoconditionallyaddfunctionality,asdescribedinExtensionswithaGenericWhereClause.

NOTE

Ifyoudefineanextensiontoaddnewfunctionalitytoanexistingtype,thenewfunctionalitywillbeavailableonallexistinginstancesofthattype,eveniftheywerecreatedbeforetheextensionwasdefined.

ComputedProperties

Extensionscanaddcomputedinstancepropertiesandcomputedtypepropertiestoexistingtypes.ThisexampleaddsfivecomputedinstancepropertiestoSwift’sbuilt-inDoubletype,toprovidebasicsupportforworkingwithdistanceunits:

1 extensionDouble{

2 varkm:Double{returnself*1_000.0}

3 varm:Double{returnself}

4 varcm:Double{returnself/100.0}

5 varmm:Double{returnself/1_000.0}

6 varft:Double{returnself/3.28084}

7 }

8 letoneInch=25.4.mm

9 print("Oneinchis\(oneInch)meters")

10 //Prints"Oneinchis0.0254meters"

11 letthreeFeet=3.ft

12 print("Threefeetis\(threeFeet)meters")

13 //Prints"Threefeetis0.914399970739201meters"

ThesecomputedpropertiesexpressthataDoublevalueshouldbeconsideredasacertainunitoflength.Althoughtheyareimplementedascomputedproperties,thenamesofthesepropertiescanbeappendedtoafloating-pointliteralvaluewithdotsyntax,asawaytousethatliteralvaluetoperformdistanceconversions.

Inthisexample,aDoublevalueof1.0isconsideredtorepresent“onemeter”.Thisiswhythemcomputedpropertyreturnsself—theexpression1.misconsideredtocalculateaDoublevalueof1.0.

Otherunitsrequiresomeconversiontobeexpressedasavaluemeasuredinmeters.Onekilometeristhesameas1,000meters,sothekmcomputedpropertymultipliesthevalueby1_000.00toconvertintoanumberexpressedinmeters.Similarly,thereare3.28084feetinameter,andsotheftcomputedpropertydividestheunderlyingDoublevalueby3.28084,toconvertitfromfeettometers.

Thesepropertiesareread-onlycomputedproperties,andsotheyareexpressedwithoutthegetkeyword,forbrevity.TheirreturnvalueisoftypeDouble,andcanbeusedwithinmathematicalcalculationswhereveraDoubleisaccepted:

1 letaMarathon=42.km+195.m

2 print("Amarathonis\(aMarathon)meterslong")

3 //Prints"Amarathonis42195.0meterslong"

NOTE

Extensionscanaddnewcomputedproperties,buttheycannotaddstoredproperties,oraddpropertyobserverstoexistingproperties.

Initializers

Extensionscanaddnewinitializerstoexistingtypes.Thisenablesyoutoextendothertypestoacceptyourowncustomtypesasinitializerparameters,ortoprovideadditionalinitializationoptionsthatwerenotincludedaspartofthetype’soriginalimplementation.

Extensionscanaddnewconvenienceinitializerstoaclass,buttheycannotaddnewdesignatedinitializersordeinitializerstoaclass.Designatedinitializersanddeinitializersmustalwaysbeprovidedbytheoriginalclassimplementation.

Ifyouuseanextensiontoaddaninitializertoavaluetypethatprovidesdefaultvaluesforallofitsstoredpropertiesanddoesnotdefineanycustominitializers,youcancallthedefaultinitializerandmemberwiseinitializerforthatvaluetypefromwithinyourextension’sinitializer.Thiswouldn’tbethecaseifyouhadwrittentheinitializeraspartofthevaluetype’soriginalimplementation,asdescribedinInitializerDelegationforValueTypes.

Ifyouuseanextensiontoaddaninitializertoastructurethatwasdeclaredinanothermodule,thenewinitializercan’taccessselfuntilitcallsaninitializerfromthedefiningmodule.

TheexamplebelowdefinesacustomRectstructuretorepresentageometricrectangle.TheexamplealsodefinestwosupportingstructurescalledSizeandPoint,bothofwhichprovidedefaultvaluesof0.0foralloftheirproperties:

1 structSize{

2 varwidth=0.0,height=0.0

3 }

4 structPoint{

5 varx=0.0,y=0.0

6 }

7 structRect{

8 varorigin=Point()

9 varsize=Size()

10 }

BecausetheRectstructureprovidesdefaultvaluesforallofitsproperties,itreceivesadefaultinitializerandamemberwiseinitializerautomatically,asdescribedinDefaultInitializers.TheseinitializerscanbeusedtocreatenewRectinstances:

1 letdefaultRect=Rect()

2 letmemberwiseRect=Rect(origin:Point(x:2.0,y:2.0),

3 size:Size(width:5.0,height:5.0))

YoucanextendtheRectstructuretoprovideanadditionalinitializerthattakesaspecificcenterpointandsize:

1 extensionRect{

2 init(center:Point,size:Size){

3 letoriginX=center.x-(size.width/2)

4 letoriginY=center.y-(size.height/2)

5 self.init(origin:Point(x:originX,y:originY),size:

size)

6 }

7 }

Thisnewinitializerstartsbycalculatinganappropriateoriginpointbasedontheprovidedcenterpointandsizevalue.Theinitializerthencallsthestructure’sautomaticmemberwiseinitializerinit(origin:size:),whichstorestheneworiginandsizevaluesintheappropriateproperties:

1 letcenterRect=Rect(center:Point(x:4.0,y:4.0),

2 size:Size(width:3.0,height:3.0))

3 //centerRect'soriginis(2.5,2.5)anditssizeis(3.0,3.0)

NOTE

Ifyouprovideanewinitializerwithanextension,youarestillresponsibleformakingsurethateachinstanceisfullyinitializedoncetheinitializercompletes.

Methods

Extensionscanaddnewinstancemethodsandtypemethodstoexistingtypes.ThefollowingexampleaddsanewinstancemethodcalledrepetitionstotheInttype:

1 extensionInt{

2 funcrepetitions(task:()->Void){

3 for_in0..<self{

4 task()

5 }

6 }

7 }

Therepetitions(task:)methodtakesasingleargumentoftype()->Void,whichindicatesafunctionthathasnoparametersanddoesnotreturnavalue.

Afterdefiningthisextension,youcancalltherepetitions(task:)methodonanyintegertoperformataskthatmanynumberoftimes:

1 3.repetitions{

2 print("Hello!")

3 }

4 //Hello!

5 //Hello!

6 //Hello!

MutatingInstanceMethodsInstancemethodsaddedwithanextensioncanalsomodify(ormutate)theinstanceitself.Structureandenumerationmethodsthatmodifyselforitspropertiesmustmarktheinstancemethodasmutating,justlikemutatingmethodsfromanoriginalimplementation.

TheexamplebelowaddsanewmutatingmethodcalledsquaretoSwift’sInttype,whichsquarestheoriginalvalue:

1 extensionInt{

2 mutatingfuncsquare(){

3 self=self*self

4 }

5 }

6 varsomeInt=3

7 someInt.square()

8 //someIntisnow9

Subscripts

Extensionscanaddnewsubscriptstoanexistingtype.ThisexampleaddsanintegersubscripttoSwift’sbuilt-inInttype.Thissubscript[n]returnsthedecimaldigitnplacesinfromtherightofthenumber:

123456789[0]returns9

123456789[1]returns8

…andsoon:

1 extensionInt{

2 subscript(digitIndex:Int)->Int{

3 vardecimalBase=1

4 for_in0..<digitIndex{

5 decimalBase*=10

6 }

7 return(self/decimalBase)%10

8 }

9 }

10 746381295[0]

11 //returns5

12 746381295[1]

13 //returns9

14 746381295[2]

15 //returns2

16 746381295[8]

17 //returns7

IftheIntvaluedoesnothaveenoughdigitsfortherequestedindex,thesubscriptimplementationreturns0,asifthenumberhadbeenpaddedwithzerostotheleft:

1 746381295[9]

2 //returns0,asifyouhadrequested:

3 0746381295[9]

NestedTypes

Extensionscanaddnewnestedtypestoexistingclasses,structures,andenumerations:

1 extensionInt{

2 enumKind{

3 casenegative,zero,positive

4 }

5 varkind:Kind{

6 switchself{

7 case0:

8 return.zero

9 caseletxwherex>0:

10 return.positive

11 default:

12 return.negative

13 }

14 }

15 }

ThisexampleaddsanewnestedenumerationtoInt.Thisenumeration,calledKind,expressesthekindofnumberthataparticularintegerrepresents.Specifically,itexpresseswhetherthenumberisnegative,zero,orpositive.

ThisexamplealsoaddsanewcomputedinstancepropertytoInt,calledkind,whichreturnstheappropriateKindenumerationcaseforthatinteger.

ThenestedenumerationcannowbeusedwithanyIntvalue:

1 funcprintIntegerKinds(_numbers:[Int]){

2 fornumberinnumbers{

3 switchnumber.kind{

4 case.negative:

5 print("-",terminator:"")

6 case.zero:

7 print("0",terminator:"")

8 case.positive:

9 print("+",terminator:"")

10 }

11 }

12 print("")

13 }

14 printIntegerKinds([3,19,-27,0,-6,0,7])

15 //Prints"++-0-0+"

Thisfunction,printIntegerKinds(_:),takesaninputarrayofIntvaluesanditeratesoverthosevaluesinturn.Foreachintegerinthearray,thefunctionconsidersthekindcomputedpropertyforthatinteger,andprintsanappropriatedescription.

NOTE

number.kindisalreadyknowntobeoftypeInt.Kind.Becauseofthis,alloftheInt.Kindcasevaluescanbewritteninshorthandforminsidetheswitchstatement,suchas.negativeratherthanInt.Kind.negative.

Protocols

Aprotocoldefinesablueprintofmethods,properties,andotherrequirementsthatsuitaparticulartaskorpieceoffunctionality.Theprotocolcanthenbeadoptedbyaclass,structure,orenumerationtoprovideanactualimplementationofthoserequirements.Anytypethatsatisfiestherequirementsofaprotocolissaidtoconformtothatprotocol.

Inadditiontospecifyingrequirementsthatconformingtypesmustimplement,youcanextendaprotocoltoimplementsomeoftheserequirementsortoimplementadditionalfunctionalitythatconformingtypescantakeadvantageof.

ProtocolSyntax

Youdefineprotocolsinaverysimilarwaytoclasses,structures,andenumerations:

1 protocolSomeProtocol{

2 //protocoldefinitiongoeshere

3 }

Customtypesstatethattheyadoptaparticularprotocolbyplacingtheprotocol’snameafterthetype’sname,separatedbyacolon,aspartoftheirdefinition.Multipleprotocolscanbelisted,andareseparatedbycommas:

1 structSomeStructure:FirstProtocol,AnotherProtocol{

2 //structuredefinitiongoeshere

3 }

Ifaclasshasasuperclass,listthesuperclassnamebeforeanyprotocolsitadopts,followedbyacomma:

1 classSomeClass:SomeSuperclass,FirstProtocol,AnotherProtocol

{

2 //classdefinitiongoeshere

3 }

PropertyRequirements

Aprotocolcanrequireanyconformingtypetoprovideaninstancepropertyortypepropertywithaparticularnameandtype.Theprotocoldoesn’tspecifywhetherthepropertyshouldbeastoredpropertyoracomputedproperty—itonlyspecifiestherequiredpropertynameandtype.Theprotocolalsospecifieswhethereachpropertymustbegettableorgettableandsettable.

Ifaprotocolrequiresapropertytobegettableandsettable,thatpropertyrequirementcan’tbefulfilledbyaconstantstoredpropertyoraread-onlycomputedproperty.Iftheprotocolonlyrequiresapropertytobegettable,therequirementcanbesatisfiedbyanykindofproperty,andit’svalidforthepropertytobealsosettableifthisisusefulforyourowncode.

Propertyrequirementsarealwaysdeclaredasvariableproperties,prefixedwiththevarkeyword.Gettableandsettablepropertiesareindicatedbywriting{getset}aftertheirtypedeclaration,andgettablepropertiesareindicatedbywriting{get}.

1 protocolSomeProtocol{

2 varmustBeSettable:Int{getset}

3 vardoesNotNeedToBeSettable:Int{get}

4 }

Alwaysprefixtypepropertyrequirementswiththestatickeywordwhenyoudefinetheminaprotocol.Thisrulepertainseventhoughtypepropertyrequirementscanbeprefixedwiththeclassorstatickeywordwhenimplementedbyaclass:

1 protocolAnotherProtocol{

2 staticvarsomeTypeProperty:Int{getset}

3 }

Here’sanexampleofaprotocolwithasingleinstancepropertyrequirement:

1 protocolFullyNamed{

2 varfullName:String{get}

3 }

TheFullyNamedprotocolrequiresaconformingtypetoprovideafullyqualifiedname.Theprotocoldoesn’tspecifyanythingelseaboutthenatureoftheconformingtype—itonlyspecifiesthatthetypemustbeabletoprovideafullnameforitself.TheprotocolstatesthatanyFullyNamedtypemusthaveagettableinstancepropertycalledfullName,whichisoftypeString.

Here’sanexampleofasimplestructurethatadoptsandconformstotheFullyNamedprotocol:

1 structPerson:FullyNamed{

2 varfullName:String

3 }

4 letjohn=Person(fullName:"JohnAppleseed")

5 //john.fullNameis"JohnAppleseed"

ThisexampledefinesastructurecalledPerson,whichrepresentsaspecificnamedperson.ItstatesthatitadoptstheFullyNamedprotocolaspartofthefirstlineofitsdefinition.

EachinstanceofPersonhasasinglestoredpropertycalledfullName,whichisoftypeString.ThismatchesthesinglerequirementoftheFullyNamedprotocol,andmeansthatPersonhascorrectlyconformedtotheprotocol.(Swiftreportsanerroratcompile-timeifaprotocolrequirementisnotfulfilled.)

Here’samorecomplexclass,whichalsoadoptsandconformstotheFullyNamed

protocol:

1 classStarship:FullyNamed{

2 varprefix:String?

3 varname:String

4 init(name:String,prefix:String?=nil){

5 self.name=name

6 self.prefix=prefix

7 }

8 varfullName:String{

9 return(prefix!=nil?prefix!+"":"")+name

10 }

11 }

12 varncc1701=Starship(name:"Enterprise",prefix:"USS")

13 //ncc1701.fullNameis"USSEnterprise"

ThisclassimplementsthefullNamepropertyrequirementasacomputedread-onlypropertyforastarship.EachStarshipclassinstancestoresamandatorynameandanoptionalprefix.ThefullNamepropertyusestheprefixvalueifitexists,andprependsittothebeginningofnametocreateafullnameforthestarship.

MethodRequirements

Protocolscanrequirespecificinstancemethodsandtypemethodstobeimplementedbyconformingtypes.Thesemethodsarewrittenaspartoftheprotocol’sdefinitioninexactlythesamewayasfornormalinstanceandtypemethods,butwithoutcurlybracesoramethodbody.Variadicparametersareallowed,subjecttothesamerulesasfornormalmethods.Defaultvalues,however,can’tbespecifiedformethodparameterswithinaprotocol’sdefinition.

Aswithtypepropertyrequirements,youalwaysprefixtypemethod

requirementswiththestatickeywordwhenthey’redefinedinaprotocol.Thisistrueeventhoughtypemethodrequirementsareprefixedwiththeclassorstatickeywordwhenimplementedbyaclass:

1 protocolSomeProtocol{

2 staticfuncsomeTypeMethod()

3 }

Thefollowingexampledefinesaprotocolwithasingleinstancemethodrequirement:

1 protocolRandomNumberGenerator{

2 funcrandom()->Double

3 }

Thisprotocol,RandomNumberGenerator,requiresanyconformingtypetohaveaninstancemethodcalledrandom,whichreturnsaDoublevaluewheneverit’scalled.Althoughit’snotspecifiedaspartoftheprotocol,it’sassumedthatthisvaluewillbeanumberfrom0.0upto(butnotincluding)1.0.

TheRandomNumberGeneratorprotocoldoesn’tmakeanyassumptionsabouthoweachrandomnumberwillbegenerated—itsimplyrequiresthegeneratortoprovideastandardwaytogenerateanewrandomnumber.

Here’sanimplementationofaclassthatadoptsandconformstotheRandomNumberGeneratorprotocol.Thisclassimplementsapseudorandomnumbergeneratoralgorithmknownasalinearcongruentialgenerator:

1 classLinearCongruentialGenerator:RandomNumberGenerator{

2 varlastRandom=42.0

3 letm=139968.0

4 leta=3877.0

5 letc=29573.0

6 funcrandom()->Double{

7 lastRandom=((lastRandom*a+c)

8 .truncatingRemainder(dividingBy:m))

9 returnlastRandom/m

10 }

11 }

12 letgenerator=LinearCongruentialGenerator()

13 print("Here'sarandomnumber:\(generator.random())")

14 //Prints"Here'sarandomnumber:0.3746499199817101"

15 print("Andanotherone:\(generator.random())")

16 //Prints"Andanotherone:0.729023776863283"

MutatingMethodRequirements

It’ssometimesnecessaryforamethodtomodify(ormutate)theinstanceitbelongsto.Forinstancemethodsonvaluetypes(thatis,structuresandenumerations)youplacethemutatingkeywordbeforeamethod’sfunckeywordtoindicatethatthemethodisallowedtomodifytheinstanceitbelongstoandanypropertiesofthatinstance.ThisprocessisdescribedinModifyingValueTypesfromWithinInstanceMethods.

Ifyoudefineaprotocolinstancemethodrequirementthatisintendedtomutateinstancesofanytypethatadoptstheprotocol,markthemethodwiththemutatingkeywordaspartoftheprotocol’sdefinition.Thisenablesstructuresandenumerationstoadopttheprotocolandsatisfythatmethodrequirement.

NOTE

Ifyoumarkaprotocolinstancemethodrequirementasmutating,youdon’tneedtowritethemutatingkeywordwhenwritinganimplementationofthatmethodforaclass.Themutatingkeywordisonlyusedbystructuresandenumerations.

TheexamplebelowdefinesaprotocolcalledTogglable,whichdefinesasingleinstancemethodrequirementcalledtoggle.Asitsnamesuggests,thetoggle()

methodisintendedtotoggleorinvertthestateofanyconformingtype,typicallybymodifyingapropertyofthattype.

Thetoggle()methodismarkedwiththemutatingkeywordaspartoftheTogglableprotocoldefinition,toindicatethatthemethodisexpectedtomutatethestateofaconforminginstancewhenit’scalled:

1 protocolTogglable{

2 mutatingfunctoggle()

3 }

IfyouimplementtheTogglableprotocolforastructureorenumeration,thatstructureorenumerationcanconformtotheprotocolbyprovidinganimplementationofthetoggle()methodthatisalsomarkedasmutating.

TheexamplebelowdefinesanenumerationcalledOnOffSwitch.Thisenumerationtogglesbetweentwostates,indicatedbytheenumerationcasesonandoff.Theenumeration’stoggleimplementationismarkedasmutating,tomatchtheTogglableprotocol’srequirements:

1 enumOnOffSwitch:Togglable{

2 caseoff,on

3 mutatingfunctoggle(){

4 switchself{

5 case.off:

6 self=.on

7 case.on:

8 self=.off

9 }

10 }

11 }

12 varlightSwitch=OnOffSwitch.off

13 lightSwitch.toggle()

14 //lightSwitchisnowequalto.on

InitializerRequirements

Protocolscanrequirespecificinitializerstobeimplementedbyconformingtypes.Youwritetheseinitializersaspartoftheprotocol’sdefinitioninexactlythesamewayasfornormalinitializers,butwithoutcurlybracesoraninitializerbody:

1 protocolSomeProtocol{

2 init(someParameter:Int)

3 }

ClassImplementationsofProtocolInitializerRequirementsYoucanimplementaprotocolinitializerrequirementonaconformingclassaseitheradesignatedinitializeroraconvenienceinitializer.Inbothcases,youmustmarktheinitializerimplementationwiththerequiredmodifier:

1 classSomeClass:SomeProtocol{

2 requiredinit(someParameter:Int){

3 //initializerimplementationgoeshere

4 }

5 }

Theuseoftherequiredmodifierensuresthatyouprovideanexplicitorinheritedimplementationoftheinitializerrequirementonallsubclassesoftheconformingclass,suchthattheyalsoconformtotheprotocol.

Formoreinformationonrequiredinitializers,seeRequiredInitializers.

NOTE

Youdon’tneedtomarkprotocolinitializerimplementationswiththerequiredmodifieronclassesthataremarkedwiththefinalmodifier,becausefinalclassescan’tsubclassed.Formoreaboutthefinalmodifier,seePreventingOverrides.

Ifasubclassoverridesadesignatedinitializerfromasuperclass,andalsoimplementsamatchinginitializerrequirementfromaprotocol,marktheinitializerimplementationwithboththerequiredandoverridemodifiers:

1 protocolSomeProtocol{

2 init()

3 }

4

5 classSomeSuperClass{

6 init(){

7 //initializerimplementationgoeshere

8 }

9 }

10

11 classSomeSubClass:SomeSuperClass,SomeProtocol{

12 //"required"fromSomeProtocolconformance;"override"

fromSomeSuperClass

13 requiredoverrideinit(){

14 //initializerimplementationgoeshere

15 }

16 }

FailableInitializerRequirementsProtocolscandefinefailableinitializerrequirementsforconformingtypes,asdefinedinFailableInitializers.

Afailableinitializerrequirementcanbesatisfiedbyafailableornonfailable

initializeronaconformingtype.Anonfailableinitializerrequirementcanbesatisfiedbyanonfailableinitializeroranimplicitlyunwrappedfailableinitializer.

ProtocolsasTypes

Protocolsdon’tactuallyimplementanyfunctionalitythemselves.Nonetheless,youcanuseprotocolsasafullyfledgedtypesinyourcode.Usingaprotocolasatypeissometimescalledanexistentialtype,whichcomesfromthephrase“thereexistsatypeTsuchthatTconformstotheprotocol”.

Youcanuseaprotocolinmanyplaceswhereothertypesareallowed,including:

Asaparametertypeorreturntypeinafunction,method,orinitializer

Asthetypeofaconstant,variable,orproperty

Asthetypeofitemsinanarray,dictionary,orothercontainer

NOTE

Becauseprotocolsaretypes,begintheirnameswithacapitalletter(suchasFullyNamedandRandomNumberGenerator)tomatchthenamesofothertypesinSwift(suchasInt,String,andDouble).

Here’sanexampleofaprotocolusedasatype:

1 classDice{

2 letsides:Int

3 letgenerator:RandomNumberGenerator

4 init(sides:Int,generator:RandomNumberGenerator){

5 self.sides=sides

6 self.generator=generator

7 }

8 funcroll()->Int{

9 returnInt(generator.random()*Double(sides))+1

10 }

11 }

ThisexampledefinesanewclasscalledDice,whichrepresentsann-sideddiceforuseinaboardgame.Diceinstanceshaveanintegerpropertycalledsides,whichrepresentshowmanysidestheyhave,andapropertycalledgenerator,whichprovidesarandomnumbergeneratorfromwhichtocreatedicerollvalues.

ThegeneratorpropertyisoftypeRandomNumberGenerator.Therefore,youcansetittoaninstanceofanytypethatadoptstheRandomNumberGeneratorprotocol.Nothingelseisrequiredoftheinstanceyouassigntothisproperty,exceptthattheinstancemustadopttheRandomNumberGeneratorprotocol.BecauseitstypeisRandomNumberGenerator,codeinsidetheDiceclasscanonlyinteractwithgeneratorinwaysthatapplytoallgeneratorsthatconformtothisprotocol.Thatmeansitcan’tuseanymethodsorpropertiesthataredefinedbytheunderlyingtypeofthegenerator.However,youcandowncastfromaprotocoltypetoanunderlyingtypeinthesamewayyoucandowncastfromasuperclasstoasubclass,asdiscussedinDowncasting.

Dicealsohasaninitializer,tosetupitsinitialstate.Thisinitializerhasaparametercalledgenerator,whichisalsooftypeRandomNumberGenerator.YoucanpassavalueofanyconformingtypeintothisparameterwheninitializinganewDiceinstance.

Diceprovidesoneinstancemethod,roll,whichreturnsanintegervaluebetween1andthenumberofsidesonthedice.Thismethodcallsthegenerator’srandom()methodtocreateanewrandomnumberbetween0.0and1.0,andusesthisrandomnumbertocreateadicerollvaluewithinthecorrectrange.BecausegeneratorisknowntoadoptRandomNumberGenerator,it’sguaranteedtohavearandom()methodtocall.

Here’showtheDiceclasscanbeusedtocreateasix-sideddicewithaLinearCongruentialGeneratorinstanceasitsrandomnumbergenerator:

1 vard6=Dice(sides:6,generator:

LinearCongruentialGenerator())

2 for_in1...5{

3 print("Randomdicerollis\(d6.roll())")

4 }

5 //Randomdicerollis3

6 //Randomdicerollis5

7 //Randomdicerollis4

8 //Randomdicerollis5

9 //Randomdicerollis4

Delegation

Delegationisadesignpatternthatenablesaclassorstructuretohandoff(ordelegate)someofitsresponsibilitiestoaninstanceofanothertype.Thisdesignpatternisimplementedbydefiningaprotocolthatencapsulatesthedelegatedresponsibilities,suchthataconformingtype(knownasadelegate)isguaranteedtoprovidethefunctionalitythathasbeendelegated.Delegationcanbeusedtorespondtoaparticularaction,ortoretrievedatafromanexternalsourcewithoutneedingtoknowtheunderlyingtypeofthatsource.

Theexamplebelowdefinestwoprotocolsforusewithdice-basedboardgames:

1 protocolDiceGame{

2 vardice:Dice{get}

3 funcplay()

4 }

5 protocolDiceGameDelegate:AnyObject{

6 funcgameDidStart(_game:DiceGame)

7 funcgame(_game:DiceGame,didStartNewTurnWithDiceRoll

diceRoll:Int)

8 funcgameDidEnd(_game:DiceGame)

9 }

TheDiceGameprotocolisaprotocolthatcanbeadoptedbyanygamethatinvolvesdice.

TheDiceGameDelegateprotocolcanbeadoptedtotracktheprogressofaDiceGame.Topreventstrongreferencecycles,delegatesaredeclaredasweakreferences.Forinformationaboutweakreferences,seeStrongReferenceCyclesBetweenClassInstances.Markingtheprotocolasclass-onlyletstheSnakesAndLaddersclasslaterinthischapterdeclarethatitsdelegatemustuseaweakreference.Aclass-onlyprotocolismarkedbyitsinheritancefromAnyObject,asdiscussedinClass-OnlyProtocols.

Here’saversionoftheSnakesandLaddersgameoriginallyintroducedinControlFlow.ThisversionisadaptedtouseaDiceinstanceforitsdice-rolls;toadopttheDiceGameprotocol;andtonotifyaDiceGameDelegateaboutitsprogress:

1 classSnakesAndLadders:DiceGame{

2 letfinalSquare=25

3 letdice=Dice(sides:6,generator:

LinearCongruentialGenerator())

4 varsquare=0

5 varboard:[Int]

6 init(){

7 board=Array(repeating:0,count:finalSquare+1)

8 board[03]=+08;board[06]=+11;board[09]=+09;

board[10]=+02

9 board[14]=-10;board[19]=-11;board[22]=-02;

board[24]=-08

10 }

11 weakvardelegate:DiceGameDelegate?

12 funcplay(){

13 square=0

14 delegate?.gameDidStart(self)

15 gameLoop:whilesquare!=finalSquare{

16 letdiceRoll=dice.roll()

17 delegate?.game(self,didStartNewTurnWithDiceRoll:

diceRoll)

18 switchsquare+diceRoll{

19 casefinalSquare:

20 breakgameLoop

21 caseletnewSquarewherenewSquare>finalSquare:

22 continuegameLoop

23 default:

24 square+=diceRoll

25 square+=board[square]

26 }

27 }

28 delegate?.gameDidEnd(self)

29 }

30 }

ForadescriptionoftheSnakesandLaddersgameplay,seeBreak.

ThisversionofthegameiswrappedupasaclasscalledSnakesAndLadders,whichadoptstheDiceGameprotocol.Itprovidesagettabledicepropertyandaplay()methodinordertoconformtotheprotocol.(Thedicepropertyisdeclaredasaconstantpropertybecauseitdoesn’tneedtochangeafterinitialization,andtheprotocolonlyrequiresthatitmustbegettable.)

TheSnakesandLaddersgameboardsetuptakesplacewithintheclass’sinit()initializer.Allgamelogicismovedintotheprotocol’splaymethod,whichusestheprotocol’srequireddicepropertytoprovideitsdicerollvalues.

NotethatthedelegatepropertyisdefinedasanoptionalDiceGameDelegate,becauseadelegateisn’trequiredinordertoplaythegame.Becauseit’sofanoptionaltype,thedelegatepropertyisautomaticallysettoaninitialvalueofnil.Thereafter,thegameinstantiatorhastheoptiontosetthepropertytoasuitabledelegate.BecausetheDiceGameDelegateprotocolisclass-only,youcandeclarethedelegatetobeweaktopreventreferencecycles.

DiceGameDelegateprovidesthreemethodsfortrackingtheprogressofagame.Thesethreemethodshavebeenincorporatedintothegamelogicwithintheplay()methodabove,andarecalledwhenanewgamestarts,anewturnbegins,orthegameends.

BecausethedelegatepropertyisanoptionalDiceGameDelegate,theplay()methodusesoptionalchainingeachtimeitcallsamethodonthedelegate.Ifthedelegatepropertyisnil,thesedelegatecallsfailgracefullyandwithouterror.Ifthedelegatepropertyisnon-nil,thedelegatemethodsarecalled,andarepassedtheSnakesAndLaddersinstanceasaparameter.

ThisnextexampleshowsaclasscalledDiceGameTracker,whichadoptstheDiceGameDelegateprotocol:

1 classDiceGameTracker:DiceGameDelegate{

2 varnumberOfTurns=0

3 funcgameDidStart(_game:DiceGame){

4 numberOfTurns=0

5 ifgameisSnakesAndLadders{

6 print("StartedanewgameofSnakesandLadders")

7 }

8 print("Thegameisusinga\(game.dice.sides)-sided

dice")

9 }

10 funcgame(_game:DiceGame,didStartNewTurnWithDiceRoll

diceRoll:Int){

11 numberOfTurns+=1

12 print("Rolleda\(diceRoll)")

13 }

14 funcgameDidEnd(_game:DiceGame){

15 print("Thegamelastedfor\(numberOfTurns)turns")

16 }

17 }

DiceGameTrackerimplementsallthreemethodsrequiredbyDiceGameDelegate.Itusesthesemethodstokeeptrackofthenumberofturnsagamehastaken.ItresetsanumberOfTurnspropertytozerowhenthegamestarts,incrementsiteachtimeanewturnbegins,andprintsoutthetotalnumberofturnsoncethegamehasended.

TheimplementationofgameDidStart(_:)shownaboveusesthegameparametertoprintsomeintroductoryinformationaboutthegamethatisabouttobeplayed.ThegameparameterhasatypeofDiceGame,notSnakesAndLadders,andsogameDidStart(_:)canaccessanduseonlymethodsandpropertiesthatareimplementedaspartoftheDiceGameprotocol.However,themethodisstillabletousetypecastingtoquerythetypeoftheunderlyinginstance.Inthisexample,itcheckswhethergameisactuallyaninstanceofSnakesAndLaddersbehindthescenes,andprintsanappropriatemessageifso.

ThegameDidStart(_:)methodalsoaccessesthedicepropertyofthepassedgameparameter.BecausegameisknowntoconformtotheDiceGameprotocol,it’sguaranteedtohaveadiceproperty,andsothegameDidStart(_:)methodisabletoaccessandprintthedice’ssidesproperty,regardlessofwhatkindofgameisbeingplayed.

Here’showDiceGameTrackerlooksinaction:

1 lettracker=DiceGameTracker()

2 letgame=SnakesAndLadders()

3 game.delegate=tracker

4 game.play()

5 //StartedanewgameofSnakesandLadders

6 //Thegameisusinga6-sideddice

7 //Rolleda3

8 //Rolleda5

9 //Rolleda4

10 //Rolleda5

11 //Thegamelastedfor4turns

AddingProtocolConformancewithanExtension

Youcanextendanexistingtypetoadoptandconformtoanewprotocol,evenifyoudon’thaveaccesstothesourcecodefortheexistingtype.Extensionscanaddnewproperties,methods,andsubscriptstoanexistingtype,andarethereforeabletoaddanyrequirementsthataprotocolmaydemand.Formoreaboutextensions,seeExtensions.

NOTE

Existinginstancesofatypeautomaticallyadoptandconformtoaprotocolwhenthatconformanceisaddedtotheinstance’stypeinanextension.

Forexample,thisprotocol,calledTextRepresentable,canbeimplementedbyanytypethathasawaytoberepresentedastext.Thismightbeadescriptionofitself,oratextversionofitscurrentstate:

1 protocolTextRepresentable{

2 vartextualDescription:String{get}

3 }

TheDiceclassfromabovecanbeextendedtoadoptandconformtoTextRepresentable:

1 extensionDice:TextRepresentable{

2 vartextualDescription:String{

3 return"A\(sides)-sideddice"

4 }

5 }

ThisextensionadoptsthenewprotocolinexactlythesamewayasifDicehadprovideditinitsoriginalimplementation.Theprotocolnameisprovidedafterthetypename,separatedbyacolon,andanimplementationofallrequirementsoftheprotocolisprovidedwithintheextension’scurlybraces.

AnyDiceinstancecannowbetreatedasTextRepresentable:

1 letd12=Dice(sides:12,generator:

LinearCongruentialGenerator())

2 print(d12.textualDescription)

3 //Prints"A12-sideddice"

Similarly,theSnakesAndLaddersgameclasscanbeextendedtoadoptandconformtotheTextRepresentableprotocol:

1 extensionSnakesAndLadders:TextRepresentable{

2 vartextualDescription:String{

3 return"AgameofSnakesandLadderswith\

(finalSquare)squares"

4 }

5 }

6 print(game.textualDescription)

7 //Prints"AgameofSnakesandLadderswith25squares"

ConditionallyConformingtoaProtocolAgenerictypemaybeabletosatisfytherequirementsofaprotocolonlyundercertainconditions,suchaswhenthetype’sgenericparameterconformstotheprotocol.Youcanmakeagenerictypeconditionallyconformtoaprotocolby

listingconstraintswhenextendingthetype.Writetheseconstraintsafterthenameoftheprotocolyou’readoptingbywritingagenericwhereclause.Formoreaboutgenericwhereclauses,seeGenericWhereClauses.

ThefollowingextensionmakesArrayinstancesconformtotheTextRepresentableprotocolwhenevertheystoreelementsofatypethatconformstoTextRepresentable.

1 extensionArray:TextRepresentablewhereElement:

TextRepresentable{

2 vartextualDescription:String{

3 letitemsAsText=self.map{$0.textualDescription}

4 return"["+itemsAsText.joined(separator:",")+"]"

5 }

6 }

7 letmyDice=[d6,d12]

8 print(myDice.textualDescription)

9 //Prints"[A6-sideddice,A12-sideddice]"

DeclaringProtocolAdoptionwithanExtensionIfatypealreadyconformstoalloftherequirementsofaprotocol,buthasnotyetstatedthatitadoptsthatprotocol,youcanmakeitadopttheprotocolwithanemptyextension:

1 structHamster{

2 varname:String

3 vartextualDescription:String{

4 return"Ahamsternamed\(name)"

5 }

6 }

7 extensionHamster:TextRepresentable{}

InstancesofHamstercannowbeusedwhereverTextRepresentableistherequiredtype:

1 letsimonTheHamster=Hamster(name:"Simon")

2 letsomethingTextRepresentable:TextRepresentable=

simonTheHamster

3 print(somethingTextRepresentable.textualDescription)

4 //Prints"AhamsternamedSimon"

NOTE

Typesdon’tautomaticallyadoptaprotocoljustbysatisfyingitsrequirements.Theymustalwaysexplicitlydeclaretheiradoptionoftheprotocol.

CollectionsofProtocolTypes

Aprotocolcanbeusedasthetypetobestoredinacollectionsuchasanarrayoradictionary,asmentionedinProtocolsasTypes.ThisexamplecreatesanarrayofTextRepresentablethings:

letthings:[TextRepresentable]=[game,d12,simonTheHamster]

It’snowpossibletoiterateovertheitemsinthearray,andprinteachitem’stextualdescription:

1 forthinginthings{

2 print(thing.textualDescription)

3 }

4 //AgameofSnakesandLadderswith25squares

5 //A12-sideddice

6 //AhamsternamedSimon

NotethatthethingconstantisoftypeTextRepresentable.It’snotoftypeDice,orDiceGame,orHamster,eveniftheactualinstancebehindthescenesisofoneofthosetypes.Nonetheless,becauseit’softypeTextRepresentable,andanythingthatisTextRepresentableisknowntohaveatextualDescriptionproperty,it’ssafetoaccessthing.textualDescriptioneachtimethroughtheloop.

ProtocolInheritance

Aprotocolcaninheritoneormoreotherprotocolsandcanaddfurtherrequirementsontopoftherequirementsitinherits.Thesyntaxforprotocolinheritanceissimilartothesyntaxforclassinheritance,butwiththeoptiontolistmultipleinheritedprotocols,separatedbycommas:

1 protocolInheritingProtocol:SomeProtocol,AnotherProtocol{

2 //protocoldefinitiongoeshere

3 }

Here’sanexampleofaprotocolthatinheritstheTextRepresentableprotocolfromabove:

1 protocolPrettyTextRepresentable:TextRepresentable{

2 varprettyTextualDescription:String{get}

3 }

Thisexampledefinesanewprotocol,PrettyTextRepresentable,whichinheritsfromTextRepresentable.AnythingthatadoptsPrettyTextRepresentablemustsatisfyalloftherequirementsenforcedbyTextRepresentable,plustheadditionalrequirementsenforcedbyPrettyTextRepresentable.Inthisexample,PrettyTextRepresentableaddsasinglerequirementtoprovideagettablepropertycalledprettyTextualDescriptionthatreturnsaString.

TheSnakesAndLaddersclasscanbeextendedtoadoptandconformtoPrettyTextRepresentable:

1 extensionSnakesAndLadders:PrettyTextRepresentable{

2 varprettyTextualDescription:String{

3 varoutput=textualDescription+":\n"

4 forindexin1...finalSquare{

5 switchboard[index]{

6 caseletladderwhereladder>0:

7 output+="▲"

8 caseletsnakewheresnake<0:

9 output+="▼"

10 default:

11 output+="○"

12 }

13 }

14 returnoutput

15 }

16 }

ThisextensionstatesthatitadoptsthePrettyTextRepresentableprotocolandprovidesanimplementationoftheprettyTextualDescriptionpropertyfortheSnakesAndLadderstype.AnythingthatisPrettyTextRepresentablemustalsobeTextRepresentable,andsotheimplementationofprettyTextualDescriptionstartsbyaccessingthetextualDescriptionpropertyfromtheTextRepresentableprotocoltobeginanoutputstring.Itappendsacolonandalinebreak,andusesthisasthestartofitsprettytextrepresentation.Ittheniteratesthroughthearrayofboardsquares,andappendsageometricshapetorepresentthecontentsofeachsquare:

Ifthesquare’svalueisgreaterthan0,it’sthebaseofaladder,andisrepresentedby▲.

Ifthesquare’svalueislessthan0,it’stheheadofasnake,andisrepresentedby▼.

Otherwise,thesquare’svalueis0,andit’sa“free”square,representedby○.

TheprettyTextualDescriptionpropertycannowbeusedtoprintaprettytextdescriptionofanySnakesAndLaddersinstance:

1 print(game.prettyTextualDescription)

2 //AgameofSnakesandLadderswith25squares:

3 //○○▲○○▲○○▲▲○○○▼○○○○▼○○▼○▼○

Class-OnlyProtocols

Youcanlimitprotocoladoptiontoclasstypes(andnotstructuresorenumerations)byaddingtheAnyObjectprotocoltoaprotocol’sinheritancelist.

1 protocolSomeClassOnlyProtocol:AnyObject,

SomeInheritedProtocol{

2 //class-onlyprotocoldefinitiongoeshere

3 }

Intheexampleabove,SomeClassOnlyProtocolcanonlybeadoptedbyclasstypes.It’sacompile-timeerrortowriteastructureorenumerationdefinitionthattriestoadoptSomeClassOnlyProtocol.

NOTE

Useaclass-onlyprotocolwhenthebehaviordefinedbythatprotocol’srequirementsassumesorrequiresthataconformingtypehasreferencesemanticsratherthanvaluesemantics.Formoreaboutreferenceandvaluesemantics,seeStructuresandEnumerationsAreValueTypesandClassesAreReferenceTypes.

ProtocolComposition

Itcanbeusefultorequireatypetoconformtomultipleprotocolsatthesametime.Youcancombinemultipleprotocolsintoasinglerequirementwithaprotocolcomposition.Protocolcompositionsbehaveasifyoudefinedatemporarylocalprotocolthathasthecombinedrequirementsofallprotocolsinthecomposition.Protocolcompositionsdon’tdefineanynewprotocoltypes.

ProtocolcompositionshavetheformSomeProtocol&AnotherProtocol.Youcanlistasmanyprotocolsasyouneed,separatingthemwithampersands(&).Inadditiontoitslistofprotocols,aprotocolcompositioncanalsocontainoneclasstype,whichyoucanusetospecifyarequiredsuperclass.

Here’sanexamplethatcombinestwoprotocolscalledNamedandAgedintoasingleprotocolcompositionrequirementonafunctionparameter:

1 protocolNamed{

2 varname:String{get}

3 }

4 protocolAged{

5 varage:Int{get}

6 }

7 structPerson:Named,Aged{

8 varname:String

9 varage:Int

10 }

11 funcwishHappyBirthday(tocelebrator:Named&Aged){

12 print("Happybirthday,\(celebrator.name),you're\

(celebrator.age)!")

13 }

14 letbirthdayPerson=Person(name:"Malcolm",age:21)

15 wishHappyBirthday(to:birthdayPerson)

16 //Prints"Happybirthday,Malcolm,you're21!"

Inthisexample,theNamedprotocolhasasinglerequirementforagettableString

propertycalledname.TheAgedprotocolhasasinglerequirementforagettableIntpropertycalledage.BothprotocolsareadoptedbyastructurecalledPerson.

TheexamplealsodefinesawishHappyBirthday(to:)function.ThetypeofthecelebratorparameterisNamed&Aged,whichmeans“anytypethatconformstoboththeNamedandAgedprotocols.”Itdoesn’tmatterwhichspecifictypeispassedtothefunction,aslongasitconformstobothoftherequiredprotocols.

TheexamplethencreatesanewPersoninstancecalledbirthdayPersonandpassesthisnewinstancetothewishHappyBirthday(to:)function.BecausePersonconformstobothprotocols,thiscallisvalid,andthewishHappyBirthday(to:)functioncanprintitsbirthdaygreeting.

Here’sanexamplethatcombinestheNamedprotocolfromthepreviousexamplewithaLocationclass:

1 classLocation{

2 varlatitude:Double

3 varlongitude:Double

4 init(latitude:Double,longitude:Double){

5 self.latitude=latitude

6 self.longitude=longitude

7 }

8 }

9 classCity:Location,Named{

10 varname:String

11 init(name:String,latitude:Double,longitude:Double){

12 self.name=name

13 super.init(latitude:latitude,longitude:longitude)

14 }

15 }

16 funcbeginConcert(inlocation:Location&Named){

17 print("Hello,\(location.name)!")

18 }

19

20 letseattle=City(name:"Seattle",latitude:47.6,longitude:

-122.3)

21 beginConcert(in:seattle)

22 //Prints"Hello,Seattle!"

ThebeginConcert(in:)functiontakesaparameteroftypeLocation&Named,whichmeans“anytypethat’sasubclassofLocationandthatconformstotheNamedprotocol.”Inthiscase,Citysatisfiesbothrequirements.

PassingbirthdayPersontothebeginConcert(in:)functionisinvalidbecausePersonisn’tasubclassofLocation.Likewise,ifyoumadeasubclassofLocationthatdidn’tconformtotheNamedprotocol,callingbeginConcert(in:)withaninstanceofthattypeisalsoinvalid.

CheckingforProtocolConformance

YoucanusetheisandasoperatorsdescribedinTypeCastingtocheckforprotocolconformance,andtocasttoaspecificprotocol.Checkingforandcastingtoaprotocolfollowsexactlythesamesyntaxascheckingforandcastingtoatype:

Theisoperatorreturnstrueifaninstanceconformstoaprotocolandreturnsfalseifitdoesn’t.

Theas?versionofthedowncastoperatorreturnsanoptionalvalueoftheprotocol’stype,andthisvalueisniliftheinstancedoesn’tconformtothatprotocol.

Theas!versionofthedowncastoperatorforcesthedowncasttotheprotocoltypeandtriggersaruntimeerrorifthedowncastdoesn’tsucceed.

ThisexampledefinesaprotocolcalledHasArea,withasingleproperty

requirementofagettableDoublepropertycalledarea:

1 protocolHasArea{

2 vararea:Double{get}

3 }

Herearetwoclasses,CircleandCountry,bothofwhichconformtotheHasAreaprotocol:

1 classCircle:HasArea{

2 letpi=3.1415927

3 varradius:Double

4 vararea:Double{returnpi*radius*radius}

5 init(radius:Double){self.radius=radius}

6 }

7 classCountry:HasArea{

8 vararea:Double

9 init(area:Double){self.area=area}

10 }

TheCircleclassimplementstheareapropertyrequirementasacomputedproperty,basedonastoredradiusproperty.TheCountryclassimplementsthearearequirementdirectlyasastoredproperty.BothclassescorrectlyconformtotheHasAreaprotocol.

Here’saclasscalledAnimal,whichdoesn’tconformtotheHasAreaprotocol:

1 classAnimal{

2 varlegs:Int

3 init(legs:Int){self.legs=legs}

4 }

TheCircle,CountryandAnimalclassesdon’thaveasharedbaseclass.

Nonetheless,they’reallclasses,andsoinstancesofallthreetypescanbeusedtoinitializeanarraythatstoresvaluesoftypeAnyObject:

1 letobjects:[AnyObject]=[

2 Circle(radius:2.0),

3 Country(area:243_610),

4 Animal(legs:4)

5 ]

TheobjectsarrayisinitializedwithanarrayliteralcontainingaCircleinstancewitharadiusof2units;aCountryinstanceinitializedwiththesurfaceareaoftheUnitedKingdominsquarekilometers;andanAnimalinstancewithfourlegs.

Theobjectsarraycannowbeiterated,andeachobjectinthearraycanbecheckedtoseeifitconformstotheHasAreaprotocol:

1 forobjectinobjects{

2 ifletobjectWithArea=objectas?HasArea{

3 print("Areais\(objectWithArea.area)")

4 }else{

5 print("Somethingthatdoesn'thaveanarea")

6 }

7 }

8 //Areais12.5663708

9 //Areais243610.0

10 //Somethingthatdoesn'thaveanarea

WheneveranobjectinthearrayconformstotheHasAreaprotocol,theoptionalvaluereturnedbytheas?operatorisunwrappedwithoptionalbindingintoaconstantcalledobjectWithArea.TheobjectWithAreaconstantisknowntobeoftypeHasArea,andsoitsareapropertycanbeaccessedandprintedinatype-safeway.

Notethattheunderlyingobjectsaren’tchangedbythecastingprocess.They

continuetobeaCircle,aCountryandanAnimal.However,atthepointthatthey’restoredintheobjectWithAreaconstant,they’reonlyknowntobeoftypeHasArea,andsoonlytheirareapropertycanbeaccessed.

OptionalProtocolRequirements

Youcandefineoptionalrequirementsforprotocols.Theserequirementsdon’thavetobeimplementedbytypesthatconformtotheprotocol.Optionalrequirementsareprefixedbytheoptionalmodifieraspartoftheprotocol’sdefinition.OptionalrequirementsareavailablesothatyoucanwritecodethatinteroperateswithObjective-C.Boththeprotocolandtheoptionalrequirementmustbemarkedwiththe@objcattribute.Notethat@objcprotocolscanbeadoptedonlybyclassesthatinheritfromObjective-Cclassesorother@objcclasses.Theycan’tbeadoptedbystructuresorenumerations.

Whenyouuseamethodorpropertyinanoptionalrequirement,itstypeautomaticallybecomesanoptional.Forexample,amethodoftype(Int)->Stringbecomes((Int)->String)?.Notethattheentirefunctiontypeiswrappedintheoptional,notthemethod’sreturnvalue.

Anoptionalprotocolrequirementcanbecalledwithoptionalchaining,toaccountforthepossibilitythattherequirementwasnotimplementedbyatypethatconformstotheprotocol.Youcheckforanimplementationofanoptionalmethodbywritingaquestionmarkafterthenameofthemethodwhenit’scalled,suchassomeOptionalMethod?(someArgument).Forinformationonoptionalchaining,seeOptionalChaining.

Thefollowingexampledefinesaninteger-countingclasscalledCounter,whichusesanexternaldatasourcetoprovideitsincrementamount.ThisdatasourceisdefinedbytheCounterDataSourceprotocol,whichhastwooptionalrequirements:

1 @objcprotocolCounterDataSource{

2 @objcoptionalfuncincrement(forCountcount:Int)->Int

3 @objcoptionalvarfixedIncrement:Int{get}

4 }

TheCounterDataSourceprotocoldefinesanoptionalmethodrequirementcalledincrement(forCount:)andanoptionalpropertyrequirementcalledfixedIncrement.TheserequirementsdefinetwodifferentwaysfordatasourcestoprovideanappropriateincrementamountforaCounterinstance.

NOTE

Strictlyspeaking,youcanwriteacustomclassthatconformstoCounterDataSourcewithoutimplementingeitherprotocolrequirement.They’rebothoptional,afterall.Althoughtechnicallyallowed,thiswouldn’tmakeforaverygooddatasource.

TheCounterclass,definedbelow,hasanoptionaldataSourcepropertyoftypeCounterDataSource?:

1 classCounter{

2 varcount=0

3 vardataSource:CounterDataSource?

4 funcincrement(){

5 ifletamount=dataSource?.increment?(forCount:count)

{

6 count+=amount

7 }elseifletamount=dataSource?.fixedIncrement{

8 count+=amount

9 }

10 }

11 }

TheCounterclassstoresitscurrentvalueinavariablepropertycalledcount.TheCounterclassalsodefinesamethodcalledincrement,whichincrementsthecountpropertyeverytimethemethodiscalled.

Theincrement()methodfirsttriestoretrieveanincrementamountbylookingforanimplementationoftheincrement(forCount:)methodonitsdatasource.

Theincrement()methodusesoptionalchainingtotrytocallincrement(forCount:),andpassesthecurrentcountvalueasthemethod’ssingleargument.

Notethattwolevelsofoptionalchainingareatplayhere.First,it’spossiblethatdataSourcemaybenil,andsodataSourcehasaquestionmarkafteritsnametoindicatethatincrement(forCount:)shouldbecalledonlyifdataSourceisn’tnil.Second,evenifdataSourcedoesexist,there’snoguaranteethatitimplementsincrement(forCount:),becauseit’sanoptionalrequirement.Here,thepossibilitythatincrement(forCount:)mightnotbeimplementedisalsohandledbyoptionalchaining.Thecalltoincrement(forCount:)happensonlyifincrement(forCount:)exists—thatis,ifitisn’tnil.Thisiswhyincrement(forCount:)isalsowrittenwithaquestionmarkafteritsname.

Becausethecalltoincrement(forCount:)canfailforeitherofthesetworeasons,thecallreturnsanoptionalIntvalue.Thisistrueeventhoughincrement(forCount:)isdefinedasreturninganon-optionalIntvalueinthedefinitionofCounterDataSource.Eventhoughtherearetwooptionalchainingoperations,oneafteranother,theresultisstillwrappedinasingleoptional.Formoreinformationaboutusingmultipleoptionalchainingoperations,seeLinkingMultipleLevelsofChaining.

Aftercallingincrement(forCount:),theoptionalIntthatitreturnsisunwrappedintoaconstantcalledamount,usingoptionalbinding.IftheoptionalIntdoescontainavalue—thatis,ifthedelegateandmethodbothexist,andthemethodreturnedavalue—theunwrappedamountisaddedontothestoredcountproperty,andincrementationiscomplete.

Ifit’snotpossibletoretrieveavaluefromtheincrement(forCount:)method—eitherbecausedataSourceisnil,orbecausethedatasourcedoesn’timplementincrement(forCount:)—thentheincrement()methodtriestoretrieveavaluefromthedatasource’sfixedIncrementpropertyinstead.ThefixedIncrementpropertyisalsoanoptionalrequirement,soitsvalueisanoptionalIntvalue,eventhoughfixedIncrementisdefinedasanon-optionalIntpropertyaspartoftheCounterDataSourceprotocoldefinition.

Here’sasimpleCounterDataSourceimplementationwherethedatasourcereturnsaconstantvalueof3everytimeit’squeried.Itdoesthisbyimplementing

theoptionalfixedIncrementpropertyrequirement:

1 classThreeSource:NSObject,CounterDataSource{

2 letfixedIncrement=3

3 }

YoucanuseaninstanceofThreeSourceasthedatasourceforanewCounterinstance:

1 varcounter=Counter()

2 counter.dataSource=ThreeSource()

3 for_in1...4{

4 counter.increment()

5 print(counter.count)

6 }

7 //3

8 //6

9 //9

10 //12

ThecodeabovecreatesanewCounterinstance;setsitsdatasourcetobeanewThreeSourceinstance;andcallsthecounter’sincrement()methodfourtimes.Asexpected,thecounter’scountpropertyincreasesbythreeeachtimeincrement()iscalled.

Here’samorecomplexdatasourcecalledTowardsZeroSource,whichmakesaCounterinstancecountupordowntowardszerofromitscurrentcountvalue:

1 classTowardsZeroSource:NSObject,CounterDataSource{

2 funcincrement(forCountcount:Int)->Int{

3 ifcount==0{

4 return0

5 }elseifcount<0{

6 return1

7 }else{

8 return-1

9 }

10 }

11 }

TheTowardsZeroSourceclassimplementstheoptionalincrement(forCount:)methodfromtheCounterDataSourceprotocolandusesthecountargumentvaluetoworkoutwhichdirectiontocountin.Ifcountisalreadyzero,themethodreturns0toindicatethatnofurthercountingshouldtakeplace.

YoucanuseaninstanceofTowardsZeroSourcewiththeexistingCounterinstancetocountfrom-4tozero.Oncethecounterreacheszero,nomorecountingtakesplace:

1 counter.count=-4

2 counter.dataSource=TowardsZeroSource()

3 for_in1...5{

4 counter.increment()

5 print(counter.count)

6 }

7 //-3

8 //-2

9 //-1

10 //0

11 //0

ProtocolExtensions

Protocolscanbeextendedtoprovidemethod,initializer,subscript,and

computedpropertyimplementationstoconformingtypes.Thisallowsyoutodefinebehavioronprotocolsthemselves,ratherthanineachtype’sindividualconformanceorinaglobalfunction.

Forexample,theRandomNumberGeneratorprotocolcanbeextendedtoprovidearandomBool()method,whichusestheresultoftherequiredrandom()methodtoreturnarandomBoolvalue:

1 extensionRandomNumberGenerator{

2 funcrandomBool()->Bool{

3 returnrandom()>0.5

4 }

5 }

Bycreatinganextensionontheprotocol,allconformingtypesautomaticallygainthismethodimplementationwithoutanyadditionalmodification.

1 letgenerator=LinearCongruentialGenerator()

2 print("Here'sarandomnumber:\(generator.random())")

3 //Prints"Here'sarandomnumber:0.3746499199817101"

4 print("Andhere'sarandomBoolean:\(generator.randomBool())")

5 //Prints"Andhere'sarandomBoolean:true"

Protocolextensionscanaddimplementationstoconformingtypesbutcan’tmakeaprotocolextendorinheritfromanotherprotocol.Protocolinheritanceisalwaysspecifiedintheprotocoldeclarationitself.

ProvidingDefaultImplementationsYoucanuseprotocolextensionstoprovideadefaultimplementationtoanymethodorcomputedpropertyrequirementofthatprotocol.Ifaconformingtypeprovidesitsownimplementationofarequiredmethodorproperty,thatimplementationwillbeusedinsteadoftheoneprovidedbytheextension.

NOTE

Protocolrequirementswithdefaultimplementationsprovidedbyextensionsaredistinctfromoptionalprotocolrequirements.Althoughconformingtypesdon’thavetoprovidetheirownimplementationofeither,requirementswithdefaultimplementationscanbecalledwithoutoptionalchaining.

Forexample,thePrettyTextRepresentableprotocol,whichinheritstheTextRepresentableprotocolcanprovideadefaultimplementationofitsrequiredprettyTextualDescriptionpropertytosimplyreturntheresultofaccessingthetextualDescriptionproperty:

1 extensionPrettyTextRepresentable{

2 varprettyTextualDescription:String{

3 returntextualDescription

4 }

5 }

AddingConstraintstoProtocolExtensionsWhenyoudefineaprotocolextension,youcanspecifyconstraintsthatconformingtypesmustsatisfybeforethemethodsandpropertiesoftheextensionareavailable.Youwritetheseconstraintsafterthenameoftheprotocolyou’reextendingbywritingagenericwhereclause.Formoreaboutgenericwhereclauses,seeGenericWhereClauses.

Forexample,youcandefineanextensiontotheCollectionprotocolthatappliestoanycollectionwhoseelementsconformtotheEquatableprotocol.Byconstrainingacollection’selementstotheEquatableprotocol,apartofthestandardlibrary,youcanusethe==and!=operatorstocheckforequalityandinequalitybetweentwoelements.

1 extensionCollectionwhereElement:Equatable{

2 funcallEqual()->Bool{

3 forelementinself{

4 ifelement!=self.first{

5 returnfalse

6 }

7 }

8 returntrue

9 }

10 }

TheallEqual()methodreturnstrueonlyifalltheelementsinthecollectionareequal.

Considertwoarraysofintegers,onewherealltheelementsarethesame,andonewheretheyaren’t:

1 letequalNumbers=[100,100,100,100,100]

2 letdifferentNumbers=[100,100,200,100,200]

BecausearraysconformtoCollectionandintegersconformtoEquatable,equalNumbersanddifferentNumberscanusetheallEqual()method:

1 print(equalNumbers.allEqual())

2 //Prints"true"

3 print(differentNumbers.allEqual())

4 //Prints"false"

NOTE

Ifaconformingtypesatisfiestherequirementsformultipleconstrainedextensionsthatprovideimplementationsforthesamemethodorproperty,Swiftusestheimplementationcorrespondingtothemostspecializedconstraints.

Generics

Genericcodeenablesyoutowriteflexible,reusablefunctionsandtypesthatcanworkwithanytype,subjecttorequirementsthatyoudefine.Youcanwritecodethatavoidsduplicationandexpressesitsintentinaclear,abstractedmanner.

GenericsareoneofthemostpowerfulfeaturesofSwift,andmuchoftheSwiftstandardlibraryisbuiltwithgenericcode.Infact,you’vebeenusinggenericsthroughouttheLanguageGuide,evenifyoudidn’trealizeit.Forexample,Swift’sArrayandDictionarytypesarebothgenericcollections.YoucancreateanarraythatholdsIntvalues,oranarraythatholdsStringvalues,orindeedanarrayforanyothertypethatcanbecreatedinSwift.Similarly,youcancreateadictionarytostorevaluesofanyspecifiedtype,andtherearenolimitationsonwhatthattypecanbe.

TheProblemThatGenericsSolve

Here’sastandard,nongenericfunctioncalledswapTwoInts(_:_:),whichswapstwoIntvalues:

1 funcswapTwoInts(_a:inoutInt,_b:inoutInt){

2 lettemporaryA=a

3 a=b

4 b=temporaryA

5 }

Thisfunctionmakesuseofin-outparameterstoswapthevaluesofaandb,asdescribedinIn-OutParameters.

TheswapTwoInts(_:_:)functionswapstheoriginalvalueofbintoa,andtheoriginalvalueofaintob.YoucancallthisfunctiontoswapthevaluesintwoIntvariables:

1 varsomeInt=3

2 varanotherInt=107

3 swapTwoInts(&someInt,&anotherInt)

4 print("someIntisnow\(someInt),andanotherIntisnow\

(anotherInt)")

5 //Prints"someIntisnow107,andanotherIntisnow3"

TheswapTwoInts(_:_:)functionisuseful,butitcanonlybeusedwithIntvalues.IfyouwanttoswaptwoStringvalues,ortwoDoublevalues,youhavetowritemorefunctions,suchastheswapTwoStrings(_:_:)andswapTwoDoubles(_:_:)functionsshownbelow:

1 funcswapTwoStrings(_a:inoutString,_b:inoutString){

2 lettemporaryA=a

3 a=b

4 b=temporaryA

5 }

6

7 funcswapTwoDoubles(_a:inoutDouble,_b:inoutDouble){

8 lettemporaryA=a

9 a=b

10 b=temporaryA

11 }

YoumayhavenoticedthatthebodiesoftheswapTwoInts(_:_:),swapTwoStrings(_:_:),andswapTwoDoubles(_:_:)functionsareidentical.Theonlydifferenceisthetypeofthevaluesthattheyaccept(Int,String,andDouble).

It’smoreuseful,andconsiderablymoreflexible,towriteasinglefunctionthatswapstwovaluesofanytype.Genericcodeenablesyoutowritesuchafunction.(Agenericversionofthesefunctionsisdefinedbelow.)

NOTE

Inallthreefunctions,thetypesofaandbmustbethesame.Ifaandbaren’tofthesametype,itisn’tpossibletoswaptheirvalues.Swiftisatype-safelanguage,anddoesn’tallow(forexample)avariableoftypeStringandavariableoftypeDoubletoswapvalueswitheachother.Attemptingtodosoresultsinacompile-timeerror.

GenericFunctions

Genericfunctionscanworkwithanytype.Here’sagenericversionoftheswapTwoInts(_:_:)functionfromabove,calledswapTwoValues(_:_:):

1 funcswapTwoValues<T>(_a:inoutT,_b:inoutT){

2 lettemporaryA=a

3 a=b

4 b=temporaryA

5 }

ThebodyoftheswapTwoValues(_:_:)functionisidenticaltothebodyoftheswapTwoInts(_:_:)function.However,thefirstlineofswapTwoValues(_:_:)isslightlydifferentfromswapTwoInts(_:_:).Here’showthefirstlinescompare:

1 funcswapTwoInts(_a:inoutInt,_b:inoutInt)

2 funcswapTwoValues<T>(_a:inoutT,_b:inoutT)

Thegenericversionofthefunctionusesaplaceholdertypename(calledT,inthiscase)insteadofanactualtypename(suchasInt,String,orDouble).Theplaceholdertypenamedoesn’tsayanythingaboutwhatTmustbe,butitdoessaythatbothaandbmustbeofthesametypeT,whateverTrepresents.TheactualtypetouseinplaceofTisdeterminedeachtimetheswapTwoValues(_:_:)functioniscalled.

Theotherdifferencebetweenagenericfunctionandanongenericfunctionisthatthegenericfunction’sname(swapTwoValues(_:_:))isfollowedbythe

placeholdertypename(T)insideanglebrackets(<T>).ThebracketstellSwiftthatTisaplaceholdertypenamewithintheswapTwoValues(_:_:)functiondefinition.BecauseTisaplaceholder,Swiftdoesn’tlookforanactualtypecalledT.

TheswapTwoValues(_:_:)functioncannowbecalledinthesamewayasswapTwoInts,exceptthatitcanbepassedtwovaluesofanytype,aslongasbothofthosevaluesareofthesametypeaseachother.EachtimeswapTwoValues(_:_:)iscalled,thetypetouseforTisinferredfromthetypesofvaluespassedtothefunction.

Inthetwoexamplesbelow,TisinferredtobeIntandStringrespectively:

1 varsomeInt=3

2 varanotherInt=107

3 swapTwoValues(&someInt,&anotherInt)

4 //someIntisnow107,andanotherIntisnow3

5

6 varsomeString="hello"

7 varanotherString="world"

8 swapTwoValues(&someString,&anotherString)

9 //someStringisnow"world",andanotherStringisnow"hello"

NOTE

TheswapTwoValues(_:_:)functiondefinedaboveisinspiredbyagenericfunctioncalledswap,whichispartoftheSwiftstandardlibrary,andisautomaticallymadeavailableforyoutouseinyourapps.IfyouneedthebehavioroftheswapTwoValues(_:_:)functioninyourowncode,youcanuseSwift’sexistingswap(_:_:)functionratherthanprovidingyourownimplementation.

TypeParameters

IntheswapTwoValues(_:_:)exampleabove,theplaceholdertypeTisanexampleofatypeparameter.Typeparametersspecifyandnameaplaceholdertype,and

arewrittenimmediatelyafterthefunction’sname,betweenapairofmatchinganglebrackets(suchas<T>).

Onceyouspecifyatypeparameter,youcanuseittodefinethetypeofafunction’sparameters(suchastheaandbparametersoftheswapTwoValues(_:_:)function),orasthefunction’sreturntype,orasatypeannotationwithinthebodyofthefunction.Ineachcase,thetypeparameterisreplacedwithanactualtypewheneverthefunctioniscalled.(IntheswapTwoValues(_:_:)exampleabove,TwasreplacedwithIntthefirsttimethefunctionwascalled,andwasreplacedwithStringthesecondtimeitwascalled.)

Youcanprovidemorethanonetypeparameterbywritingmultipletypeparameternameswithintheanglebrackets,separatedbycommas.

NamingTypeParameters

Inmostcases,typeparametershavedescriptivenames,suchasKeyandValueinDictionary<Key,Value>andElementinArray<Element>,whichtellsthereaderabouttherelationshipbetweenthetypeparameterandthegenerictypeorfunctionit’susedin.However,whenthereisn’tameaningfulrelationshipbetweenthem,it’straditionaltonamethemusingsingleletterssuchasT,U,andV,suchasTintheswapTwoValues(_:_:)functionabove.

NOTE

Alwaysgivetypeparametersuppercamelcasenames(suchasTandMyTypeParameter)toindicatethatthey’reaplaceholderforatype,notavalue.

GenericTypes

Inadditiontogenericfunctions,Swiftenablesyoutodefineyourowngenerictypes.Thesearecustomclasses,structures,andenumerationsthatcanworkwithanytype,inasimilarwaytoArrayandDictionary.

ThissectionshowsyouhowtowriteagenericcollectiontypecalledStack.Astackisanorderedsetofvalues,similartoanarray,butwithamorerestrictedsetofoperationsthanSwift’sArraytype.Anarrayallowsnewitemstobeinsertedandremovedatanylocationinthearray.Astack,however,allowsnewitemstobeappendedonlytotheendofthecollection(knownaspushinganewvalueontothestack).Similarly,astackallowsitemstoberemovedonlyfromtheendofthecollection(knownaspoppingavalueoffthestack).

NOTE

TheconceptofastackisusedbytheUINavigationControllerclasstomodeltheviewcontrollersinitsnavigationhierarchy.YoucalltheUINavigationControllerclasspushViewController(_:animated:)methodtoadd(orpush)aviewcontrollerontothenavigationstack,anditspopViewControllerAnimated(_:)methodtoremove(orpop)aviewcontrollerfromthenavigationstack.Astackisausefulcollectionmodelwheneveryouneedastrict“lastin,firstout”approachtomanagingacollection.

Theillustrationbelowshowsthepushandpopbehaviorforastack:

1. Therearecurrentlythreevaluesonthestack.

2. Afourthvalueispushedontothetopofthestack.

3. Thestacknowholdsfourvalues,withthemostrecentoneatthetop.

4. Thetopiteminthestackispopped.

5. Afterpoppingavalue,thestackonceagainholdsthreevalues.

Here’showtowriteanongenericversionofastack,inthiscaseforastackofIntvalues:

1 structIntStack{

2 varitems=[Int]()

3 mutatingfuncpush(_item:Int){

4 items.append(item)

5 }

6 mutatingfuncpop()->Int{

7 returnitems.removeLast()

8 }

9 }

ThisstructureusesanArraypropertycalleditemstostorethevaluesinthestack.Stackprovidestwomethods,pushandpop,topushandpopvaluesonandoffthestack.Thesemethodsaremarkedasmutating,becausetheyneedtomodify(ormutate)thestructure’sitemsarray.

TheIntStacktypeshownabovecanonlybeusedwithIntvalues,however.ItwouldbemuchmoreusefultodefineagenericStackclass,thatcanmanageastackofanytypeofvalue.

Here’sagenericversionofthesamecode:

1 structStack<Element>{

2 varitems=[Element]()

3 mutatingfuncpush(_item:Element){

4 items.append(item)

5 }

6 mutatingfuncpop()->Element{

7 returnitems.removeLast()

8 }

9 }

NotehowthegenericversionofStackisessentiallythesameasthenongenericversion,butwithatypeparametercalledElementinsteadofanactualtypeofInt.Thistypeparameteriswrittenwithinapairofanglebrackets(<Element>)immediatelyafterthestructure’sname.

Elementdefinesaplaceholdernameforatypetobeprovidedlater.ThisfuturetypecanbereferredtoasElementanywherewithinthestructure’sdefinition.Inthiscase,Elementisusedasaplaceholderinthreeplaces:

Tocreateapropertycalleditems,whichisinitializedwithanemptyarrayofvaluesoftypeElement

Tospecifythatthepush(_:)methodhasasingleparametercalleditem,whichmustbeoftypeElement

Tospecifythatthevaluereturnedbythepop()methodwillbeavalueoftypeElement

Becauseit’sagenerictype,StackcanbeusedtocreateastackofanyvalidtypeinSwift,inasimilarmannertoArrayandDictionary.

YoucreateanewStackinstancebywritingthetypetobestoredinthestackwithinanglebrackets.Forexample,tocreateanewstackofstrings,youwriteStack<String>():

1 varstackOfStrings=Stack<String>()

2 stackOfStrings.push("uno")

3 stackOfStrings.push("dos")

4 stackOfStrings.push("tres")

5 stackOfStrings.push("cuatro")

6 //thestacknowcontains4strings

Here’showstackOfStringslooksafterpushingthesefourvaluesontothestack:

Poppingavaluefromthestackremovesandreturnsthetopvalue,"cuatro":

1 letfromTheTop=stackOfStrings.pop()

2 //fromTheTopisequalto"cuatro",andthestacknowcontains

3strings

Here’showthestacklooksafterpoppingitstopvalue:

ExtendingaGenericType

Whenyouextendagenerictype,youdon’tprovideatypeparameterlistaspartoftheextension’sdefinition.Instead,thetypeparameterlistfromtheoriginaltypedefinitionisavailablewithinthebodyoftheextension,andtheoriginaltypeparameternamesareusedtorefertothetypeparametersfromtheoriginaldefinition.

ThefollowingexampleextendsthegenericStacktypetoaddaread-onlycomputedpropertycalledtopItem,whichreturnsthetopitemonthestackwithoutpoppingitfromthestack:

1 extensionStack{

2 vartopItem:Element?{

3 returnitems.isEmpty?nil:items[items.count-1]

4 }

5 }

ThetopItempropertyreturnsanoptionalvalueoftypeElement.Ifthestackisempty,topItemreturnsnil;ifthestackisn’tempty,topItemreturnsthefinalitemintheitemsarray.

Notethatthisextensiondoesn’tdefineatypeparameterlist.Instead,theStacktype’sexistingtypeparametername,Element,isusedwithintheextensiontoindicatetheoptionaltypeofthetopItemcomputedproperty.

ThetopItemcomputedpropertycannowbeusedwithanyStackinstancetoaccessandqueryitstopitemwithoutremovingit.

1 iflettopItem=stackOfStrings.topItem{

2 print("Thetopitemonthestackis\(topItem).")

3 }

4 //Prints"Thetopitemonthestackistres."

Extensionsofagenerictypecanalsoincluderequirementsthatinstancesoftheextendedtypemustsatisfyinordertogainthenewfunctionality,asdiscussedinExtensionswithaGenericWhereClausebelow.

TypeConstraints

TheswapTwoValues(_:_:)functionandtheStacktypecanworkwithanytype.

However,it’ssometimesusefultoenforcecertaintypeconstraintsonthetypesthatcanbeusedwithgenericfunctionsandgenerictypes.Typeconstraintsspecifythatatypeparametermustinheritfromaspecificclass,orconformtoaparticularprotocolorprotocolcomposition.

Forexample,Swift’sDictionarytypeplacesalimitationonthetypesthatcanbeusedaskeysforadictionary.AsdescribedinDictionaries,thetypeofadictionary’skeysmustbehashable.Thatis,itmustprovideawaytomakeitselfuniquelyrepresentable.Dictionaryneedsitskeystobehashablesothatitcancheckwhetheritalreadycontainsavalueforaparticularkey.Withoutthisrequirement,Dictionarycouldnottellwhetheritshouldinsertorreplaceavalueforaparticularkey,norwoulditbeabletofindavalueforagivenkeythatisalreadyinthedictionary.

ThisrequirementisenforcedbyatypeconstraintonthekeytypeforDictionary,whichspecifiesthatthekeytypemustconformtotheHashableprotocol,aspecialprotocoldefinedintheSwiftstandardlibrary.AllofSwift’sbasictypes(suchasString,Int,Double,andBool)arehashablebydefault.

Youcandefineyourowntypeconstraintswhencreatingcustomgenerictypes,andtheseconstraintsprovidemuchofthepowerofgenericprogramming.AbstractconceptslikeHashablecharacterizetypesintermsoftheirconceptualcharacteristics,ratherthantheirconcretetype.

TypeConstraintSyntaxYouwritetypeconstraintsbyplacingasingleclassorprotocolconstraintafteratypeparameter’sname,separatedbyacolon,aspartofthetypeparameterlist.Thebasicsyntaxfortypeconstraintsonagenericfunctionisshownbelow(althoughthesyntaxisthesameforgenerictypes):

1 funcsomeFunction<T:SomeClass,U:SomeProtocol>(someT:T,

someU:U){

2 //functionbodygoeshere

3 }

Thehypotheticalfunctionabovehastwotypeparameters.Thefirsttypeparameter,T,hasatypeconstraintthatrequiresTtobeasubclassofSomeClass.Thesecondtypeparameter,U,hasatypeconstraintthatrequiresUtoconformtotheprotocolSomeProtocol.

TypeConstraintsinActionHere’sanongenericfunctioncalledfindIndex(ofString:in:),whichisgivenaStringvaluetofindandanarrayofStringvalueswithinwhichtofindit.ThefindIndex(ofString:in:)functionreturnsanoptionalIntvalue,whichwillbetheindexofthefirstmatchingstringinthearrayifit’sfound,ornilifthestringcan’tbefound:

1 funcfindIndex(ofStringvalueToFind:String,inarray:

[String])->Int?{

2 for(index,value)inarray.enumerated(){

3 ifvalue==valueToFind{

4 returnindex

5 }

6 }

7 returnnil

8 }

ThefindIndex(ofString:in:)functioncanbeusedtofindastringvalueinanarrayofstrings:

1 letstrings=["cat","dog","llama","parakeet","terrapin"]

2 ifletfoundIndex=findIndex(ofString:"llama",in:strings){

3 print("Theindexofllamais\(foundIndex)")

4 }

5 //Prints"Theindexofllamais2"

Theprincipleoffindingtheindexofavalueinanarrayisn’tusefulonlyfor

strings,however.YoucanwritethesamefunctionalityasagenericfunctionbyreplacinganymentionofstringswithvaluesofsometypeTinstead.

Here’showyoumightexpectagenericversionoffindIndex(ofString:in:),calledfindIndex(of:in:),tobewritten.NotethatthereturntypeofthisfunctionisstillInt?,becausethefunctionreturnsanoptionalindexnumber,notanoptionalvaluefromthearray.Bewarned,though—thisfunctiondoesn’tcompile,forreasonsexplainedaftertheexample:

1 funcfindIndex<T>(ofvalueToFind:T,inarray:[T])->Int?{

2 for(index,value)inarray.enumerated(){

3 ifvalue==valueToFind{

4 returnindex

5 }

6 }

7 returnnil

8 }

Thisfunctiondoesn’tcompileaswrittenabove.Theproblemlieswiththeequalitycheck,“ifvalue==valueToFind”.NoteverytypeinSwiftcanbecomparedwiththeequaltooperator(==).Ifyoucreateyourownclassorstructuretorepresentacomplexdatamodel,forexample,thenthemeaningof“equalto”forthatclassorstructureisn’tsomethingthatSwiftcanguessforyou.Becauseofthis,itisn’tpossibletoguaranteethatthiscodewillworkforeverypossibletypeT,andanappropriateerrorisreportedwhenyoutrytocompilethecode.

Allisnotlost,however.TheSwiftstandardlibrarydefinesaprotocolcalledEquatable,whichrequiresanyconformingtypetoimplementtheequaltooperator(==)andthenotequaltooperator(!=)tocompareanytwovaluesofthattype.AllofSwift’sstandardtypesautomaticallysupporttheEquatableprotocol.

AnytypethatisEquatablecanbeusedsafelywiththefindIndex(of:in:)function,becauseit’sguaranteedtosupporttheequaltooperator.Toexpressthisfact,youwriteatypeconstraintofEquatableaspartofthetypeparameter’sdefinitionwhenyoudefinethefunction:

1 funcfindIndex<T:Equatable>(ofvalueToFind:T,inarray:[T])-

>Int?{

2 for(index,value)inarray.enumerated(){

3 ifvalue==valueToFind{

4 returnindex

5 }

6 }

7 returnnil

8 }

ThesingletypeparameterforfindIndex(of:in:)iswrittenasT:Equatable,whichmeans“anytypeTthatconformstotheEquatableprotocol.”

ThefindIndex(of:in:)functionnowcompilessuccessfullyandcanbeusedwithanytypethatisEquatable,suchasDoubleorString:

1 letdoubleIndex=findIndex(of:9.3,in:[3.14159,0.1,0.25])

2 //doubleIndexisanoptionalIntwithnovalue,because9.3

isn'tinthearray

3 letstringIndex=findIndex(of:"Andrea",in:["Mike",

"Malcolm","Andrea"])

4 //stringIndexisanoptionalIntcontainingavalueof2

AssociatedTypes

Whendefiningaprotocol,it’ssometimesusefultodeclareoneormoreassociatedtypesaspartoftheprotocol’sdefinition.Anassociatedtypegivesaplaceholdernametoatypethatisusedaspartoftheprotocol.Theactualtypetouseforthatassociatedtypeisn’tspecifieduntiltheprotocolisadopted.Associatedtypesarespecifiedwiththeassociatedtypekeyword.

AssociatedTypesinActionHere’sanexampleofaprotocolcalledContainer,whichdeclaresanassociatedtypecalledItem:

1 protocolContainer{

2 associatedtypeItem

3 mutatingfuncappend(_item:Item)

4 varcount:Int{get}

5 subscript(i:Int)->Item{get}

6 }

TheContainerprotocoldefinesthreerequiredcapabilitiesthatanycontainermustprovide:

Itmustbepossibletoaddanewitemtothecontainerwithanappend(_:)method.

ItmustbepossibletoaccessacountoftheitemsinthecontainerthroughacountpropertythatreturnsanIntvalue.

ItmustbepossibletoretrieveeachiteminthecontainerwithasubscriptthattakesanIntindexvalue.

Thisprotocoldoesn’tspecifyhowtheitemsinthecontainershouldbestoredorwhattypethey’reallowedtobe.TheprotocolonlyspecifiesthethreebitsoffunctionalitythatanytypemustprovideinordertobeconsideredaContainer.Aconformingtypecanprovideadditionalfunctionality,aslongasitsatisfiesthesethreerequirements.

AnytypethatconformstotheContainerprotocolmustbeabletospecifythetypeofvaluesitstores.Specifically,itmustensurethatonlyitemsoftherighttypeareaddedtothecontainer,anditmustbeclearaboutthetypeoftheitemsreturnedbyitssubscript.

Todefinetheserequirements,theContainerprotocolneedsawaytorefertothetypeoftheelementsthatacontainerwillhold,withoutknowingwhatthattypeis

foraspecificcontainer.TheContainerprotocolneedstospecifythatanyvaluepassedtotheappend(_:)methodmusthavethesametypeasthecontainer’selementtype,andthatthevaluereturnedbythecontainer’ssubscriptwillbeofthesametypeasthecontainer’selementtype.

Toachievethis,theContainerprotocoldeclaresanassociatedtypecalledItem,writtenasassociatedtypeItem.Theprotocoldoesn’tdefinewhatItemis—thatinformationisleftforanyconformingtypetoprovide.Nonetheless,theItemaliasprovidesawaytorefertothetypeoftheitemsinaContainer,andtodefineatypeforusewiththeappend(_:)methodandsubscript,toensurethattheexpectedbehaviorofanyContainerisenforced.

Here’saversionofthenongenericIntStacktypefromGenericTypesabove,adaptedtoconformtotheContainerprotocol:

1 structIntStack:Container{

2 //originalIntStackimplementation

3 varitems=[Int]()

4 mutatingfuncpush(_item:Int){

5 items.append(item)

6 }

7 mutatingfuncpop()->Int{

8 returnitems.removeLast()

9 }

10 //conformancetotheContainerprotocol

11 typealiasItem=Int

12 mutatingfuncappend(_item:Int){

13 self.push(item)

14 }

15 varcount:Int{

16 returnitems.count

17 }

18 subscript(i:Int)->Int{

19 returnitems[i]

20 }

21 }

TheIntStacktypeimplementsallthreeoftheContainerprotocol’srequirements,andineachcasewrapspartoftheIntStacktype’sexistingfunctionalitytosatisfytheserequirements.

Moreover,IntStackspecifiesthatforthisimplementationofContainer,theappropriateItemtouseisatypeofInt.ThedefinitionoftypealiasItem=IntturnstheabstracttypeofItemintoaconcretetypeofIntforthisimplementationoftheContainerprotocol.

ThankstoSwift’stypeinference,youdon’tactuallyneedtodeclareaconcreteItemofIntaspartofthedefinitionofIntStack.BecauseIntStackconformstoalloftherequirementsoftheContainerprotocol,SwiftcaninfertheappropriateItemtouse,simplybylookingatthetypeoftheappend(_:)method’sitemparameterandthereturntypeofthesubscript.Indeed,ifyoudeletethetypealiasItem=Intlinefromthecodeabove,everythingstillworks,becauseit’sclearwhattypeshouldbeusedforItem.

YoucanalsomakethegenericStacktypeconformtotheContainerprotocol:

1 structStack<Element>:Container{

2 //originalStack<Element>implementation

3 varitems=[Element]()

4 mutatingfuncpush(_item:Element){

5 items.append(item)

6 }

7 mutatingfuncpop()->Element{

8 returnitems.removeLast()

9 }

10 //conformancetotheContainerprotocol

11 mutatingfuncappend(_item:Element){

12 self.push(item)

13 }

14 varcount:Int{

15 returnitems.count

16 }

17 subscript(i:Int)->Element{

18 returnitems[i]

19 }

20 }

Thistime,thetypeparameterElementisusedasthetypeoftheappend(_:)method’sitemparameterandthereturntypeofthesubscript.SwiftcanthereforeinferthatElementistheappropriatetypetouseastheItemforthisparticularcontainer.

ExtendinganExistingTypetoSpecifyanAssociatedTypeYoucanextendanexistingtypetoaddconformancetoaprotocol,asdescribedinAddingProtocolConformancewithanExtension.Thisincludesaprotocolwithanassociatedtype.

Swift’sArraytypealreadyprovidesanappend(_:)method,acountproperty,andasubscriptwithanIntindextoretrieveitselements.ThesethreecapabilitiesmatchtherequirementsoftheContainerprotocol.ThismeansthatyoucanextendArraytoconformtotheContainerprotocolsimplybydeclaringthatArrayadoptstheprotocol.Youdothiswithanemptyextension,asdescribedinDeclaringProtocolAdoptionwithanExtension:

extensionArray:Container{}

Array’sexistingappend(_:)methodandsubscriptenableSwifttoinfertheappropriatetypetouseforItem,justasforthegenericStacktypeabove.Afterdefiningthisextension,youcanuseanyArrayasaContainer.

AddingConstraintstoanAssociatedTypeYoucanaddtypeconstraintstoanassociatedtypeinaprotocoltorequirethatconformingtypessatisfythoseconstraints.Forexample,thefollowingcodedefinesaversionofContainerthatrequirestheitemsinthecontainertobeequatable.

1 protocolContainer{

2 associatedtypeItem:Equatable

3 mutatingfuncappend(_item:Item)

4 varcount:Int{get}

5 subscript(i:Int)->Item{get}

6 }

ToconformtothisversionofContainer,thecontainer’sItemtypehastoconformtotheEquatableprotocol.

UsingaProtocolinItsAssociatedType’sConstraintsAprotocolcanappearaspartofitsownrequirements.Forexample,here’saprotocolthatrefinestheContainerprotocol,addingtherequirementofasuffix(_:)method.Thesuffix(_:)methodreturnsagivennumberofelementsfromtheendofthecontainer,storingtheminaninstanceoftheSuffixtype.

1 protocolSuffixableContainer:Container{

2 associatedtypeSuffix:SuffixableContainerwhere

Suffix.Item==Item

3 funcsuffix(_size:Int)->Suffix

4 }

Inthisprotocol,Suffixisanassociatedtype,liketheItemtypeintheContainerexampleabove.Suffixhastwoconstraints:ItmustconformtotheSuffixableContainerprotocol(theprotocolcurrentlybeingdefined),anditsItemtypemustbethesameasthecontainer’sItemtype.TheconstraintonItem

isagenericwhereclause,whichisdiscussedinAssociatedTypeswithaGenericWhereClausebelow.

Here’sanextensionoftheStacktypefromGenericTypesabovethataddsconformancetotheSuffixableContainerprotocol:

1 extensionStack:SuffixableContainer{

2 funcsuffix(_size:Int)->Stack{

3 varresult=Stack()

4 forindexin(count-size)..<count{

5 result.append(self[index])

6 }

7 returnresult

8 }

9 //InferredthatSuffixisStack.

10 }

11 varstackOfInts=Stack<Int>()

12 stackOfInts.append(10)

13 stackOfInts.append(20)

14 stackOfInts.append(30)

15 letsuffix=stackOfInts.suffix(2)

16 //suffixcontains20and30

Intheexampleabove,theSuffixassociatedtypeforStackisalsoStack,sothesuffixoperationonStackreturnsanotherStack.Alternatively,atypethatconformstoSuffixableContainercanhaveaSuffixtypethat’sdifferentfromitself—meaningthesuffixoperationcanreturnadifferenttype.Forexample,here’sanextensiontothenongenericIntStacktypethataddsSuffixableContainerconformance,usingStack<Int>asitssuffixtypeinsteadofIntStack:

1 extensionIntStack:SuffixableContainer{

2 funcsuffix(_size:Int)->Stack<Int>{

3 varresult=Stack<Int>()

4 forindexin(count-size)..<count{

5 result.append(self[index])

6 }

7 returnresult

8 }

9 //InferredthatSuffixisStack<Int>.

10 }

GenericWhereClauses

Typeconstraints,asdescribedinTypeConstraints,enableyoutodefinerequirementsonthetypeparametersassociatedwithagenericfunction,subscript,ortype.

Itcanalsobeusefultodefinerequirementsforassociatedtypes.Youdothisbydefiningagenericwhereclause.Agenericwhereclauseenablesyoutorequirethatanassociatedtypemustconformtoacertainprotocol,orthatcertaintypeparametersandassociatedtypesmustbethesame.Agenericwhereclausestartswiththewherekeyword,followedbyconstraintsforassociatedtypesorequalityrelationshipsbetweentypesandassociatedtypes.Youwriteagenericwhereclauserightbeforetheopeningcurlybraceofatypeorfunction’sbody.

TheexamplebelowdefinesagenericfunctioncalledallItemsMatch,whichcheckstoseeiftwoContainerinstancescontainthesameitemsinthesameorder.ThefunctionreturnsaBooleanvalueoftrueifallitemsmatchandavalueoffalseiftheydon’t.

Thetwocontainerstobecheckeddon’thavetobethesametypeofcontainer(althoughtheycanbe),buttheydohavetoholdthesametypeofitems.Thisrequirementisexpressedthroughacombinationoftypeconstraintsandagenericwhereclause:

1 funcallItemsMatch<C1:Container,C2:Container>

2 (_someContainer:C1,_anotherContainer:C2)->Bool

3 whereC1.Item==C2.Item,C1.Item:Equatable{

4

5 //Checkthatbothcontainerscontainthesamenumber

ofitems.

6 ifsomeContainer.count!=anotherContainer.count{

7 returnfalse

8 }

9

10 //Checkeachpairofitemstoseeifthey're

equivalent.

11 foriin0..<someContainer.count{

12 ifsomeContainer[i]!=anotherContainer[i]{

13 returnfalse

14 }

15 }

16

17 //Allitemsmatch,soreturntrue.

18 returntrue

19 }

ThisfunctiontakestwoargumentscalledsomeContainerandanotherContainer.ThesomeContainerargumentisoftypeC1,andtheanotherContainerargumentisoftypeC2.BothC1andC2aretypeparametersfortwocontainertypestobedeterminedwhenthefunctioniscalled.

Thefollowingrequirementsareplacedonthefunction’stwotypeparameters:

C1mustconformtotheContainerprotocol(writtenasC1:Container).

C2mustalsoconformtotheContainerprotocol(writtenasC2:Container).

TheItemforC1mustbethesameastheItemforC2(writtenasC1.Item==C2.Item).

TheItemforC1mustconformtotheEquatableprotocol(writtenasC1.Item:Equatable).

Thefirstandsecondrequirementsaredefinedinthefunction’stypeparameterlist,andthethirdandfourthrequirementsaredefinedinthefunction’sgenericwhereclause.

Theserequirementsmean:

someContainerisacontaineroftypeC1.

anotherContainerisacontaineroftypeC2.

someContainerandanotherContainercontainthesametypeofitems.

TheitemsinsomeContainercanbecheckedwiththenotequaloperator(!=)toseeifthey’redifferentfromeachother.

ThethirdandfourthrequirementscombinetomeanthattheitemsinanotherContainercanalsobecheckedwiththe!=operator,becausethey’reexactlythesametypeastheitemsinsomeContainer.

TheserequirementsenabletheallItemsMatch(_:_:)functiontocomparethetwocontainers,evenifthey’reofadifferentcontainertype.

TheallItemsMatch(_:_:)functionstartsbycheckingthatbothcontainerscontainthesamenumberofitems.Iftheycontainadifferentnumberofitems,there’snowaythattheycanmatch,andthefunctionreturnsfalse.

Aftermakingthischeck,thefunctioniteratesoveralloftheitemsinsomeContainerwithafor-inloopandthehalf-openrangeoperator(..<).Foreachitem,thefunctioncheckswhethertheitemfromsomeContainerisn’tequaltothecorrespondingiteminanotherContainer.Ifthetwoitemsaren’tequal,thenthetwocontainersdon’tmatch,andthefunctionreturnsfalse.

Iftheloopfinisheswithoutfindingamismatch,thetwocontainersmatch,and

thefunctionreturnstrue.

Here’showtheallItemsMatch(_:_:)functionlooksinaction:

1 varstackOfStrings=Stack<String>()

2 stackOfStrings.push("uno")

3 stackOfStrings.push("dos")

4 stackOfStrings.push("tres")

5

6 vararrayOfStrings=["uno","dos","tres"]

7

8 ifallItemsMatch(stackOfStrings,arrayOfStrings){

9 print("Allitemsmatch.")

10 }else{

11 print("Notallitemsmatch.")

12 }

13 //Prints"Allitemsmatch."

TheexampleabovecreatesaStackinstancetostoreStringvalues,andpushesthreestringsontothestack.TheexamplealsocreatesanArrayinstanceinitializedwithanarrayliteralcontainingthesamethreestringsasthestack.Eventhoughthestackandthearrayareofadifferenttype,theybothconformtotheContainerprotocol,andbothcontainthesametypeofvalues.YoucanthereforecalltheallItemsMatch(_:_:)functionwiththesetwocontainersasitsarguments.Intheexampleabove,theallItemsMatch(_:_:)functioncorrectlyreportsthatalloftheitemsinthetwocontainersmatch.

ExtensionswithaGenericWhereClause

Youcanalsouseagenericwhereclauseaspartofanextension.TheexamplebelowextendsthegenericStackstructurefromthepreviousexamplestoaddanisTop(_:)method.

1 extensionStackwhereElement:Equatable{

2 funcisTop(_item:Element)->Bool{

3 guardlettopItem=items.lastelse{

4 returnfalse

5 }

6 returntopItem==item

7 }

8 }

ThisnewisTop(_:)methodfirstchecksthatthestackisn’tempty,andthencomparesthegivenitemagainstthestack’stopmostitem.Ifyoutriedtodothiswithoutagenericwhereclause,youwouldhaveaproblem:TheimplementationofisTop(_:)usesthe==operator,butthedefinitionofStackdoesn’trequireitsitemstobeequatable,sousingthe==operatorresultsinacompile-timeerror.Usingagenericwhereclauseletsyouaddanewrequirementtotheextension,sothattheextensionaddstheisTop(_:)methodonlywhentheitemsinthestackareequatable.

Here’showtheisTop(_:)methodlooksinaction:

1 ifstackOfStrings.isTop("tres"){

2 print("Topelementistres.")

3 }else{

4 print("Topelementissomethingelse.")

5 }

6 //Prints"Topelementistres."

IfyoutrytocalltheisTop(_:)methodonastackwhoseelementsaren’tequatable,you’llgetacompile-timeerror.

1 structNotEquatable{}

2 varnotEquatableStack=Stack<NotEquatable>()

3 letnotEquatableValue=NotEquatable()

4 notEquatableStack.push(notEquatableValue)

5 notEquatableStack.isTop(notEquatableValue)//Error

Youcanuseagenericwhereclausewithextensionstoaprotocol.TheexamplebelowextendstheContainerprotocolfromthepreviousexamplestoaddastartsWith(_:)method.

1 extensionContainerwhereItem:Equatable{

2 funcstartsWith(_item:Item)->Bool{

3 returncount>=1&&self[0]==item

4 }

5 }

ThestartsWith(_:)methodfirstmakessurethatthecontainerhasatleastoneitem,andthenitcheckswhetherthefirstiteminthecontainermatchesthegivenitem.ThisnewstartsWith(_:)methodcanbeusedwithanytypethatconformstotheContainerprotocol,includingthestacksandarraysusedabove,aslongasthecontainer’sitemsareequatable.

1 if[9,9,9].startsWith(42){

2 print("Startswith42.")

3 }else{

4 print("Startswithsomethingelse.")

5 }

6 //Prints"Startswithsomethingelse."

ThegenericwhereclauseintheexampleaboverequiresItemtoconformtoaprotocol,butyoucanalsowriteagenericwhereclausesthatrequireItemtobeaspecifictype.Forexample:

1 extensionContainerwhereItem==Double{

2 funcaverage()->Double{

3 varsum=0.0

4 forindexin0..<count{

5 sum+=self[index]

6 }

7 returnsum/Double(count)

8 }

9 }

10 print([1260.0,1200.0,98.6,37.0].average())

11 //Prints"648.9"

Thisexampleaddsanaverage()methodtocontainerswhoseItemtypeisDouble.Ititeratesovertheitemsinthecontainertoaddthemup,anddividesbythecontainer’scounttocomputetheaverage.ItexplicitlyconvertsthecountfromInttoDoubletobeabletodofloating-pointdivision.

Youcanincludemultiplerequirementsinagenericwhereclausethatispartofanextension,justlikeyoucanforagenericwhereclausethatyouwriteelsewhere.Separateeachrequirementinthelistwithacomma.

AssociatedTypeswithaGenericWhereClause

Youcanincludeagenericwhereclauseonanassociatedtype.Forexample,supposeyouwanttomakeaversionofContainerthatincludesaniterator,likewhattheSequenceprotocolusesinthestandardlibrary.Here’showyouwritethat:

1 protocolContainer{

2 associatedtypeItem

3 mutatingfuncappend(_item:Item)

4 varcount:Int{get}

5 subscript(i:Int)->Item{get}

6

7 associatedtypeIterator:IteratorProtocolwhere

Iterator.Element==Item

8 funcmakeIterator()->Iterator

9 }

ThegenericwhereclauseonIteratorrequiresthattheiteratormusttraverseoverelementsofthesameitemtypeasthecontainer’sitems,regardlessoftheiterator’stype.ThemakeIterator()functionprovidesaccesstoacontainer’siterator.

Foraprotocolthatinheritsfromanotherprotocol,youaddaconstrainttoaninheritedassociatedtypebyincludingthegenericwhereclauseintheprotocoldeclaration.Forexample,thefollowingcodedeclaresaComparableContainerprotocolthatrequiresItemtoconformtoComparable:

protocolComparableContainer:ContainerwhereItem:Comparable

{}

GenericSubscripts

Subscriptscanbegeneric,andtheycanincludegenericwhereclauses.Youwritetheplaceholdertypenameinsideanglebracketsaftersubscript,andyouwriteagenericwhereclauserightbeforetheopeningcurlybraceofthesubscript’sbody.Forexample:

1 extensionContainer{

2 subscript<Indices:Sequence>(indices:Indices)->[Item]

3 whereIndices.Iterator.Element==Int{

4 varresult=[Item]()

5 forindexinindices{

6 result.append(self[index])

7 }

8 returnresult

9 }

10 }

ThisextensiontotheContainerprotocoladdsasubscriptthattakesasequenceofindicesandreturnsanarraycontainingtheitemsateachgivenindex.Thisgenericsubscriptisconstrainedasfollows:

ThegenericparameterIndicesinanglebracketshastobeatypethatconformstotheSequenceprotocolfromthestandardlibrary.

Thesubscripttakesasingleparameter,indices,whichisaninstanceofthatIndicestype.

ThegenericwhereclauserequiresthattheiteratorforthesequencemusttraverseoverelementsoftypeInt.Thisensuresthattheindicesinthesequencearethesametypeastheindicesusedforacontainer.

Takentogether,theseconstraintsmeanthatthevaluepassedfortheindicesparameterisasequenceofintegers.

OpaqueTypes

Afunctionormethodwithanopaquereturntypehidesitsreturnvalue’stypeinformation.Insteadofprovidingaconcretetypeasthefunction’sreturntype,thereturnvalueisdescribedintermsoftheprotocolsitsupports.Hidingtypeinformationisusefulatboundariesbetweenamoduleandcodethatcallsintothemodule,becausetheunderlyingtypeofthereturnvaluecanremainprivate.Unlikereturningavaluewhosetypeisaprotocoltype,opaquetypespreservetypeidentity—thecompilerhasaccesstothetypeinformation,butclientsofthemoduledon’t.

TheProblemThatOpaqueTypesSolve

Forexample,supposeyou’rewritingamodulethatdrawsASCIIartshapes.ThebasiccharacteristicofanASCIIartshapeisadraw()functionthatreturnsthestringrepresentationofthatshape,whichyoucanuseastherequirementfortheShapeprotocol:

1 protocolShape{

2 funcdraw()->String

3 }

4

5 structTriangle:Shape{

6 varsize:Int

7 funcdraw()->String{

8 varresult=[String]()

9 forlengthin1...size{

10 result.append(String(repeating:"*",count:

length))

11 }

12 returnresult.joined(separator:"\n")

13 }

14 }

15 letsmallTriangle=Triangle(size:3)

16 print(smallTriangle.draw())

17 //*

18 //**

19 //***

Youcouldusegenericstoimplementoperationslikeflippingashapevertically,asshowninthecodebelow.However,there’sanimportantlimitationtothisapproach:Theflippedresultexposestheexactgenerictypesthatwereusedtocreateit.

1 structFlippedShape<T:Shape>:Shape{

2 varshape:T

3 funcdraw()->String{

4 letlines=shape.draw().split(separator:"\n")

5 returnlines.reversed().joined(separator:"\n")

6 }

7 }

8 letflippedTriangle=FlippedShape(shape:smallTriangle)

9 print(flippedTriangle.draw())

10 //***

11 //**

12 //*

ThisapproachtodefiningaJoinedShape<T:Shape,U:Shape>structurethatjoinstwoshapestogethervertically,likethecodebelowshows,resultsintypeslikeJoinedShape<FlippedShape<Triangle>,Triangle>fromjoiningaflippedtrianglewithanothertriangle.

1 structJoinedShape<T:Shape,U:Shape>:Shape{

2 vartop:T

3 varbottom:U

4 funcdraw()->String{

5 returntop.draw()+"\n"+bottom.draw()

6 }

7 }

8 letjoinedTriangles=JoinedShape(top:smallTriangle,bottom:

flippedTriangle)

9 print(joinedTriangles.draw())

10 //*

11 //**

12 //***

13 //***

14 //**

15 //*

Exposingdetailedinformationaboutthecreationofashapeallowstypesthataren’tmeanttobepartoftheASCIIartmodule’spublicinterfacetoleakoutbecauseoftheneedtostatethefullreturntype.Thecodeinsidethemodulecouldbuildupthesameshapeinavarietyofways,andothercodeoutsidethemodulethatusestheshapeshouldn’thavetoaccountfortheimplementationdetailsaboutthelistoftransformations.WrappertypeslikeJoinedShapeandFlippedShapedon’tmattertothemodule’susers,andtheyshouldn’tbevisible.Themodule’spublicinterfaceconsistsofoperationslikejoiningandflippingashape,andthoseoperationsreturnanotherShapevalue.

ReturninganOpaqueType

Youcanthinkofanopaquetypelikebeingthereverseofagenerictype.Generictypesletthecodethatcallsafunctionpickthetypeforthatfunction’s

parametersandreturnvalueinawaythat’sabstractedawayfromthefunctionimplementation.Forexample,thefunctioninthefollowingcodereturnsatypethatdependsonitscaller:

funcmax<T>(_x:T,_y:T)->TwhereT:Comparable{...}

Thecodethatcallsmax(_:_:)choosesthevaluesforxandy,andthetypeofthosevaluesdeterminestheconcretetypeofT.ThecallingcodecanuseanytypethatconformstotheComparableprotocol.Thecodeinsidethefunctioniswritteninageneralwaysoitcanhandlewhatevertypethecallerprovides.Theimplementationofmax(_:_:)usesonlyfunctionalitythatallComparabletypesshare.

Thoserolesarereversedforafunctionwithanopaquereturntype.Anopaquetypeletsthefunctionimplementationpickthetypeforthevalueitreturnsinawaythat’sabstractedawayfromthecodethatcallsthefunction.Forexample,thefunctioninthefollowingexamplereturnsatrapezoidwithoutexposingtheunderlyingtypeofthatshape.

1 structSquare:Shape{

2 varsize:Int

3 funcdraw()->String{

4 letline=String(repeating:"*",count:size)

5 letresult=Array<String>(repeating:line,count:

size)

6 returnresult.joined(separator:"\n")

7 }

8 }

9

10 funcmakeTrapezoid()->someShape{

11 lettop=Triangle(size:2)

12 letmiddle=Square(size:2)

13 letbottom=FlippedShape(shape:top)

14 lettrapezoid=JoinedShape(

15 top:top,

16 bottom:JoinedShape(top:middle,bottom:bottom)

17 )

18 returntrapezoid

19 }

20 lettrapezoid=makeTrapezoid()

21 print(trapezoid.draw())

22 //*

23 //**

24 //**

25 //**

26 //**

27 //*

ThemakeTrapezoid()functioninthisexampledeclaresitsreturntypeassomeShape;asaresult,thefunctionreturnsavalueofsomegiventypethatconformstotheShapeprotocol,withoutspecifyinganyparticularconcretetype.WritingmakeTrapezoid()thiswayletsitexpressthefundamentalaspectofitspublicinterface—thevalueitreturnsisashape—withoutmakingthespecifictypesthattheshapeismadefromapartofitspublicinterface.Thisimplementationusestwotrianglesandasquare,butthefunctioncouldberewrittentodrawatrapezoidinavarietyofotherwayswithoutchangingitsreturntype.

Thisexamplehighlightsthewaythatanopaquereturntypeislikethereverseofagenerictype.ThecodeinsidemakeTrapezoid()canreturnanytypeitneedsto,aslongasthattypeconformstotheShapeprotocol,likethecallingcodedoesforagenericfunction.Thecodethatcallsthefunctionneedstobewritteninageneralway,liketheimplementationofagenericfunction,sothatitcanworkwithanyShapevaluethat’sreturnedbymakeTrapezoid().

Youcanalsocombineopaquereturntypeswithgenerics.ThefunctionsinthefollowingcodebothreturnavalueofsometypethatconformstotheShapeprotocol.

1 funcflip<T:Shape>(_shape:T)->someShape{

2 returnFlippedShape(shape:shape)

3 }

4 funcjoin<T:Shape,U:Shape>(_top:T,_bottom:U)->some

Shape{

5 JoinedShape(top:top,bottom:bottom)

6 }

7

8 letopaqueJoinedTriangles=join(smallTriangle,

flip(smallTriangle))

9 print(opaqueJoinedTriangles.draw())

10 //*

11 //**

12 //***

13 //***

14 //**

15 //*

ThevalueofopaqueJoinedTrianglesinthisexampleisthesameasjoinedTrianglesinthegenericsexampleintheTheProblemThatOpaqueTypesSolvesectionearlierinthischapter.However,unlikethevalueinthatexample,flip(_:)andjoin(_:_:)wraptheunderlyingtypesthatthegenericshapeoperationsreturninanopaquereturntype,whichpreventsthosetypesfrombeingvisible.Bothfunctionsaregenericbecausethetypestheyrelyonaregeneric,andthetypeparameterstothefunctionpassalongthetypeinformationneededbyFlippedShapeandJoinedShape.

Ifafunctionwithanopaquereturntypereturnsfrommultipleplaces,allofthepossiblereturnvaluesmusthavethesametype.Foragenericfunction,thatreturntypecanusethefunction’sgenerictypeparameters,butitmuststillbeasingletype.Forexample,here’saninvalidversionoftheshape-flippingfunctionthatincludesaspecialcaseforsquares:

1 funcinvalidFlip<T:Shape>(_shape:T)->someShape{

2 ifshapeisSquare{

3 returnshape//Error:returntypesdon'tmatch

4 }

5 returnFlippedShape(shape:shape)//Error:returntypes

don'tmatch

6 }

IfyoucallthisfunctionwithaSquare,itreturnsaSquare;otherwise,itreturnsaFlippedShape.ThisviolatestherequirementtoreturnvaluesofonlyonetypeandmakesinvalidFlip(_:)invalidcode.OnewaytofixinvalidFlip(_:)istomovethespecialcaseforsquaresintotheimplementationofFlippedShape,whichletsthisfunctionalwaysreturnaFlippedShapevalue:

1 structFlippedShape<T:Shape>:Shape{

2 varshape:T

3 funcdraw()->String{

4 ifshapeisSquare{

5 returnshape.draw()

6 }

7 letlines=shape.draw().split(separator:"\n")

8 returnlines.reversed().joined(separator:"\n")

9 }

10 }

Therequirementtoalwaysreturnasingletypedoesn’tpreventyoufromusinggenericsinanopaquereturntype.Here’sanexampleofafunctionthatincorporatesitstypeparameterintotheunderlyingtypeofthevalueitreturns:

1 func`repeat`<T:Shape>(shape:T,count:Int)->some

Collection{

2 returnArray<T>(repeating:shape,count:count)

3 }

Inthiscase,theunderlyingtypeofthereturnvaluevariesdependingonT:Whatevershapeispassedit,repeat(shape:count:)createsandreturnsanarrayofthatshape.Nevertheless,thereturnvaluealwayshasthesameunderlyingtypeof[T],soitfollowstherequirementthatfunctionswithopaquereturntypesmustreturnvaluesofonlyasingletype.

DifferencesBetweenOpaqueTypesandProtocolTypes

Returninganopaquetypelooksverysimilartousingaprotocoltypeasthereturntypeofafunction,butthesetwokindsofreturntypedifferinwhethertheypreservetypeidentity.Anopaquetypereferstoonespecifictype,althoughthecallerofthefunctionisn’tabletoseewhichtype;aprotocoltypecanrefertoanytypethatconformstotheprotocol.Generallyspeaking,protocoltypesgiveyoumoreflexibilityabouttheunderlyingtypesofthevaluestheystore,andopaquetypesletyoumakestrongerguaranteesaboutthoseunderlyingtypes.

Forexample,here’saversionofflip(_:)thatreturnsavalueofprotocoltypeinsteadofusinganopaquereturntype:

1 funcprotoFlip<T:Shape>(_shape:T)->Shape{

2 returnFlippedShape(shape:shape)

3 }

ThisversionofprotoFlip(_:)hasthesamebodyasflip(_:),anditalwaysreturnsavalueofthesametype.Unlikeflip(_:),thevaluethatprotoFlip(_:)returnsisn’trequiredtoalwayshavethesametype—itjusthastoconformtotheShapeprotocol.Putanotherway,protoFlip(_:)makesamuchlooserAPIcontractwithitscallerthanflip(_:)makes.Itreservestheflexibilitytoreturnvaluesofmultipletypes:

1 funcprotoFlip<T:Shape>(_shape:T)->Shape{

2 ifshapeisSquare{

3 returnshape

4 }

5

6 returnFlippedShape(shape:shape)

7 }

TherevisedversionofthecodereturnsaninstanceofSquareoraninstanceofFlippedShape,dependingonwhatshapeispassedin.Twoflippedshapesreturnedbythisfunctionmighthavecompletelydifferenttypes.Othervalidversionsofthisfunctioncouldreturnvaluesofdifferenttypeswhenflippingmultipleinstancesofthesameshape.ThelessspecificreturntypeinformationfromprotoFlip(_:)meansthatmanyoperationsthatdependontypeinformationaren’tavailableonthereturnedvalue.Forexample,it’snotpossibletowritean==operatorcomparingresultsreturnedbythisfunction.

1 letprotoFlippedTriangle=protoFlip(smallTriangle)

2 letsameThing=protoFlip(smallTriangle)

3 protoFlippedTriangle==sameThing//Error

Theerroronthelastlineoftheexampleoccursforseveralreasons.TheimmediateissueisthattheShapedoesn’tincludean==operatoraspartofitsprotocolrequirements.Ifyoutryaddingone,thenextissueyou’llencounteristhatthe==operatorneedstoknowthetypesofitsleft-handandright-handarguments.ThissortofoperatorusuallytakesargumentsoftypeSelf,matchingwhateverconcretetypeadoptstheprotocol,butaddingaSelfrequirementtotheprotocoldoesn’tallowforthetypeerasurethathappenswhenyouusetheprotocolasatype.

Usingaprotocoltypeasthereturntypeforafunctiongivesyoutheflexibilitytoreturnanytypethatconformstotheprotocol.However,thecostofthatflexibilityisthatsomeoperationsaren’tpossibleonthereturnedvalues.Theexampleshowshowthe==operatorisn’tavailable—itdependsonspecifictypeinformationthatisn’tpreservedbyusingaprotocoltype.

Anotherproblemwiththisapproachisthattheshapetransformationsdon’tnest.

TheresultofflippingatriangleisavalueoftypeShape,andtheprotoFlip(_:)functiontakesanargumentofsometypethatconformstotheShapeprotocol.However,avalueofaprotocoltypedoesn’tconformtothatprotocol;thevaluereturnedbyprotoFlip(_:)doesn’tconformtoShape.ThismeanscodelikeprotoFlip(protoFlip(smallTriange))thatappliesmultipletransformationsisinvalidbecausetheflippedshapeisn’tavalidargumenttoprotoFlip(_:).

Incontrast,opaquetypespreservetheidentityoftheunderlyingtype.Swiftcaninferassociatedtypes,whichletsyouuseanopaquereturnvalueinplaceswhereaprotocoltypecan’tbeusedasareturnvalue.Forexample,here’saversionoftheContainerprotocolfromGenerics:

1 protocolContainer{

2 associatedtypeItem

3 varcount:Int{get}

4 subscript(i:Int)->Item{get}

5 }

6 extensionArray:Container{}

Youcan’tuseContainerasthereturntypeofafunctionbecausethatprotocolhasanassociatedtype.Youalsocan’tuseitasconstraintagenericreturntypebecausethereisn’tenoughinformationoutsidethefunctionbodytoinferwhatthegenerictypeneedstobe.

1 //Error:Protocolwithassociatedtypescan'tbeusedasa

returntype.

2 funcmakeProtocolContainer<T>(item:T)->Container{

3 return[item]

4 }

5

6 //Error:NotenoughinformationtoinferC.

7 funcmakeProtocolContainer<T,C:Container>(item:T)->C{

8 return[item]

9 }

UsingtheopaquetypesomeContainerasareturntypeexpressesthedesiredAPIcontract—thefunctionreturnsacontainer,butdeclinestospecifythecontainer’stype:

1 funcmakeOpaqueContainer<T>(item:T)->someContainer{

2 return[item]

3 }

4 letopaqueContainer=makeOpaqueContainer(item:12)

5 lettwelve=opaqueContainer[0]

6 print(type(of:twelve))

7 //Prints"Int"

ThetypeoftwelveisinferredtobeInt,whichillustratesthefactthattypeinferenceworkswithopaquetypes.IntheimplementationofmakeOpaqueContainer(item:),theunderlyingtypeoftheopaquecontaineris[T].Inthiscase,TisInt,sothereturnvalueisanarrayofintegersandtheItemassociatedtypeisinferredtobeInt.ThesubscriptonContainerreturnsItem,whichmeansthatthetypeoftwelveisalsoinferredtobeInt.

AutomaticReferenceCounting

SwiftusesAutomaticReferenceCounting(ARC)totrackandmanageyourapp’smemoryusage.Inmostcases,thismeansthatmemorymanagement“justworks”inSwift,andyoudonotneedtothinkaboutmemorymanagementyourself.ARCautomaticallyfreesupthememoryusedbyclassinstanceswhenthoseinstancesarenolongerneeded.

However,inafewcasesARCrequiresmoreinformationabouttherelationshipsbetweenpartsofyourcodeinordertomanagememoryforyou.ThischapterdescribesthosesituationsandshowshowyouenableARCtomanageallofyourapp’smemory.UsingARCinSwiftisverysimilartotheapproachdescribedinTransitioningtoARCReleaseNotesforusingARCwithObjective-C.

Referencecountingappliesonlytoinstancesofclasses.Structuresandenumerationsarevaluetypes,notreferencetypes,andarenotstoredandpassedbyreference.

HowARCWorks

Everytimeyoucreateanewinstanceofaclass,ARCallocatesachunkofmemorytostoreinformationaboutthatinstance.Thismemoryholdsinformationaboutthetypeoftheinstance,togetherwiththevaluesofanystoredpropertiesassociatedwiththatinstance.

Additionally,whenaninstanceisnolongerneeded,ARCfreesupthememoryusedbythatinstancesothatthememorycanbeusedforotherpurposesinstead.Thisensuresthatclassinstancesdonottakeupspaceinmemorywhentheyarenolongerneeded.

However,ifARCweretodeallocateaninstancethatwasstillinuse,itwouldnolongerbepossibletoaccessthatinstance’sproperties,orcallthatinstance’smethods.Indeed,ifyoutriedtoaccesstheinstance,yourappwouldmostlikelycrash.

Tomakesurethatinstancesdon’tdisappearwhiletheyarestillneeded,ARCtrackshowmanyproperties,constants,andvariablesarecurrentlyreferringtoeachclassinstance.ARCwillnotdeallocateaninstanceaslongasatleastoneactivereferencetothatinstancestillexists.

Tomakethispossible,wheneveryouassignaclassinstancetoaproperty,constant,orvariable,thatproperty,constant,orvariablemakesastrongreferencetotheinstance.Thereferenceiscalleda“strong”referencebecauseitkeepsafirmholdonthatinstance,anddoesnotallowittobedeallocatedforaslongasthatstrongreferenceremains.

ARCinAction

Here’sanexampleofhowAutomaticReferenceCountingworks.ThisexamplestartswithasimpleclasscalledPerson,whichdefinesastoredconstantpropertycalledname:

1 classPerson{

2 letname:String

3 init(name:String){

4 self.name=name

5 print("\(name)isbeinginitialized")

6 }

7 deinit{

8 print("\(name)isbeingdeinitialized")

9 }

10 }

ThePersonclasshasaninitializerthatsetstheinstance’snamepropertyandprintsamessagetoindicatethatinitializationisunderway.ThePersonclassalsohasadeinitializerthatprintsamessagewhenaninstanceoftheclassisdeallocated.

ThenextcodesnippetdefinesthreevariablesoftypePerson?,whichareusedtosetupmultiplereferencestoanewPersoninstanceinsubsequentcodesnippets.Becausethesevariablesareofanoptionaltype(Person?,notPerson),theyareautomaticallyinitializedwithavalueofnil,anddonotcurrentlyreferenceaPersoninstance.

1 varreference1:Person?

2 varreference2:Person?

3 varreference3:Person?

YoucannowcreateanewPersoninstanceandassignittooneofthesethreevariables:

1 reference1=Person(name:"JohnAppleseed")

2 //Prints"JohnAppleseedisbeinginitialized"

Notethatthemessage"JohnAppleseedisbeinginitialized"isprintedatthepointthatyoucallthePersonclass’sinitializer.Thisconfirmsthatinitializationhastakenplace.

BecausethenewPersoninstancehasbeenassignedtothereference1variable,thereisnowastrongreferencefromreference1tothenewPersoninstance.Becausethereisatleastonestrongreference,ARCmakessurethatthisPersoniskeptinmemoryandisnotdeallocated.

IfyouassignthesamePersoninstancetotwomorevariables,twomorestrongreferencestothatinstanceareestablished:

1 reference2=reference1

2 reference3=reference1

TherearenowthreestrongreferencestothissinglePersoninstance.

Ifyoubreaktwoofthesestrongreferences(includingtheoriginalreference)byassigningniltotwoofthevariables,asinglestrongreferenceremains,andthePersoninstanceisnotdeallocated:

1 reference1=nil

2 reference2=nil

ARCdoesnotdeallocatethePersoninstanceuntilthethirdandfinalstrongreferenceisbroken,atwhichpointit’sclearthatyouarenolongerusingthePersoninstance:

1 reference3=nil

2 //Prints"JohnAppleseedisbeingdeinitialized"

StrongReferenceCyclesBetweenClassInstances

Intheexamplesabove,ARCisabletotrackthenumberofreferencestothenewPersoninstanceyoucreateandtodeallocatethatPersoninstancewhenit’snolongerneeded.

However,it’spossibletowritecodeinwhichaninstanceofaclassnevergetstoapointwhereithaszerostrongreferences.Thiscanhappeniftwoclassinstancesholdastrongreferencetoeachother,suchthateachinstancekeepstheotheralive.Thisisknownasastrongreferencecycle.

Youresolvestrongreferencecyclesbydefiningsomeoftherelationshipsbetweenclassesasweakorunownedreferencesinsteadofasstrongreferences.ThisprocessisdescribedinResolvingStrongReferenceCyclesBetweenClassInstances.However,beforeyoulearnhowtoresolveastrongreferencecycle,it’susefultounderstandhowsuchacycleiscaused.

Here’sanexampleofhowastrongreferencecyclecanbecreatedbyaccident.ThisexampledefinestwoclassescalledPersonandApartment,whichmodelablockofapartmentsanditsresidents:

1 classPerson{

2 letname:String

3 init(name:String){self.name=name}

4 varapartment:Apartment?

5 deinit{print("\(name)isbeingdeinitialized")}

6 }

7

8 classApartment{

9 letunit:String

10 init(unit:String){self.unit=unit}

11 vartenant:Person?

12 deinit{print("Apartment\(unit)isbeingdeinitialized")

}

13 }

EveryPersoninstancehasanamepropertyoftypeStringandanoptionalapartmentpropertythatisinitiallynil.Theapartmentpropertyisoptional,becauseapersonmaynotalwayshaveanapartment.

Similarly,everyApartmentinstancehasaunitpropertyoftypeStringandhasanoptionaltenantpropertythatisinitiallynil.Thetenantpropertyisoptionalbecauseanapartmentmaynotalwayshaveatenant.

Bothoftheseclassesalsodefineadeinitializer,whichprintsthefactthataninstanceofthatclassisbeingdeinitialized.ThisenablesyoutoseewhetherinstancesofPersonandApartmentarebeingdeallocatedasexpected.

Thisnextcodesnippetdefinestwovariablesofoptionaltypecalledjohnandunit4A,whichwillbesettoaspecificApartmentandPersoninstancebelow.Bothofthesevariableshaveaninitialvalueofnil,byvirtueofbeingoptional:

1 varjohn:Person?

2 varunit4A:Apartment?

YoucannowcreateaspecificPersoninstanceandApartmentinstanceandassignthesenewinstancestothejohnandunit4Avariables:

1 john=Person(name:"JohnAppleseed")

2 unit4A=Apartment(unit:"4A")

Here’showthestrongreferenceslookaftercreatingandassigningthesetwoinstances.ThejohnvariablenowhasastrongreferencetothenewPersoninstance,andtheunit4AvariablehasastrongreferencetothenewApartmentinstance:

Youcannowlinkthetwoinstancestogethersothatthepersonhasanapartment,andtheapartmenthasatenant.Notethatanexclamationmark(!)isusedtounwrapandaccesstheinstancesstoredinsidethejohnandunit4Aoptionalvariables,sothatthepropertiesofthoseinstancescanbeset:

1 john!.apartment=unit4A

2 unit4A!.tenant=john

Here’showthestrongreferenceslookafteryoulinkthetwoinstancestogether:

Unfortunately,linkingthesetwoinstancescreatesastrongreferencecycle

betweenthem.ThePersoninstancenowhasastrongreferencetotheApartmentinstance,andtheApartmentinstancehasastrongreferencetothePersoninstance.Therefore,whenyoubreakthestrongreferencesheldbythejohnandunit4Avariables,thereferencecountsdonotdroptozero,andtheinstancesarenotdeallocatedbyARC:

1 john=nil

2 unit4A=nil

Notethatneitherdeinitializerwascalledwhenyousetthesetwovariablestonil.ThestrongreferencecyclepreventsthePersonandApartmentinstancesfromeverbeingdeallocated,causingamemoryleakinyourapp.

Here’showthestrongreferenceslookafteryousetthejohnandunit4Avariablestonil:

ThestrongreferencesbetweenthePersoninstanceandtheApartmentinstanceremainandcannotbebroken.

ResolvingStrongReferenceCyclesBetweenClassInstances

Swiftprovidestwowaystoresolvestrongreferencecycleswhenyouworkwithpropertiesofclasstype:weakreferencesandunownedreferences.

Weakandunownedreferencesenableoneinstanceinareferencecycletorefertotheotherinstancewithoutkeepingastrongholdonit.Theinstancescanthen

refertoeachotherwithoutcreatingastrongreferencecycle.

Useaweakreferencewhentheotherinstancehasashorterlifetime—thatis,whentheotherinstancecanbedeallocatedfirst.IntheApartmentexampleabove,it’sappropriateforanapartmenttobeabletohavenotenantatsomepointinitslifetime,andsoaweakreferenceisanappropriatewaytobreakthereferencecycleinthiscase.Incontrast,useanunownedreferencewhentheotherinstancehasthesamelifetimeoralongerlifetime.

WeakReferencesAweakreferenceisareferencethatdoesnotkeepastrongholdontheinstanceitrefersto,andsodoesnotstopARCfromdisposingofthereferencedinstance.Thisbehaviorpreventsthereferencefrombecomingpartofastrongreferencecycle.Youindicateaweakreferencebyplacingtheweakkeywordbeforeapropertyorvariabledeclaration.

Becauseaweakreferencedoesnotkeepastrongholdontheinstanceitrefersto,it’spossibleforthatinstancetobedeallocatedwhiletheweakreferenceisstillreferringtoit.Therefore,ARCautomaticallysetsaweakreferencetonilwhentheinstancethatitreferstoisdeallocated.And,becauseweakreferencesneedtoallowtheirvaluetobechangedtonilatruntime,theyarealwaysdeclaredasvariables,ratherthanconstants,ofanoptionaltype.

Youcancheckfortheexistenceofavalueintheweakreference,justlikeanyotheroptionalvalue,andyouwillneverendupwithareferencetoaninvalidinstancethatnolongerexists.

NOTE

Propertyobserversaren’tcalledwhenARCsetsaweakreferencetonil.

TheexamplebelowisidenticaltothePersonandApartmentexamplefromabove,withoneimportantdifference.Thistimearound,theApartmenttype’stenantpropertyisdeclaredasaweakreference:

1 classPerson{

2 letname:String

3 init(name:String){self.name=name}

4 varapartment:Apartment?

5 deinit{print("\(name)isbeingdeinitialized")}

6 }

7

8 classApartment{

9 letunit:String

10 init(unit:String){self.unit=unit}

11 weakvartenant:Person?

12 deinit{print("Apartment\(unit)isbeingdeinitialized")

}

13 }

Thestrongreferencesfromthetwovariables(johnandunit4A)andthelinksbetweenthetwoinstancesarecreatedasbefore:

1 varjohn:Person?

2 varunit4A:Apartment?

3

4 john=Person(name:"JohnAppleseed")

5 unit4A=Apartment(unit:"4A")

6

7 john!.apartment=unit4A

8 unit4A!.tenant=john

Here’showthereferenceslooknowthatyou’velinkedthetwoinstancestogether:

ThePersoninstancestillhasastrongreferencetotheApartmentinstance,buttheApartmentinstancenowhasaweakreferencetothePersoninstance.Thismeansthatwhenyoubreakthestrongreferenceheldbythejohnvariablebysettingittonil,therearenomorestrongreferencestothePersoninstance:

1 john=nil

2 //Prints"JohnAppleseedisbeingdeinitialized"

BecausetherearenomorestrongreferencestothePersoninstance,it’sdeallocatedandthetenantpropertyissettonil:

TheonlyremainingstrongreferencetotheApartmentinstanceisfromtheunit4Avariable.Ifyoubreakthatstrongreference,therearenomorestrongreferencestotheApartmentinstance:

1 unit4A=nil

2 //Prints"Apartment4Aisbeingdeinitialized"

BecausetherearenomorestrongreferencestotheApartmentinstance,ittoois

deallocated:

NOTE

Insystemsthatusegarbagecollection,weakpointersaresometimesusedtoimplementasimplecachingmechanismbecauseobjectswithnostrongreferencesaredeallocatedonlywhenmemorypressuretriggersgarbagecollection.However,withARC,valuesaredeallocatedassoonastheirlaststrongreferenceisremoved,makingweakreferencesunsuitableforsuchapurpose.

UnownedReferencesLikeaweakreference,anunownedreferencedoesnotkeepastrongholdontheinstanceitrefersto.Unlikeaweakreference,however,anunownedreferenceisusedwhentheotherinstancehasthesamelifetimeoralongerlifetime.Youindicateanunownedreferencebyplacingtheunownedkeywordbeforeapropertyorvariabledeclaration.

Anunownedreferenceisexpectedtoalwayshaveavalue.Asaresult,ARCneversetsanunownedreference’svaluetonil,whichmeansthatunownedreferencesaredefinedusingnon-optionaltypes.

IMPORTANT

Useanunownedreferenceonlywhenyouaresurethatthereferencealwaysreferstoaninstancethathasnotbeendeallocated.

Ifyoutrytoaccessthevalueofanunownedreferenceafterthatinstancehasbeendeallocated,you’llgetaruntimeerror.

Thefollowingexampledefinestwoclasses,CustomerandCreditCard,whichmodelabankcustomerandapossiblecreditcardforthatcustomer.Thesetwo

classeseachstoreaninstanceoftheotherclassasaproperty.Thisrelationshiphasthepotentialtocreateastrongreferencecycle.

TherelationshipbetweenCustomerandCreditCardisslightlydifferentfromtherelationshipbetweenApartmentandPersonseenintheweakreferenceexampleabove.Inthisdatamodel,acustomermayormaynothaveacreditcard,butacreditcardwillalwaysbeassociatedwithacustomer.ACreditCardinstanceneveroutlivestheCustomerthatitrefersto.Torepresentthis,theCustomerclasshasanoptionalcardproperty,buttheCreditCardclasshasanunowned(andnon-optional)customerproperty.

Furthermore,anewCreditCardinstancecanonlybecreatedbypassinganumbervalueandacustomerinstancetoacustomCreditCardinitializer.ThisensuresthataCreditCardinstancealwayshasacustomerinstanceassociatedwithitwhentheCreditCardinstanceiscreated.

Becauseacreditcardwillalwayshaveacustomer,youdefineitscustomerpropertyasanunownedreference,toavoidastrongreferencecycle:

1 classCustomer{

2 letname:String

3 varcard:CreditCard?

4 init(name:String){

5 self.name=name

6 }

7 deinit{print("\(name)isbeingdeinitialized")}

8 }

9

10 classCreditCard{

11 letnumber:UInt64

12 unownedletcustomer:Customer

13 init(number:UInt64,customer:Customer){

14 self.number=number

15 self.customer=customer

16 }

17 deinit{print("Card#\(number)isbeingdeinitialized")}

18 }

NOTE

ThenumberpropertyoftheCreditCardclassisdefinedwithatypeofUInt64ratherthanInt,toensurethatthenumberproperty’scapacityislargeenoughtostorea16-digitcardnumberonboth32-bitand64-bitsystems.

ThisnextcodesnippetdefinesanoptionalCustomervariablecalledjohn,whichwillbeusedtostoreareferencetoaspecificcustomer.Thisvariablehasaninitialvalueofnil,byvirtueofbeingoptional:

varjohn:Customer?

YoucannowcreateaCustomerinstance,anduseittoinitializeandassignanewCreditCardinstanceasthatcustomer’scardproperty:

1 john=Customer(name:"JohnAppleseed")

2 john!.card=CreditCard(number:1234_5678_9012_3456,customer:

john!)

Here’showthereferenceslook,nowthatyou’velinkedthetwoinstances:

TheCustomerinstancenowhasastrongreferencetotheCreditCardinstance,andtheCreditCardinstancehasanunownedreferencetotheCustomerinstance.

Becauseoftheunownedcustomerreference,whenyoubreakthestrongreferenceheldbythejohnvariable,therearenomorestrongreferencestotheCustomerinstance:

BecausetherearenomorestrongreferencestotheCustomerinstance,it’sdeallocated.Afterthishappens,therearenomorestrongreferencestotheCreditCardinstance,andittooisdeallocated:

1 john=nil

2 //Prints"JohnAppleseedisbeingdeinitialized"

3 //Prints"Card#1234567890123456isbeingdeinitialized"

ThefinalcodesnippetaboveshowsthatthedeinitializersfortheCustomerinstanceandCreditCardinstancebothprinttheir“deinitialized”messagesafterthejohnvariableissettonil.

NOTE

Theexamplesaboveshowhowtousesafeunownedreferences.Swiftalsoprovidesunsafeunownedreferencesforcaseswhereyouneedtodisableruntimesafetychecks—forexample,forperformancereasons.Aswithallunsafeoperations,youtakeontheresponsibilityforcheckingthatcodeforsafety.

Youindicateanunsafeunownedreferencebywritingunowned(unsafe).Ifyoutrytoaccessanunsafeunownedreferenceaftertheinstancethatitreferstoisdeallocated,yourprogramwilltrytoaccessthememorylocationwheretheinstanceusedtobe,whichisanunsafeoperation.

UnownedReferencesandImplicitlyUnwrappedOptional

PropertiesTheexamplesforweakandunownedreferencesabovecovertwoofthemorecommonscenariosinwhichit’snecessarytobreakastrongreferencecycle.

ThePersonandApartmentexampleshowsasituationwheretwoproperties,bothofwhichareallowedtobenil,havethepotentialtocauseastrongreferencecycle.Thisscenarioisbestresolvedwithaweakreference.

TheCustomerandCreditCardexampleshowsasituationwhereonepropertythatisallowedtobenilandanotherpropertythatcannotbenilhavethepotentialtocauseastrongreferencecycle.Thisscenarioisbestresolvedwithanunownedreference.

However,thereisathirdscenario,inwhichbothpropertiesshouldalwayshaveavalue,andneitherpropertyshouldeverbenilonceinitializationiscomplete.Inthisscenario,it’susefultocombineanunownedpropertyononeclasswithanimplicitlyunwrappedoptionalpropertyontheotherclass.

Thisenablesbothpropertiestobeaccesseddirectly(withoutoptionalunwrapping)onceinitializationiscomplete,whilestillavoidingareferencecycle.Thissectionshowsyouhowtosetupsucharelationship.

Theexamplebelowdefinestwoclasses,CountryandCity,eachofwhichstoresaninstanceoftheotherclassasaproperty.Inthisdatamodel,everycountrymustalwayshaveacapitalcity,andeverycitymustalwaysbelongtoacountry.Torepresentthis,theCountryclasshasacapitalCityproperty,andtheCityclasshasacountryproperty:

1 classCountry{

2 letname:String

3 varcapitalCity:City!

4 init(name:String,capitalName:String){

5 self.name=name

6 self.capitalCity=City(name:capitalName,country:

self)

7 }

8 }

9

10 classCity{

11 letname:String

12 unownedletcountry:Country

13 init(name:String,country:Country){

14 self.name=name

15 self.country=country

16 }

17 }

Tosetuptheinterdependencybetweenthetwoclasses,theinitializerforCitytakesaCountryinstance,andstoresthisinstanceinitscountryproperty.

TheinitializerforCityiscalledfromwithintheinitializerforCountry.However,theinitializerforCountrycannotpassselftotheCityinitializeruntilanewCountryinstanceisfullyinitialized,asdescribedinTwo-PhaseInitialization.

Tocopewiththisrequirement,youdeclarethecapitalCitypropertyofCountryasanimplicitlyunwrappedoptionalproperty,indicatedbytheexclamationmarkattheendofitstypeannotation(City!).ThismeansthatthecapitalCitypropertyhasadefaultvalueofnil,likeanyotheroptional,butcanbeaccessedwithouttheneedtounwrapitsvalueasdescribedinImplicitlyUnwrappedOptionals.

BecausecapitalCityhasadefaultnilvalue,anewCountryinstanceisconsideredfullyinitializedassoonastheCountryinstancesetsitsnamepropertywithinitsinitializer.ThismeansthattheCountryinitializercanstarttoreferenceandpassaroundtheimplicitselfpropertyassoonasthenamepropertyisset.TheCountryinitializercanthereforepassselfasoneoftheparametersfortheCityinitializerwhentheCountryinitializerissettingitsowncapitalCityproperty.

AllofthismeansthatyoucancreatetheCountryandCityinstancesinasinglestatement,withoutcreatingastrongreferencecycle,andthecapitalCity

propertycanbeaccesseddirectly,withoutneedingtouseanexclamationmarktounwrapitsoptionalvalue:

1 varcountry=Country(name:"Canada",capitalName:"Ottawa")

2 print("\(country.name)'scapitalcityiscalled\

(country.capitalCity.name)")

3 //Prints"Canada'scapitalcityiscalledOttawa"

Intheexampleabove,theuseofanimplicitlyunwrappedoptionalmeansthatallofthetwo-phaseclassinitializerrequirementsaresatisfied.ThecapitalCitypropertycanbeusedandaccessedlikeanon-optionalvalueonceinitializationiscomplete,whilestillavoidingastrongreferencecycle.

StrongReferenceCyclesforClosures

Yousawabovehowastrongreferencecyclecanbecreatedwhentwoclassinstancepropertiesholdastrongreferencetoeachother.Youalsosawhowtouseweakandunownedreferencestobreakthesestrongreferencecycles.

Astrongreferencecyclecanalsooccurifyouassignaclosuretoapropertyofaclassinstance,andthebodyofthatclosurecapturestheinstance.Thiscapturemightoccurbecausetheclosure’sbodyaccessesapropertyoftheinstance,suchasself.someProperty,orbecausetheclosurecallsamethodontheinstance,suchasself.someMethod().Ineithercase,theseaccessescausetheclosureto“capture”self,creatingastrongreferencecycle.

Thisstrongreferencecycleoccursbecauseclosures,likeclasses,arereferencetypes.Whenyouassignaclosuretoaproperty,youareassigningareferencetothatclosure.Inessence,it’sthesameproblemasabove—twostrongreferencesarekeepingeachotheralive.However,ratherthantwoclassinstances,thistimeit’saclassinstanceandaclosurethatarekeepingeachotheralive.

Swiftprovidesanelegantsolutiontothisproblem,knownasaclosurecapturelist.However,beforeyoulearnhowtobreakastrongreferencecyclewitha

closurecapturelist,it’susefultounderstandhowsuchacyclecanbecaused.

Theexamplebelowshowshowyoucancreateastrongreferencecyclewhenusingaclosurethatreferencesself.ThisexampledefinesaclasscalledHTMLElement,whichprovidesasimplemodelforanindividualelementwithinanHTMLdocument:

1 classHTMLElement{

2

3 letname:String

4 lettext:String?

5

6 lazyvarasHTML:()->String={

7 iflettext=self.text{

8 return"<\(self.name)>\(text)</\(self.name)>"

9 }else{

10 return"<\(self.name)/>"

11 }

12 }

13

14 init(name:String,text:String?=nil){

15 self.name=name

16 self.text=text

17 }

18

19 deinit{

20 print("\(name)isbeingdeinitialized")

21 }

22

23 }

TheHTMLElementclassdefinesanameproperty,whichindicatesthenameoftheelement,suchas"h1"foraheadingelement,"p"foraparagraphelement,or"br"foralinebreakelement.HTMLElementalsodefinesanoptionaltextproperty,whichyoucansettoastringthatrepresentsthetexttoberenderedwithinthatHTMLelement.

Inadditiontothesetwosimpleproperties,theHTMLElementclassdefinesalazypropertycalledasHTML.ThispropertyreferencesaclosurethatcombinesnameandtextintoanHTMLstringfragment.TheasHTMLpropertyisoftype()->String,or“afunctionthattakesnoparameters,andreturnsaStringvalue”.

Bydefault,theasHTMLpropertyisassignedaclosurethatreturnsastringrepresentationofanHTMLtag.Thistagcontainstheoptionaltextvalueifitexists,ornotextcontentiftextdoesnotexist.Foraparagraphelement,theclosurewouldreturn"<p>sometext</p>"or"<p/>",dependingonwhetherthetextpropertyequals"sometext"ornil.

TheasHTMLpropertyisnamedandusedsomewhatlikeaninstancemethod.However,becauseasHTMLisaclosurepropertyratherthananinstancemethod,youcanreplacethedefaultvalueoftheasHTMLpropertywithacustomclosure,ifyouwanttochangetheHTMLrenderingforaparticularHTMLelement.

Forexample,theasHTMLpropertycouldbesettoaclosurethatdefaultstosometextifthetextpropertyisnil,inordertopreventtherepresentationfromreturninganemptyHTMLtag:

1 letheading=HTMLElement(name:"h1")

2 letdefaultText="somedefaulttext"

3 heading.asHTML={

4 return"<\(heading.name)>\(heading.text??defaultText)</\

(heading.name)>"

5 }

6 print(heading.asHTML())

7 //Prints"<h1>somedefaulttext</h1>"

NOTE

TheasHTMLpropertyisdeclaredasalazyproperty,becauseit’sonlyneededifandwhentheelementactuallyneedstoberenderedasastringvalueforsomeHTMLoutputtarget.ThefactthatasHTMLisalazypropertymeansthatyoucanrefertoselfwithinthedefaultclosure,becausethelazypropertywillnotbeaccesseduntilafterinitializationhasbeencompletedandselfisknowntoexist.

TheHTMLElementclassprovidesasingleinitializer,whichtakesanameargumentand(ifdesired)atextargumenttoinitializeanewelement.Theclassalsodefinesadeinitializer,whichprintsamessagetoshowwhenanHTMLElementinstanceisdeallocated.

Here’showyouusetheHTMLElementclasstocreateandprintanewinstance:

1 varparagraph:HTMLElement?=HTMLElement(name:"p",text:

"hello,world")

2 print(paragraph!.asHTML())

3 //Prints"<p>hello,world</p>"

NOTE

TheparagraphvariableaboveisdefinedasanoptionalHTMLElement,sothatitcanbesettonilbelowtodemonstratethepresenceofastrongreferencecycle.

Unfortunately,theHTMLElementclass,aswrittenabove,createsastrongreferencecyclebetweenanHTMLElementinstanceandtheclosureusedforitsdefaultasHTMLvalue.Here’showthecyclelooks:

Theinstance’sasHTMLpropertyholdsastrongreferencetoitsclosure.However,becausetheclosurereferstoselfwithinitsbody(asawaytoreferenceself.nameandself.text),theclosurecapturesself,whichmeansthatitholdsastrongreferencebacktotheHTMLElementinstance.Astrongreferencecycleiscreatedbetweenthetwo.(Formoreinformationaboutcapturingvaluesinaclosure,seeCapturingValues.)

NOTE

Eventhoughtheclosurereferstoselfmultipletimes,itonlycapturesonestrongreferencetotheHTMLElementinstance.

IfyousettheparagraphvariabletonilandbreakitsstrongreferencetotheHTMLElementinstance,neithertheHTMLElementinstancenoritsclosurearedeallocated,becauseofthestrongreferencecycle:

paragraph=nil

NotethatthemessageintheHTMLElementdeinitializerisnotprinted,whichshowsthattheHTMLElementinstanceisnotdeallocated.

ResolvingStrongReferenceCyclesforClosures

Youresolveastrongreferencecyclebetweenaclosureandaclassinstancebydefiningacapturelistaspartoftheclosure’sdefinition.Acapturelistdefinestherulestousewhencapturingoneormorereferencetypeswithintheclosure’sbody.Aswithstrongreferencecyclesbetweentwoclassinstances,youdeclareeachcapturedreferencetobeaweakorunownedreferenceratherthanastrongreference.Theappropriatechoiceofweakorunowneddependsontherelationshipsbetweenthedifferentpartsofyourcode.

NOTE

Swiftrequiresyoutowriteself.somePropertyorself.someMethod()(ratherthanjustsomePropertyorsomeMethod())wheneveryourefertoamemberofselfwithinaclosure.Thishelpsyourememberthatit’spossibletocaptureselfbyaccident.

DefiningaCaptureListEachiteminacapturelistisapairingoftheweakorunownedkeywordwithareferencetoaclassinstance(suchasself)oravariableinitializedwithsomevalue(suchasdelegate=self.delegate).Thesepairingsarewrittenwithinapairofsquarebraces,separatedbycommas.

Placethecapturelistbeforeaclosure’sparameterlistandreturntypeiftheyareprovided:

1 lazyvarsomeClosure={

2 [unownedself,weakdelegate=self.delegate]

3 (index:Int,stringToProcess:String)->Stringin

4 //closurebodygoeshere

5 }

Ifaclosuredoesnotspecifyaparameterlistorreturntypebecausetheycanbeinferredfromcontext,placethecapturelistattheverystartoftheclosure,followedbytheinkeyword:

1 lazyvarsomeClosure={

2 [unownedself,weakdelegate=self.delegate]in

3 //closurebodygoeshere

4 }

WeakandUnownedReferencesDefineacaptureinaclosureasanunownedreferencewhentheclosureandtheinstanceitcaptureswillalwaysrefertoeachother,andwillalwaysbedeallocatedatthesametime.

Conversely,defineacaptureasaweakreferencewhenthecapturedreferencemaybecomenilatsomepointinthefuture.Weakreferencesarealwaysofanoptionaltype,andautomaticallybecomenilwhentheinstancetheyreferenceisdeallocated.Thisenablesyoutocheckfortheirexistencewithintheclosure’s

body.

NOTE

Ifthecapturedreferencewillneverbecomenil,itshouldalwaysbecapturedasanunownedreference,ratherthanaweakreference.

AnunownedreferenceistheappropriatecapturemethodtousetoresolvethestrongreferencecycleintheHTMLElementexamplefromStrongReferenceCyclesforClosuresabove.Here’showyouwritetheHTMLElementclasstoavoidthecycle:

1 classHTMLElement{

2

3 letname:String

4 lettext:String?

5

6 lazyvarasHTML:()->String={

7 [unownedself]in

8 iflettext=self.text{

9 return"<\(self.name)>\(text)</\(self.name)>"

10 }else{

11 return"<\(self.name)/>"

12 }

13 }

14

15 init(name:String,text:String?=nil){

16 self.name=name

17 self.text=text

18 }

19

20 deinit{

21 print("\(name)isbeingdeinitialized")

22 }

23

24 }

ThisimplementationofHTMLElementisidenticaltothepreviousimplementation,apartfromtheadditionofacapturelistwithintheasHTMLclosure.Inthiscase,thecapturelistis[unownedself],whichmeans“captureselfasanunownedreferenceratherthanastrongreference”.

YoucancreateandprintanHTMLElementinstanceasbefore:

1 varparagraph:HTMLElement?=HTMLElement(name:"p",text:

"hello,world")

2 print(paragraph!.asHTML())

3 //Prints"<p>hello,world</p>"

Here’showthereferenceslookwiththecapturelistinplace:

Thistime,thecaptureofselfbytheclosureisanunownedreference,anddoesnotkeepastrongholdontheHTMLElementinstanceithascaptured.Ifyousetthestrongreferencefromtheparagraphvariabletonil,theHTMLElementinstanceisdeallocated,ascanbeseenfromtheprintingofitsdeinitializermessageintheexamplebelow:

1 paragraph=nil

2 //Prints"pisbeingdeinitialized"

Formoreinformationaboutcapturelists,seeCaptureLists.

MemorySafety

Bydefault,Swiftpreventsunsafebehaviorfromhappeninginyourcode.Forexample,Swiftensuresthatvariablesareinitializedbeforethey’reused,memoryisn’taccessedafterit’sbeendeallocated,andarrayindicesarecheckedforout-of-boundserrors.

Swiftalsomakessurethatmultipleaccessestothesameareaofmemorydon’tconflict,byrequiringcodethatmodifiesalocationinmemorytohaveexclusiveaccesstothatmemory.BecauseSwiftmanagesmemoryautomatically,mostofthetimeyoudon’thavetothinkaboutaccessingmemoryatall.However,it’simportanttounderstandwherepotentialconflictscanoccur,soyoucanavoidwritingcodethathasconflictingaccesstomemory.Ifyourcodedoescontainconflicts,you’llgetacompile-timeorruntimeerror.

UnderstandingConflictingAccesstoMemory

Accesstomemoryhappensinyourcodewhenyoudothingslikesetthevalueofavariableorpassanargumenttoafunction.Forexample,thefollowingcodecontainsbothareadaccessandawriteaccess:

1 //Awriteaccesstothememorywhereoneisstored.

2 varone=1

3

4 //Areadaccessfromthememorywhereoneisstored.

5 print("We'renumber\(one)!")

Aconflictingaccesstomemorycanoccurwhendifferentpartsofyourcodearetryingtoaccessthesamelocationinmemoryatthesametime.Multipleaccessestoalocationinmemoryatthesametimecanproduceunpredictableorinconsistentbehavior.InSwift,therearewaystomodifyavaluethatspanseverallinesofcode,makingitpossibletoattempttoaccessavalueinthe

middleofitsownmodification.

Youcanseeasimilarproblembythinkingabouthowyouupdateabudgetthat’swrittenonapieceofpaper.Updatingthebudgetisatwo-stepprocess:Firstyouaddtheitems’namesandprices,andthenyouchangethetotalamounttoreflecttheitemscurrentlyonthelist.Beforeandaftertheupdate,youcanreadanyinformationfromthebudgetandgetacorrectanswer,asshowninthefigurebelow.

Whileyou’readdingitemstothebudget,it’sinatemporary,invalidstatebecausethetotalamounthasn’tbeenupdatedtoreflectthenewlyaddeditems.Readingthetotalamountduringtheprocessofaddinganitemgivesyouincorrectinformation.

Thisexamplealsodemonstratesachallengeyoumayencounterwhenfixingconflictingaccesstomemory:Therearesometimesmultiplewaystofixtheconflictthatproducedifferentanswers,andit’snotalwaysobviouswhichansweriscorrect.Inthisexample,dependingonwhetheryouwantedtheoriginaltotalamountortheupdatedtotalamount,either$5or$320couldbethecorrectanswer.Beforeyoucanfixtheconflictingaccess,youhavetodeterminewhatitwasintendedtodo.

NOTE

Ifyou’vewrittenconcurrentormultithreadedcode,conflictingaccesstomemorymightbeafamiliarproblem.However,theconflictingaccessdiscussedherecanhappenonasinglethreadanddoesn’tinvolveconcurrentormultithreadedcode.

Ifyouhaveconflictingaccesstomemoryfromwithinasinglethread,Swiftguaranteesthatyou’llgetanerrorateithercompiletimeorruntime.Formultithreadedcode,useThreadSanitizertohelpdetectconflictingaccessacrossthreads.

CharacteristicsofMemoryAccessTherearethreecharacteristicsofmemoryaccesstoconsiderinthecontextofconflictingaccess:whethertheaccessisareadorawrite,thedurationoftheaccess,andthelocationinmemorybeingaccessed.Specifically,aconflictoccursifyouhavetwoaccessesthatmeetallofthefollowingconditions:

Atleastoneisawriteaccess.

Theyaccessthesamelocationinmemory.

Theirdurationsoverlap.

Thedifferencebetweenareadandwriteaccessisusuallyobvious:awriteaccesschangesthelocationinmemory,butareadaccessdoesn’t.Thelocationinmemoryreferstowhatisbeingaccessed—forexample,avariable,constant,orproperty.Thedurationofamemoryaccessiseitherinstantaneousorlong-term.

Anaccessisinstantaneousifit’snotpossibleforothercodetorunafterthataccessstartsbutbeforeitends.Bytheirnature,twoinstantaneousaccessescan’thappenatthesametime.Mostmemoryaccessisinstantaneous.Forexample,allthereadandwriteaccessesinthecodelistingbelowareinstantaneous:

1 funconeMore(thannumber:Int)->Int{

2 returnnumber+1

3 }

4

5 varmyNumber=1

6 myNumber=oneMore(than:myNumber)

7 print(myNumber)

8 //Prints"2"

However,thereareseveralwaystoaccessmemory,calledlong-termaccesses,thatspantheexecutionofothercode.Thedifferencebetweeninstantaneousaccessandlong-termaccessisthatit’spossibleforothercodetorunafteralong-termaccessstartsbutbeforeitends,whichiscalledoverlap.Along-termaccesscanoverlapwithotherlong-termaccessesandinstantaneousaccesses.

Overlappingaccessesappearprimarilyincodethatusesin-outparametersinfunctionsandmethodsormutatingmethodsofastructure.ThespecifickindsofSwiftcodethatuselong-termaccessesarediscussedinthesectionsbelow.

ConflictingAccesstoIn-OutParameters

Afunctionhaslong-termwriteaccesstoallofitsin-outparameters.Thewriteaccessforanin-outparameterstartsafterallofthenon-in-outparametershavebeenevaluatedandlastsfortheentiredurationofthatfunctioncall.Iftherearemultiplein-outparameters,thewriteaccessesstartinthesameorderastheparametersappear.

Oneconsequenceofthislong-termwriteaccessisthatyoucan’taccesstheoriginalvariablethatwaspassedasin-out,evenifscopingrulesandaccesscontrolwouldotherwisepermitit—anyaccesstotheoriginalcreatesaconflict.Forexample:

1 varstepSize=1

2

3 funcincrement(_number:inoutInt){

4 number+=stepSize

5 }

6

7 increment(&stepSize)

8 //Error:conflictingaccessestostepSize

Inthecodeabove,stepSizeisaglobalvariable,anditisnormallyaccessiblefromwithinincrement(_:).However,thereadaccesstostepSizeoverlapswiththewriteaccesstonumber.Asshowninthefigurebelow,bothnumberandstepSizerefertothesamelocationinmemory.Thereadandwriteaccessesrefertothesamememoryandtheyoverlap,producingaconflict.

OnewaytosolvethisconflictistomakeanexplicitcopyofstepSize:

1 //Makeanexplicitcopy.

2 varcopyOfStepSize=stepSize

3 increment(&copyOfStepSize)

4

5 //Updatetheoriginal.

6 stepSize=copyOfStepSize

7 //stepSizeisnow2

WhenyoumakeacopyofstepSizebeforecallingincrement(_:),it’sclearthatthevalueofcopyOfStepSizeisincrementedbythecurrentstepsize.Thereadaccessendsbeforethewriteaccessstarts,sothereisn’taconflict.

Anotherconsequenceoflong-termwriteaccesstoin-outparametersisthatpassingasinglevariableastheargumentformultiplein-outparametersofthesamefunctionproducesaconflict.Forexample:

1 funcbalance(_x:inoutInt,_y:inoutInt){

2 letsum=x+y

3 x=sum/2

4 y=sum-x

5 }

6 varplayerOneScore=42

7 varplayerTwoScore=30

8 balance(&playerOneScore,&playerTwoScore)//OK

9 balance(&playerOneScore,&playerOneScore)

10 //Error:conflictingaccessestoplayerOneScore

Thebalance(_:_:)functionabovemodifiesitstwoparameterstodividethetotalvalueevenlybetweenthem.CallingitwithplayerOneScoreandplayerTwoScoreasargumentsdoesn’tproduceaconflict—therearetwowriteaccessesthatoverlapintime,buttheyaccessdifferentlocationsinmemory.Incontrast,passingplayerOneScoreasthevalueforbothparametersproducesaconflictbecauseittriestoperformtwowriteaccessestothesamelocationinmemoryatthesametime.

NOTE

Becauseoperatorsarefunctions,theycanalsohavelong-termaccessestotheirin-outparameters.Forexample,ifbalance(_:_:)wasanoperatorfunctionnamed<^>,writingplayerOneScore<^>playerOneScorewouldresultinthesameconflictasbalance(&playerOneScore,&playerOneScore).

ConflictingAccesstoselfinMethods

Amutatingmethodonastructurehaswriteaccesstoselfforthedurationofthemethodcall.Forexample,consideragamewhereeachplayerhasahealthamount,whichdecreaseswhentakingdamage,andanenergyamount,whichdecreaseswhenusingspecialabilities.

1 structPlayer{

2 varname:String

3 varhealth:Int

4 varenergy:Int

5

6 staticletmaxHealth=10

7 mutatingfuncrestoreHealth(){

8 health=Player.maxHealth

9 }

10 }

IntherestoreHealth()methodabove,awriteaccesstoselfstartsatthebeginningofthemethodandlastsuntilthemethodreturns.Inthiscase,there’snoothercodeinsiderestoreHealth()thatcouldhaveanoverlappingaccesstothepropertiesofaPlayerinstance.TheshareHealth(with:)methodbelowtakesanotherPlayerinstanceasanin-outparameter,creatingthepossibilityofoverlappingaccesses.

1 extensionPlayer{

2 mutatingfuncshareHealth(withteammate:inoutPlayer){

3 balance(&teammate.health,&health)

4 }

5 }

6

7 varoscar=Player(name:"Oscar",health:10,energy:10)

8 varmaria=Player(name:"Maria",health:5,energy:10)

9 oscar.shareHealth(with:&maria)//OK

Intheexampleabove,callingtheshareHealth(with:)methodforOscar’splayertosharehealthwithMaria’splayerdoesn’tcauseaconflict.There’sawriteaccesstooscarduringthemethodcallbecauseoscaristhevalueofselfinamutatingmethod,andthere’sawriteaccesstomariaforthesamedurationbecausemariawaspassedasanin-outparameter.Asshowninthefigurebelow,theyaccessdifferentlocationsinmemory.Eventhoughthetwowriteaccessesoverlapintime,theydon’tconflict.

However,ifyoupassoscarastheargumenttoshareHealth(with:),there’saconflict:

1 oscar.shareHealth(with:&oscar)

2 //Error:conflictingaccessestooscar

Themutatingmethodneedswriteaccesstoselfforthedurationofthemethod,andthein-outparameterneedswriteaccesstoteammateforthesameduration.Withinthemethod,bothselfandteammaterefertothesamelocationinmemory—asshowninthefigurebelow.Thetwowriteaccessesrefertothesamememoryandtheyoverlap,producingaconflict.

ConflictingAccesstoProperties

Typeslikestructures,tuples,andenumerationsaremadeupofindividualconstituentvalues,suchasthepropertiesofastructureortheelementsofatuple.Becausethesearevaluetypes,mutatinganypieceofthevaluemutatesthewholevalue,meaningreadorwriteaccesstooneofthepropertiesrequiresreadorwriteaccesstothewholevalue.Forexample,overlappingwriteaccessestotheelementsofatupleproducesaconflict:

1 varplayerInformation=(health:10,energy:20)

2 balance(&playerInformation.health,&playerInformation.energy)

3 //Error:conflictingaccesstopropertiesofplayerInformation

Intheexampleabove,callingbalance(_:_:)ontheelementsofatupleproduces

aconflictbecausethereareoverlappingwriteaccessestoplayerInformation.BothplayerInformation.healthandplayerInformation.energyarepassedasin-outparameters,whichmeansbalance(_:_:)needswriteaccesstothemforthedurationofthefunctioncall.Inbothcases,awriteaccesstothetupleelementrequiresawriteaccesstotheentiretuple.ThismeanstherearetwowriteaccessestoplayerInformationwithdurationsthatoverlap,causingaconflict.

Thecodebelowshowsthatthesameerrorappearsforoverlappingwriteaccessestothepropertiesofastructurethat’sstoredinaglobalvariable.

1 varholly=Player(name:"Holly",health:10,energy:10)

2 balance(&holly.health,&holly.energy)//Error

Inpractice,mostaccesstothepropertiesofastructurecanoverlapsafely.Forexample,ifthevariablehollyintheexampleaboveischangedtoalocalvariableinsteadofaglobalvariable,thecompilercanprovethatoverlappingaccesstostoredpropertiesofthestructureissafe:

1 funcsomeFunction(){

2 varoscar=Player(name:"Oscar",health:10,energy:10)

3 balance(&oscar.health,&oscar.energy)//OK

4 }

Intheexampleabove,Oscar’shealthandenergyarepassedasthetwoin-outparameterstobalance(_:_:).Thecompilercanprovethatmemorysafetyispreservedbecausethetwostoredpropertiesdon’tinteractinanyway.

Therestrictionagainstoverlappingaccesstopropertiesofastructureisn’talwaysnecessarytopreservememorysafety.Memorysafetyisthedesiredguarantee,butexclusiveaccessisastricterrequirementthanmemorysafety—whichmeanssomecodepreservesmemorysafety,eventhoughitviolatesexclusiveaccesstomemory.Swiftallowsthismemory-safecodeifthecompilercanprovethatthenonexclusiveaccesstomemoryisstillsafe.Specifically,itcanprovethatoverlappingaccesstopropertiesofastructureissafeifthefollowingconditionsapply:

You’reaccessingonlystoredpropertiesofaninstance,notcomputedpropertiesorclassproperties.

Thestructureisthevalueofalocalvariable,notaglobalvariable.

Thestructureiseithernotcapturedbyanyclosures,orit’scapturedonlybynonescapingclosures.

Ifthecompilercan’tprovetheaccessissafe,itdoesn’tallowtheaccess.

AccessControl

Accesscontrolrestrictsaccesstopartsofyourcodefromcodeinothersourcefilesandmodules.Thisfeatureenablesyoutohidetheimplementationdetailsofyourcode,andtospecifyapreferredinterfacethroughwhichthatcodecanbeaccessedandused.

Youcanassignspecificaccesslevelstoindividualtypes(classes,structures,andenumerations),aswellastoproperties,methods,initializers,andsubscriptsbelongingtothosetypes.Protocolscanberestrictedtoacertaincontext,ascanglobalconstants,variables,andfunctions.

Inadditiontoofferingvariouslevelsofaccesscontrol,Swiftreducestheneedtospecifyexplicitaccesscontrollevelsbyprovidingdefaultaccesslevelsfortypicalscenarios.Indeed,ifyouarewritingasingle-targetapp,youmaynotneedtospecifyexplicitaccesscontrollevelsatall.

NOTE

Thevariousaspectsofyourcodethatcanhaveaccesscontrolappliedtothem(properties,types,functions,andsoon)arereferredtoas“entities”inthesectionsbelow,forbrevity.

ModulesandSourceFiles

Swift’saccesscontrolmodelisbasedontheconceptofmodulesandsourcefiles.

Amoduleisasingleunitofcodedistribution—aframeworkorapplicationthatisbuiltandshippedasasingleunitandthatcanbeimportedbyanothermodulewithSwift’simportkeyword.

Eachbuildtarget(suchasanappbundleorframework)inXcodeistreatedasaseparatemoduleinSwift.Ifyougrouptogetheraspectsofyourapp’scodeasastand-aloneframework—perhapstoencapsulateandreusethatcodeacrossmultipleapplications—theneverythingyoudefinewithinthatframeworkwillbe

partofaseparatemodulewhenit’simportedandusedwithinanapp,orwhenit’susedwithinanotherframework.

AsourcefileisasingleSwiftsourcecodefilewithinamodule(ineffect,asinglefilewithinanapporframework).Althoughit’scommontodefineindividualtypesinseparatesourcefiles,asinglesourcefilecancontaindefinitionsformultipletypes,functions,andsoon.

AccessLevels

Swiftprovidesfivedifferentaccesslevelsforentitieswithinyourcode.Theseaccesslevelsarerelativetothesourcefileinwhichanentityisdefined,andalsorelativetothemodulethatsourcefilebelongsto.

Openaccessandpublicaccessenableentitiestobeusedwithinanysourcefilefromtheirdefiningmodule,andalsoinasourcefilefromanothermodulethatimportsthedefiningmodule.Youtypicallyuseopenorpublicaccesswhenspecifyingthepublicinterfacetoaframework.Thedifferencebetweenopenandpublicaccessisdescribedbelow.

Internalaccessenablesentitiestobeusedwithinanysourcefilefromtheirdefiningmodule,butnotinanysourcefileoutsideofthatmodule.Youtypicallyuseinternalaccesswhendefininganapp’soraframework’sinternalstructure.

File-privateaccessrestrictstheuseofanentitytoitsowndefiningsourcefile.Usefile-privateaccesstohidetheimplementationdetailsofaspecificpieceoffunctionalitywhenthosedetailsareusedwithinanentirefile.

Privateaccessrestrictstheuseofanentitytotheenclosingdeclaration,andtoextensionsofthatdeclarationthatareinthesamefile.Useprivateaccesstohidetheimplementationdetailsofaspecificpieceoffunctionalitywhenthosedetailsareusedonlywithinasingledeclaration.

Openaccessisthehighest(leastrestrictive)accesslevelandprivateaccessisthelowest(mostrestrictive)accesslevel.

Openaccessappliesonlytoclassesandclassmembers,anditdiffersfrompublicaccessbyallowingcodeoutsidethemoduletosubclassandoverride,asdiscussedbelowinSubclassing.Markingaclassasopenexplicitlyindicatesthatyou’veconsideredtheimpactofcodefromothermodulesusingthatclassasasuperclass,andthatyou’vedesignedyourclass’scodeaccordingly.

GuidingPrincipleofAccessLevelsAccesslevelsinSwiftfollowanoverallguidingprinciple:Noentitycanbedefinedintermsofanotherentitythathasalower(morerestrictive)accesslevel.

Forexample:

Apublicvariablecan’tbedefinedashavinganinternal,file-private,orprivatetype,becausethetypemightnotbeavailableeverywherethatthepublicvariableisused.

Afunctioncan’thaveahigheraccesslevelthanitsparametertypesandreturntype,becausethefunctioncouldbeusedinsituationswhereitsconstituenttypesareunavailabletothesurroundingcode.

Thespecificimplicationsofthisguidingprinciplefordifferentaspectsofthelanguagearecoveredindetailbelow.

DefaultAccessLevelsAllentitiesinyourcode(withafewspecificexceptions,asdescribedlaterinthischapter)haveadefaultaccesslevelofinternalifyoudon’tspecifyanexplicitaccesslevelyourself.Asaresult,inmanycasesyoudon’tneedtospecifyanexplicitaccesslevelinyourcode.

AccessLevelsforSingle-TargetAppsWhenyouwriteasimplesingle-targetapp,thecodeinyourappistypicallyself-containedwithintheappanddoesn’tneedtobemadeavailableoutsideofthe

app’smodule.Thedefaultaccesslevelofinternalalreadymatchesthisrequirement.Therefore,youdon’tneedtospecifyacustomaccesslevel.Youmay,however,wanttomarksomepartsofyourcodeasfileprivateorprivateinordertohidetheirimplementationdetailsfromothercodewithintheapp’smodule.

AccessLevelsforFrameworksWhenyoudevelopaframework,markthepublic-facinginterfacetothatframeworkasopenorpublicsothatitcanbeviewedandaccessedbyothermodules,suchasanappthatimportstheframework.Thispublic-facinginterfaceistheapplicationprogramminginterface(orAPI)fortheframework.

NOTE

Anyinternalimplementationdetailsofyourframeworkcanstillusethedefaultaccesslevelofinternal,orcanbemarkedasprivateorfileprivateifyouwanttohidethemfromotherpartsoftheframework’sinternalcode.Youneedtomarkanentityasopenorpubliconlyifyouwantittobecomepartofyourframework’sAPI.

AccessLevelsforUnitTestTargetsWhenyouwriteanappwithaunittesttarget,thecodeinyourappneedstobemadeavailabletothatmoduleinordertobetested.Bydefault,onlyentitiesmarkedasopenorpublicareaccessibletoothermodules.However,aunittesttargetcanaccessanyinternalentity,ifyoumarktheimportdeclarationforaproductmodulewiththe@testableattributeandcompilethatproductmodulewithtestingenabled.

AccessControlSyntax

Definetheaccesslevelforanentitybyplacingoneoftheopen,public,internal,fileprivate,orprivatemodifiersatthebeginningoftheentity’sdeclaration.

1 publicclassSomePublicClass{}

2 internalclassSomeInternalClass{}

3 fileprivateclassSomeFilePrivateClass{}

4 privateclassSomePrivateClass{}

5

6 publicvarsomePublicVariable=0

7 internalletsomeInternalConstant=0

8 fileprivatefuncsomeFilePrivateFunction(){}

9 privatefuncsomePrivateFunction(){}

Unlessotherwisespecified,thedefaultaccesslevelisinternal,asdescribedinDefaultAccessLevels.ThismeansthatSomeInternalClassandsomeInternalConstantcanbewrittenwithoutanexplicitaccess-levelmodifier,andwillstillhaveanaccesslevelofinternal:

1 classSomeInternalClass{}//implicitlyinternal

2 letsomeInternalConstant=0//implicitlyinternal

CustomTypes

Ifyouwanttospecifyanexplicitaccesslevelforacustomtype,dosoatthepointthatyoudefinethetype.Thenewtypecanthenbeusedwhereveritsaccesslevelpermits.Forexample,ifyoudefineafile-privateclass,thatclasscanonlybeusedasthetypeofaproperty,orasafunctionparameterorreturntype,inthesourcefileinwhichthefile-privateclassisdefined.

Theaccesscontrollevelofatypealsoaffectsthedefaultaccesslevelofthattype’smembers(itsproperties,methods,initializers,andsubscripts).Ifyoudefineatype’saccesslevelasprivateorfileprivate,thedefaultaccesslevelofitsmemberswillalsobeprivateorfileprivate.Ifyoudefineatype’saccesslevelasinternalorpublic(orusethedefaultaccesslevelofinternalwithoutspecifyinganaccesslevelexplicitly),thedefaultaccesslevelofthetype’s

memberswillbeinternal.

IMPORTANT

Apublictypedefaultstohavinginternalmembers,notpublicmembers.Ifyouwantatypemembertobepublic,youmustexplicitlymarkitassuch.Thisrequirementensuresthatthepublic-facingAPIforatypeissomethingyouoptintopublishing,andavoidspresentingtheinternalworkingsofatypeaspublicAPIbymistake.

1 publicclassSomePublicClass{//explicitly

publicclass

2 publicvarsomePublicProperty=0//explicitly

publicclassmember

3 varsomeInternalProperty=0//implicitly

internalclassmember

4 fileprivatefuncsomeFilePrivateMethod(){}//explicitly

file-privateclassmember

5 privatefuncsomePrivateMethod(){}//explicitly

privateclassmember

6 }

7

8 classSomeInternalClass{//implicitly

internalclass

9 varsomeInternalProperty=0//implicitly

internalclassmember

10 fileprivatefuncsomeFilePrivateMethod(){}//explicitly

file-privateclassmember

11 privatefuncsomePrivateMethod(){}//explicitly

privateclassmember

12 }

13

14 fileprivateclassSomeFilePrivateClass{//explicitly

file-privateclass

15 funcsomeFilePrivateMethod(){}//implicitly

file-privateclassmember

16 privatefuncsomePrivateMethod(){}//explicitly

privateclassmember

17 }

18

19 privateclassSomePrivateClass{//explicitly

privateclass

20 funcsomePrivateMethod(){}//implicitly

privateclassmember

21 }

TupleTypesTheaccesslevelforatupletypeisthemostrestrictiveaccesslevelofalltypesusedinthattuple.Forexample,ifyoucomposeatuplefromtwodifferenttypes,onewithinternalaccessandonewithprivateaccess,theaccesslevelforthatcompoundtupletypewillbeprivate.

NOTE

Tupletypesdon’thaveastandalonedefinitioninthewaythatclasses,structures,enumerations,andfunctionsdo.Atupletype’saccesslevelisdeterminedautomaticallyfromthetypesthatmakeupthetupletype,andcan’tbespecifiedexplicitly.

FunctionTypesTheaccesslevelforafunctiontypeiscalculatedasthemostrestrictiveaccesslevelofthefunction’sparametertypesandreturntype.Youmustspecifytheaccesslevelexplicitlyaspartofthefunction’sdefinitionifthefunction’scalculatedaccessleveldoesn’tmatchthecontextualdefault.

TheexamplebelowdefinesaglobalfunctioncalledsomeFunction(),withoutprovidingaspecificaccess-levelmodifierforthefunctionitself.Youmightexpectthisfunctiontohavethedefaultaccesslevelof“internal”,butthisisn’tthecase.Infact,someFunction()won’tcompileaswrittenbelow:

1 funcsomeFunction()->(SomeInternalClass,SomePrivateClass){

2 //functionimplementationgoeshere

3 }

Thefunction’sreturntypeisatupletypecomposedfromtwoofthecustomclassesdefinedaboveinCustomTypes.Oneoftheseclassesisdefinedasinternal,andtheotherisdefinedasprivate.Therefore,theoverallaccesslevelofthecompoundtupletypeisprivate(theminimumaccesslevelofthetuple’sconstituenttypes).

Becausethefunction’sreturntypeisprivate,youmustmarkthefunction’soverallaccesslevelwiththeprivatemodifierforthefunctiondeclarationtobevalid:

1 privatefuncsomeFunction()->(SomeInternalClass,

SomePrivateClass){

2 //functionimplementationgoeshere

3 }

It’snotvalidtomarkthedefinitionofsomeFunction()withthepublicorinternalmodifiers,ortousethedefaultsettingofinternal,becausepublicorinternalusersofthefunctionmightnothaveappropriateaccesstotheprivateclassusedinthefunction’sreturntype.

EnumerationTypesTheindividualcasesofanenumerationautomaticallyreceivethesameaccesslevelastheenumerationtheybelongto.Youcan’tspecifyadifferentaccesslevelforindividualenumerationcases.

Intheexamplebelow,theCompassPointenumerationhasanexplicitaccesslevelofpublic.Theenumerationcasesnorth,south,east,andwestthereforealsohaveanaccesslevelofpublic:

1 publicenumCompassPoint{

2 casenorth

3 casesouth

4 caseeast

5 casewest

6 }

RawValuesandAssociatedValues

Thetypesusedforanyrawvaluesorassociatedvaluesinanenumerationdefinitionmusthaveanaccesslevelatleastashighastheenumeration’saccesslevel.Forexample,youcan’tuseaprivatetypeastheraw-valuetypeofanenumerationwithaninternalaccesslevel.

NestedTypesTheaccesslevelofanestedtypeisthesameasitscontainingtype,unlessthecontainingtypeispublic.Nestedtypesdefinedwithinapublictypehaveanautomaticaccesslevelofinternal.Ifyouwantanestedtypewithinapublictypetobepubliclyavailable,youmustexplicitlydeclarethenestedtypeaspublic.

Subclassing

Youcansubclassanyclassthatcanbeaccessedinthecurrentaccesscontextandthat’sdefinedinthesamemoduleasthesubclass.Youcanalsosubclassanyopenclassthat’sdefinedinadifferentmodule.Asubclasscan’thaveahigheraccesslevelthanitssuperclass—forexample,youcan’twriteapublicsubclassofaninternalsuperclass.

Inaddition,forclassesthataredefinedinthesamemodule,youcanoverrideanyclassmember(method,property,initializer,orsubscript)that’svisibleinacertainaccesscontext.Forclassesthataredefinedinanothermodule,youcanoverrideanyopenclassmember.

Anoverridecanmakeaninheritedclassmembermoreaccessiblethanitssuperclassversion.Intheexamplebelow,classAisapublicclasswithafile-privatemethodcalledsomeMethod().ClassBisasubclassofA,withareducedaccesslevelof“internal”.Nonetheless,classBprovidesanoverrideofsomeMethod()withanaccesslevelof“internal”,whichishigherthantheoriginalimplementationofsomeMethod():

1 publicclassA{

2 fileprivatefuncsomeMethod(){}

3 }

4

5 internalclassB:A{

6 overrideinternalfuncsomeMethod(){}

7 }

It’sevenvalidforasubclassmembertocallasuperclassmemberthathasloweraccesspermissionsthanthesubclassmember,aslongasthecalltothesuperclass’smembertakesplacewithinanallowedaccesslevelcontext(thatis,withinthesamesourcefileasthesuperclassforafile-privatemembercall,orwithinthesamemoduleasthesuperclassforaninternalmembercall):

1 publicclassA{

2 fileprivatefuncsomeMethod(){}

3 }

4

5 internalclassB:A{

6 overrideinternalfuncsomeMethod(){

7 super.someMethod()

8 }

9 }

BecausesuperclassAandsubclassBaredefinedinthesamesourcefile,it’svalidfortheBimplementationofsomeMethod()tocallsuper.someMethod().

Constants,Variables,Properties,andSubscripts

Aconstant,variable,orpropertycan’tbemorepublicthanitstype.It’snotvalidtowriteapublicpropertywithaprivatetype,forexample.Similarly,asubscriptcan’tbemorepublicthaneitheritsindextypeorreturntype.

Ifaconstant,variable,property,orsubscriptmakesuseofaprivatetype,theconstant,variable,property,orsubscriptmustalsobemarkedasprivate:

privatevarprivateInstance=SomePrivateClass()

GettersandSettersGettersandsettersforconstants,variables,properties,andsubscriptsautomaticallyreceivethesameaccesslevelastheconstant,variable,property,orsubscripttheybelongto.

Youcangiveasetteraloweraccesslevelthanitscorrespondinggetter,torestricttheread-writescopeofthatvariable,property,orsubscript.Youassignaloweraccesslevelbywritingfileprivate(set),private(set),orinternal(set)beforethevarorsubscriptintroducer.

NOTE

Thisruleappliestostoredpropertiesaswellascomputedproperties.Eventhoughyoudon’twriteanexplicitgetterandsetterforastoredproperty,Swiftstillsynthesizesanimplicitgetterandsetterforyoutoprovideaccesstothestoredproperty’sbackingstorage.Usefileprivate(set),private(set),andinternal(set)tochangetheaccesslevelofthissynthesizedsetterinexactlythesamewayasforanexplicitsetterinacomputedproperty.

TheexamplebelowdefinesastructurecalledTrackedString,whichkeepstrackofthenumberoftimesastringpropertyismodified:

1 structTrackedString{

2 private(set)varnumberOfEdits=0

3 varvalue:String=""{

4 didSet{

5 numberOfEdits+=1

6 }

7 }

8 }

TheTrackedStringstructuredefinesastoredstringpropertycalledvalue,withaninitialvalueof""(anemptystring).ThestructurealsodefinesastoredintegerpropertycallednumberOfEdits,whichisusedtotrackthenumberoftimesthatvalueismodified.ThismodificationtrackingisimplementedwithadidSetpropertyobserveronthevalueproperty,whichincrementsnumberOfEditseverytimethevaluepropertyissettoanewvalue.

TheTrackedStringstructureandthevaluepropertydon’tprovideanexplicitaccess-levelmodifier,andsotheybothreceivethedefaultaccesslevelofinternal.However,theaccesslevelforthenumberOfEditspropertyismarkedwithaprivate(set)modifiertoindicatethattheproperty’sgetterstillhasthedefaultaccesslevelofinternal,butthepropertyissettableonlyfromwithincodethat’spartoftheTrackedStringstructure.ThisenablesTrackedStringtomodifythenumberOfEditspropertyinternally,buttopresentthepropertyasaread-onlypropertywhenit’susedoutsidethestructure’sdefinition.

IfyoucreateaTrackedStringinstanceandmodifyitsstringvalueafewtimes,youcanseethenumberOfEditspropertyvalueupdatetomatchthenumberofmodifications:

1 varstringToEdit=TrackedString()

2 stringToEdit.value="Thisstringwillbetracked."

3 stringToEdit.value+="Thiseditwillincrement

numberOfEdits."

4 stringToEdit.value+="Sowillthisone."

5 print("Thenumberofeditsis\(stringToEdit.numberOfEdits)")

6 //Prints"Thenumberofeditsis3"

AlthoughyoucanquerythecurrentvalueofthenumberOfEditspropertyfromwithinanothersourcefile,youcan’tmodifythepropertyfromanothersourcefile.ThisrestrictionprotectstheimplementationdetailsoftheTrackedStringedit-trackingfunctionality,whilestillprovidingconvenientaccesstoanaspectofthatfunctionality.

Notethatyoucanassignanexplicitaccesslevelforbothagetterandasetterifrequired.TheexamplebelowshowsaversionoftheTrackedStringstructureinwhichthestructureisdefinedwithanexplicitaccesslevelofpublic.Thestructure’smembers(includingthenumberOfEditsproperty)thereforehaveaninternalaccesslevelbydefault.Youcanmakethestructure’snumberOfEditspropertygetterpublic,anditspropertysetterprivate,bycombiningthepublicandprivate(set)access-levelmodifiers:

1 publicstructTrackedString{

2 publicprivate(set)varnumberOfEdits=0

3 publicvarvalue:String=""{

4 didSet{

5 numberOfEdits+=1

6 }

7 }

8 publicinit(){}

9 }

Initializers

Custominitializerscanbeassignedanaccesslevellessthanorequaltothetype

thattheyinitialize.Theonlyexceptionisforrequiredinitializers(asdefinedinRequiredInitializers).Arequiredinitializermusthavethesameaccesslevelastheclassitbelongsto.

Aswithfunctionandmethodparameters,thetypesofaninitializer’sparameterscan’tbemoreprivatethantheinitializer’sownaccesslevel.

DefaultInitializersAsdescribedinDefaultInitializers,Swiftautomaticallyprovidesadefaultinitializerwithoutanyargumentsforanystructureorbaseclassthatprovidesdefaultvaluesforallofitspropertiesanddoesn’tprovideatleastoneinitializeritself.

Adefaultinitializerhasthesameaccesslevelasthetypeitinitializes,unlessthattypeisdefinedaspublic.Foratypethatisdefinedaspublic,thedefaultinitializerisconsideredinternal.Ifyouwantapublictypetobeinitializablewithano-argumentinitializerwhenusedinanothermodule,youmustexplicitlyprovideapublicno-argumentinitializeryourselfaspartofthetype’sdefinition.

DefaultMemberwiseInitializersforStructureTypesThedefaultmemberwiseinitializerforastructuretypeisconsideredprivateifanyofthestructure’sstoredpropertiesareprivate.Likewise,ifanyofthestructure’sstoredpropertiesarefileprivate,theinitializerisfileprivate.Otherwise,theinitializerhasanaccesslevelofinternal.

Aswiththedefaultinitializerabove,ifyouwantapublicstructuretypetobeinitializablewithamemberwiseinitializerwhenusedinanothermodule,youmustprovideapublicmemberwiseinitializeryourselfaspartofthetype’sdefinition.

Protocols

Ifyouwanttoassignanexplicitaccessleveltoaprotocoltype,dosoatthepointthatyoudefinetheprotocol.Thisenablesyoutocreateprotocolsthatcanonlybeadoptedwithinacertainaccesscontext.

Theaccesslevelofeachrequirementwithinaprotocoldefinitionisautomaticallysettothesameaccesslevelastheprotocol.Youcan’tsetaprotocolrequirementtoadifferentaccesslevelthantheprotocolitsupports.Thisensuresthatalloftheprotocol’srequirementswillbevisibleonanytypethatadoptstheprotocol.

NOTE

Ifyoudefineapublicprotocol,theprotocol’srequirementsrequireapublicaccesslevelforthoserequirementswhenthey’reimplemented.Thisbehaviorisdifferentfromothertypes,whereapublictypedefinitionimpliesanaccesslevelofinternalforthetype’smembers.

ProtocolInheritanceIfyoudefineanewprotocolthatinheritsfromanexistingprotocol,thenewprotocolcanhaveatmostthesameaccesslevelastheprotocolitinheritsfrom.Forexample,youcan’twriteapublicprotocolthatinheritsfromaninternalprotocol.

ProtocolConformanceAtypecanconformtoaprotocolwithaloweraccesslevelthanthetypeitself.Forexample,youcandefineapublictypethatcanbeusedinothermodules,butwhoseconformancetoaninternalprotocolcanonlybeusedwithintheinternalprotocol’sdefiningmodule.

Thecontextinwhichatypeconformstoaparticularprotocolistheminimumofthetype’saccesslevelandtheprotocol’saccesslevel.Forexample,ifatypeispublic,butaprotocolitconformstoisinternal,thetype’sconformancetothatprotocolisalsointernal.

Whenyouwriteorextendatypetoconformtoaprotocol,youmustensurethat

thetype’simplementationofeachprotocolrequirementhasatleastthesameaccesslevelasthetype’sconformancetothatprotocol.Forexample,ifapublictypeconformstoaninternalprotocol,thetype’simplementationofeachprotocolrequirementmustbeatleastinternal.

NOTE

InSwift,asinObjective-C,protocolconformanceisglobal—itisn’tpossibleforatypetoconformtoaprotocolintwodifferentwayswithinthesameprogram.

Extensions

Youcanextendaclass,structure,orenumerationinanyaccesscontextinwhichtheclass,structure,orenumerationisavailable.Anytypemembersaddedinanextensionhavethesamedefaultaccesslevelastypemembersdeclaredintheoriginaltypebeingextended.Ifyouextendapublicorinternaltype,anynewtypemembersyouaddhaveadefaultaccesslevelofinternal.Ifyouextendafile-privatetype,anynewtypemembersyouaddhaveadefaultaccessleveloffileprivate.Ifyouextendaprivatetype,anynewtypemembersyouaddhaveadefaultaccesslevelofprivate.

Alternatively,youcanmarkanextensionwithanexplicitaccess-levelmodifier(forexample,private)tosetanewdefaultaccesslevelforallmembersdefinedwithintheextension.Thisnewdefaultcanstillbeoverriddenwithintheextensionforindividualtypemembers.

Youcan’tprovideanexplicitaccess-levelmodifierforanextensionifyou’reusingthatextensiontoaddprotocolconformance.Instead,theprotocol’sownaccesslevelisusedtoprovidethedefaultaccesslevelforeachprotocolrequirementimplementationwithintheextension.

PrivateMembersinExtensionsExtensionsthatareinthesamefileastheclass,structure,orenumerationthattheyextendbehaveasifthecodeintheextensionhadbeenwrittenaspartofthe

originaltype’sdeclaration.Asaresult,youcan:

Declareaprivatememberintheoriginaldeclaration,andaccessthatmemberfromextensionsinthesamefile.

Declareaprivatememberinoneextension,andaccessthatmemberfromanotherextensioninthesamefile.

Declareaprivatememberinanextension,andaccessthatmemberfromtheoriginaldeclarationinthesamefile.

Thisbehaviormeansyoucanuseextensionsinthesamewaytoorganizeyourcode,whetherornotyourtypeshaveprivateentities.Forexample,giventhefollowingsimpleprotocol:

1 protocolSomeProtocol{

2 funcdoSomething()

3 }

Youcanuseanextensiontoaddprotocolconformance,likethis:

1 structSomeStruct{

2 privatevarprivateVariable=12

3 }

4

5 extensionSomeStruct:SomeProtocol{

6 funcdoSomething(){

7 print(privateVariable)

8 }

9 }

Generics

Theaccesslevelforagenerictypeorgenericfunctionistheminimumoftheaccesslevelofthegenerictypeorfunctionitselfandtheaccesslevelofanytypeconstraintsonitstypeparameters.

TypeAliases

Anytypealiasesyoudefinearetreatedasdistincttypesforthepurposesofaccesscontrol.Atypealiascanhaveanaccesslevellessthanorequaltotheaccesslevelofthetypeitaliases.Forexample,aprivatetypealiascanaliasaprivate,file-private,internal,public,oropentype,butapublictypealiascan’taliasaninternal,file-private,orprivatetype.

NOTE

Thisrulealsoappliestotypealiasesforassociatedtypesusedtosatisfyprotocolconformances.

AdvancedOperators

InadditiontotheoperatorsdescribedinBasicOperators,Swiftprovidesseveraladvancedoperatorsthatperformmorecomplexvaluemanipulation.TheseincludeallofthebitwiseandbitshiftingoperatorsyouwillbefamiliarwithfromCandObjective-C.

UnlikearithmeticoperatorsinC,arithmeticoperatorsinSwiftdonotoverflowbydefault.Overflowbehavioristrappedandreportedasanerror.Tooptintooverflowbehavior,useSwift’ssecondsetofarithmeticoperatorsthatoverflowbydefault,suchastheoverflowadditionoperator(&+).Alloftheseoverflowoperatorsbeginwithanampersand(&).

Whenyoudefineyourownstructures,classes,andenumerations,itcanbeusefultoprovideyourownimplementationsofthestandardSwiftoperatorsforthesecustomtypes.Swiftmakesiteasytoprovidetailoredimplementationsoftheseoperatorsandtodetermineexactlywhattheirbehaviorshouldbeforeachtypeyoucreate.

You’renotlimitedtothepredefinedoperators.Swiftgivesyouthefreedomtodefineyourowncustominfix,prefix,postfix,andassignmentoperators,withcustomprecedenceandassociativityvalues.Theseoperatorscanbeusedandadoptedinyourcodelikeanyofthepredefinedoperators,andyoucanevenextendexistingtypestosupportthecustomoperatorsyoudefine.

BitwiseOperators

Bitwiseoperatorsenableyoutomanipulatetheindividualrawdatabitswithinadatastructure.Theyareoftenusedinlow-levelprogramming,suchasgraphicsprogramminganddevicedrivercreation.Bitwiseoperatorscanalsobeusefulwhenyouworkwithrawdatafromexternalsources,suchasencodinganddecodingdataforcommunicationoveracustomprotocol.

SwiftsupportsallofthebitwiseoperatorsfoundinC,asdescribedbelow.

BitwiseNOTOperatorThebitwiseNOToperator(~)invertsallbitsinanumber:

ThebitwiseNOToperatorisaprefixoperator,andappearsimmediatelybeforethevalueitoperateson,withoutanywhitespace:

1 letinitialBits:UInt8=0b00001111

2 letinvertedBits=~initialBits//equals11110000

UInt8integershaveeightbitsandcanstoreanyvaluebetween0and255.ThisexampleinitializesaUInt8integerwiththebinaryvalue00001111,whichhasitsfirstfourbitssetto0,anditssecondfourbitssetto1.Thisisequivalenttoadecimalvalueof15.

ThebitwiseNOToperatoristhenusedtocreateanewconstantcalledinvertedBits,whichisequaltoinitialBits,butwithallofthebitsinverted.Zerosbecomeones,andonesbecomezeros.ThevalueofinvertedBitsis11110000,whichisequaltoanunsigneddecimalvalueof240.

BitwiseANDOperatorThebitwiseANDoperator(&)combinesthebitsoftwonumbers.Itreturnsanewnumberwhosebitsaresetto1onlyifthebitswereequalto1inbothinputnumbers:

Intheexamplebelow,thevaluesoffirstSixBitsandlastSixBitsbothhavefourmiddlebitsequalto1.ThebitwiseANDoperatorcombinesthemtomakethenumber00111100,whichisequaltoanunsigneddecimalvalueof60:

1 letfirstSixBits:UInt8=0b11111100

2 letlastSixBits:UInt8=0b00111111

3 letmiddleFourBits=firstSixBits&lastSixBits//equals

00111100

BitwiseOROperatorThebitwiseORoperator(|)comparesthebitsoftwonumbers.Theoperatorreturnsanewnumberwhosebitsaresetto1ifthebitsareequalto1ineitherinputnumber:

Intheexamplebelow,thevaluesofsomeBitsandmoreBitshavedifferentbitsset

to1.ThebitwiseORoperatorcombinesthemtomakethenumber11111110,whichequalsanunsigneddecimalof254:

1 letsomeBits:UInt8=0b10110010

2 letmoreBits:UInt8=0b01011110

3 letcombinedbits=someBits|moreBits//equals11111110

BitwiseXOROperatorThebitwiseXORoperator,or“exclusiveORoperator”(^),comparesthebitsoftwonumbers.Theoperatorreturnsanewnumberwhosebitsaresetto1wheretheinputbitsaredifferentandaresetto0wheretheinputbitsarethesame:

Intheexamplebelow,thevaluesoffirstBitsandotherBitseachhaveabitsetto1inalocationthattheotherdoesnot.ThebitwiseXORoperatorsetsbothofthesebitsto1initsoutputvalue.AlloftheotherbitsinfirstBitsandotherBitsmatchandaresetto0intheoutputvalue:

1 letfirstBits:UInt8=0b00010100

2 letotherBits:UInt8=0b00000101

3 letoutputBits=firstBits^otherBits//equals00010001

BitwiseLeftandRightShiftOperators

Thebitwiseleftshiftoperator(<<)andbitwiserightshiftoperator(>>)moveallbitsinanumbertotheleftortherightbyacertainnumberofplaces,accordingtotherulesdefinedbelow.

Bitwiseleftandrightshiftshavetheeffectofmultiplyingordividinganintegerbyafactoroftwo.Shiftinganinteger’sbitstotheleftbyonepositiondoublesitsvalue,whereasshiftingittotherightbyonepositionhalvesitsvalue.

ShiftingBehaviorforUnsignedIntegers

Thebit-shiftingbehaviorforunsignedintegersisasfollows:

1. Existingbitsaremovedtotheleftorrightbytherequestednumberofplaces.

2. Anybitsthataremovedbeyondtheboundsoftheinteger’sstoragearediscarded.

3. Zerosareinsertedinthespacesleftbehindaftertheoriginalbitsaremovedtotheleftorright.

Thisapproachisknownasalogicalshift.

Theillustrationbelowshowstheresultsof11111111<<1(whichis11111111shiftedtotheleftby1place),and11111111>>1(whichis11111111shiftedtotherightby1place).Bluenumbersareshifted,graynumbersarediscarded,andorangezerosareinserted:

Here’showbitshiftinglooksinSwiftcode:

1 letshiftBits:UInt8=4//00000100inbinary

2 shiftBits<<1//00001000

3 shiftBits<<2//00010000

4 shiftBits<<5//10000000

5 shiftBits<<6//00000000

6 shiftBits>>2//00000001

Youcanusebitshiftingtoencodeanddecodevalueswithinotherdatatypes:

1 letpink:UInt32=0xCC6699

2 letredComponent=(pink&0xFF0000)>>16//redComponent

is0xCC,or204

3 letgreenComponent=(pink&0x00FF00)>>8//greenComponent

is0x66,or102

4 letblueComponent=pink&0x0000FF//blueComponent

is0x99,or153

ThisexampleusesaUInt32constantcalledpinktostoreaCascadingStyleSheetscolorvalueforthecolorpink.TheCSScolorvalue#CC6699iswrittenas0xCC6699inSwift’shexadecimalnumberrepresentation.Thiscoloristhendecomposedintoitsred(CC),green(66),andblue(99)componentsbythebitwiseANDoperator(&)andthebitwiserightshiftoperator(>>).

TheredcomponentisobtainedbyperformingabitwiseANDbetweenthenumbers0xCC6699and0xFF0000.Thezerosin0xFF0000effectively“mask”thesecondandthirdbytesof0xCC6699,causingthe6699tobeignoredandleaving0xCC0000astheresult.

Thisnumberisthenshifted16placestotheright(>>16).Eachpairofcharactersinahexadecimalnumberuses8bits,soamove16placestotherightwillconvert0xCC0000into0x0000CC.Thisisthesameas0xCC,whichhasadecimalvalueof204.

Similarly,thegreencomponentisobtainedbyperformingabitwiseANDbetweenthenumbers0xCC6699and0x00FF00,whichgivesanoutputvalueof0x006600.Thisoutputvalueisthenshiftedeightplacestotheright,givinga

valueof0x66,whichhasadecimalvalueof102.

Finally,thebluecomponentisobtainedbyperformingabitwiseANDbetweenthenumbers0xCC6699and0x0000FF,whichgivesanoutputvalueof0x000099.There’snoneedtoshiftthistotheright,as0x000099alreadyequals0x99,whichhasadecimalvalueof153.

ShiftingBehaviorforSignedIntegers

Theshiftingbehaviorismorecomplexforsignedintegersthanforunsignedintegers,becauseofthewaysignedintegersarerepresentedinbinary.(Theexamplesbelowarebasedon8-bitsignedintegersforsimplicity,butthesameprinciplesapplyforsignedintegersofanysize.)

Signedintegersusetheirfirstbit(knownasthesignbit)toindicatewhethertheintegerispositiveornegative.Asignbitof0meanspositive,andasignbitof1meansnegative.

Theremainingbits(knownasthevaluebits)storetheactualvalue.Positivenumbersarestoredinexactlythesamewayasforunsignedintegers,countingupwardsfrom0.Here’showthebitsinsideanInt8lookforthenumber4:

Thesignbitis0(meaning“positive”),andthesevenvaluebitsarejustthenumber4,writteninbinarynotation.

Negativenumbers,however,arestoreddifferently.Theyarestoredbysubtractingtheirabsolutevaluefrom2tothepowerofn,wherenisthenumberofvaluebits.Aneight-bitnumberhassevenvaluebits,sothismeans2tothepowerof7,or128.

Here’showthebitsinsideanInt8lookforthenumber-4:

Thistime,thesignbitis1(meaning“negative”),andthesevenvaluebitshaveabinaryvalueof124(whichis128-4):

Thisencodingfornegativenumbersisknownasatwo’scomplementrepresentation.Itmayseemanunusualwaytorepresentnegativenumbers,butithasseveraladvantages.

First,youcanadd-1to-4,simplybyperformingastandardbinaryadditionofalleightbits(includingthesignbit),anddiscardinganythingthatdoesn’tfitintheeightbitsonceyou’redone:

Second,thetwo’scomplementrepresentationalsoletsyoushiftthebitsofnegativenumberstotheleftandrightlikepositivenumbers,andstillendupdoublingthemforeveryshiftyoumaketotheleft,orhalvingthemforeveryshiftyoumaketotheright.Toachievethis,anextraruleisusedwhensignedintegersareshiftedtotheright:Whenyoushiftsignedintegerstotheright,applythesamerulesasforunsignedintegers,butfillanyemptybitsontheleftwiththesignbit,ratherthanwithazero.

Thisactionensuresthatsignedintegershavethesamesignaftertheyareshiftedtotheright,andisknownasanarithmeticshift.

Becauseofthespecialwaythatpositiveandnegativenumbersarestored,shiftingeitherofthemtotherightmovesthemclosertozero.Keepingthesignbitthesameduringthisshiftmeansthatnegativeintegersremainnegativeastheirvaluemovesclosertozero.

OverflowOperators

Ifyoutrytoinsertanumberintoanintegerconstantorvariablethatcannotholdthatvalue,bydefaultSwiftreportsanerrorratherthanallowinganinvalidvaluetobecreated.Thisbehaviorgivesextrasafetywhenyouworkwithnumbersthataretoolargeortoosmall.

Forexample,theInt16integertypecanholdanysignedintegerbetween-32768and32767.TryingtosetanInt16constantorvariabletoanumberoutsideofthisrangecausesanerror:

1 varpotentialOverflow=Int16.max

2 //potentialOverflowequals32767,whichisthemaximumvalue

anInt16canhold

3 potentialOverflow+=1

4 //thiscausesanerror

Providingerrorhandlingwhenvaluesgettoolargeortoosmallgivesyoumuchmoreflexibilitywhencodingforboundaryvalueconditions.

However,whenyouspecificallywantanoverflowconditiontotruncatethenumberofavailablebits,youcanoptintothisbehaviorratherthantriggeringanerror.Swiftprovidesthreearithmeticoverflowoperatorsthatoptintotheoverflowbehaviorforintegercalculations.Theseoperatorsallbeginwithanampersand(&):

Overflowaddition(&+)

Overflowsubtraction(&-)

Overflowmultiplication(&*)

ValueOverflowNumberscanoverflowinboththepositiveandnegativedirection.

Here’sanexampleofwhathappenswhenanunsignedintegerisallowedtooverflowinthepositivedirection,usingtheoverflowadditionoperator(&+):

1 varunsignedOverflow=UInt8.max

2 //unsignedOverflowequals255,whichisthemaximumvaluea

UInt8canhold

3 unsignedOverflow=unsignedOverflow&+1

4 //unsignedOverflowisnowequalto0

ThevariableunsignedOverflowisinitializedwiththemaximumvalueaUInt8canhold(255,or11111111inbinary).Itisthenincrementedby1usingtheoverflowadditionoperator(&+).ThispushesitsbinaryrepresentationjustoverthesizethataUInt8canhold,causingittooverflowbeyonditsbounds,asshowninthediagrambelow.ThevaluethatremainswithintheboundsoftheUInt8aftertheoverflowadditionis00000000,orzero.

Somethingsimilarhappenswhenanunsignedintegerisallowedtooverflowinthenegativedirection.Here’sanexampleusingtheoverflowsubtractionoperator(&-):

1 varunsignedOverflow=UInt8.min

2 //unsignedOverflowequals0,whichistheminimumvaluea

UInt8canhold

3 unsignedOverflow=unsignedOverflow&-1

4 //unsignedOverflowisnowequalto255

TheminimumvaluethataUInt8canholdiszero,or00000000inbinary.Ifyousubtract1from00000000usingtheoverflowsubtractionoperator(&-),thenumberwilloverflowandwraparoundto11111111,or255indecimal.

Overflowalsooccursforsignedintegers.Alladditionandsubtractionforsignedintegersisperformedinbitwisefashion,withthesignbitincludedaspartofthenumbersbeingaddedorsubtracted,asdescribedinBitwiseLeftandRightShiftOperators.

1 varsignedOverflow=Int8.min

2 //signedOverflowequals-128,whichistheminimumvaluean

Int8canhold

3 signedOverflow=signedOverflow&-1

4 //signedOverflowisnowequalto127

TheminimumvaluethatanInt8canholdis-128,or10000000inbinary.Subtracting1fromthisbinarynumberwiththeoverflowoperatorgivesabinaryvalueof01111111,whichtogglesthesignbitandgivespositive127,themaximumpositivevaluethatanInt8canhold.

Forbothsignedandunsignedintegers,overflowinthepositivedirectionwrapsaroundfromthemaximumvalidintegervaluebacktotheminimum,andoverflowinthenegativedirectionwrapsaroundfromtheminimumvaluetothemaximum.

PrecedenceandAssociativity

Operatorprecedencegivessomeoperatorshigherprioritythanothers;theseoperatorsareappliedfirst.

Operatorassociativitydefineshowoperatorsofthesameprecedencearegroupedtogether—eithergroupedfromtheleft,orgroupedfromtheright.Thinkofitasmeaning“theyassociatewiththeexpressiontotheirleft,”or“theyassociatewiththeexpressiontotheirright.”

Itisimportanttoconsidereachoperator’sprecedenceandassociativitywhenworkingouttheorderinwhichacompoundexpressionwillbecalculated.Forexample,operatorprecedenceexplainswhythefollowingexpressionequals17.

1 2+3%4*5

2 //thisequals17

Ifyoureadstrictlyfromlefttoright,youmightexpecttheexpressiontobecalculatedasfollows:

2plus3equals5

5remainder4equals1

1times5equals5

However,theactualansweris17,not5.Higher-precedenceoperatorsareevaluatedbeforelower-precedenceones.InSwift,asinC,theremainderoperator(%)andthemultiplicationoperator(*)haveahigherprecedencethantheadditionoperator(+).Asaresult,theyarebothevaluatedbeforetheadditionisconsidered.

However,remainderandmultiplicationhavethesameprecedenceaseachother.Toworkouttheexactevaluationordertouse,youalsoneedtoconsidertheirassociativity.Remainderandmultiplicationbothassociatewiththeexpressiontotheirleft.Thinkofthisasaddingimplicitparenthesesaroundthesepartsoftheexpression,startingfromtheirleft:

2+((3%4)*5)

(3%4)is3,sothisisequivalentto:

2+(3*5)

(3*5)is15,sothisisequivalentto:

2+15

Thiscalculationyieldsthefinalanswerof17.

ForinformationabouttheoperatorsprovidedbytheSwiftstandardlibrary,includingacompletelistoftheoperatorprecedencegroupsandassociativitysettings,seeOperatorDeclarations.

NOTE

Swift’soperatorprecedencesandassociativityrulesaresimplerandmorepredictablethanthosefoundinCandObjective-C.However,thismeansthattheyarenotexactlythesameasinC-basedlanguages.BecarefultoensurethatoperatorinteractionsstillbehaveinthewayyouintendwhenportingexistingcodetoSwift.

OperatorMethods

Classesandstructurescanprovidetheirownimplementationsofexistingoperators.Thisisknownasoverloadingtheexistingoperators.

Theexamplebelowshowshowtoimplementthearithmeticadditionoperator(+)foracustomstructure.Thearithmeticadditionoperatorisabinaryoperatorbecauseitoperatesontwotargetsandissaidtobeinfixbecauseitappearsinbetweenthosetwotargets.

TheexampledefinesaVector2Dstructureforatwo-dimensionalpositionvector(x,y),followedbyadefinitionofanoperatormethodtoaddtogetherinstancesoftheVector2Dstructure:

1 structVector2D{

2 varx=0.0,y=0.0

3 }

4

5 extensionVector2D{

6 staticfunc+(left:Vector2D,right:Vector2D)->Vector2D

{

7 returnVector2D(x:left.x+right.x,y:left.y+

right.y)

8 }

9 }

TheoperatormethodisdefinedasatypemethodonVector2D,withamethodnamethatmatchestheoperatortobeoverloaded(+).Becauseadditionisn’tpartoftheessentialbehaviorforavector,thetypemethodisdefinedinanextensionofVector2DratherthaninthemainstructuredeclarationofVector2D.Becausethearithmeticadditionoperatorisabinaryoperator,thisoperatormethodtakestwoinputparametersoftypeVector2Dandreturnsasingleoutputvalue,alsooftypeVector2D.

Inthisimplementation,theinputparametersarenamedleftandrighttorepresenttheVector2Dinstancesthatwillbeontheleftsideandrightsideofthe+operator.ThemethodreturnsanewVector2Dinstance,whosexandypropertiesareinitializedwiththesumofthexandypropertiesfromthetwoVector2Dinstancesthatareaddedtogether.

ThetypemethodcanbeusedasaninfixoperatorbetweenexistingVector2Dinstances:

1 letvector=Vector2D(x:3.0,y:1.0)

2 letanotherVector=Vector2D(x:2.0,y:4.0)

3 letcombinedVector=vector+anotherVector

4 //combinedVectorisaVector2Dinstancewithvaluesof(5.0,

5.0)

Thisexampleaddstogetherthevectors(3.0,1.0)and(2.0,4.0)tomakethevector(5.0,5.0),asillustratedbelow.

PrefixandPostfixOperatorsTheexampleshownabovedemonstratesacustomimplementationofabinaryinfixoperator.Classesandstructurescanalsoprovideimplementationsofthestandardunaryoperators.Unaryoperatorsoperateonasingletarget.Theyareprefixiftheyprecedetheirtarget(suchas-a)andpostfixoperatorsiftheyfollowtheirtarget(suchasb!).

Youimplementaprefixorpostfixunaryoperatorbywritingtheprefixorpostfixmodifierbeforethefunckeywordwhendeclaringtheoperatormethod:

1 extensionVector2D{

2 staticprefixfunc-(vector:Vector2D)->Vector2D{

3 returnVector2D(x:-vector.x,y:-vector.y)

4 }

5 }

Theexampleaboveimplementstheunaryminusoperator(-a)forVector2D

instances.Theunaryminusoperatorisaprefixoperator,andsothismethodhastobequalifiedwiththeprefixmodifier.

Forsimplenumericvalues,theunaryminusoperatorconvertspositivenumbersintotheirnegativeequivalentandviceversa.ThecorrespondingimplementationforVector2Dinstancesperformsthisoperationonboththexandyproperties:

1 letpositive=Vector2D(x:3.0,y:4.0)

2 letnegative=-positive

3 //negativeisaVector2Dinstancewithvaluesof(-3.0,-4.0)

4 letalsoPositive=-negative

5 //alsoPositiveisaVector2Dinstancewithvaluesof(3.0,

4.0)

CompoundAssignmentOperatorsCompoundassignmentoperatorscombineassignment(=)withanotheroperation.Forexample,theadditionassignmentoperator(+=)combinesadditionandassignmentintoasingleoperation.Youmarkacompoundassignmentoperator’sleftinputparametertypeasinout,becausetheparameter’svaluewillbemodifieddirectlyfromwithintheoperatormethod.

TheexamplebelowimplementsanadditionassignmentoperatormethodforVector2Dinstances:

1 extensionVector2D{

2 staticfunc+=(left:inoutVector2D,right:Vector2D){

3 left=left+right

4 }

5 }

Becauseanadditionoperatorwasdefinedearlier,youdon’tneedtoreimplementtheadditionprocesshere.Instead,theadditionassignmentoperatormethodtakesadvantageoftheexistingadditionoperatormethod,andusesittosettheleft

valuetobetheleftvalueplustherightvalue:

1 varoriginal=Vector2D(x:1.0,y:2.0)

2 letvectorToAdd=Vector2D(x:3.0,y:4.0)

3 original+=vectorToAdd

4 //originalnowhasvaluesof(4.0,6.0)

NOTE

Itisn’tpossibletooverloadthedefaultassignmentoperator(=).Onlythecompoundassignmentoperatorscanbeoverloaded.Similarly,theternaryconditionaloperator(a?b:c)can’tbeoverloaded.

EquivalenceOperatorsBydefault,customclassesandstructuresdon’thaveanimplementationoftheequivalenceoperators,knownastheequaltooperator(==)andnotequaltooperator(!=).Youusuallyimplementthe==operator,andusethestandardlibrary’sdefaultimplementationofthe!=operatorthatnegatestheresultofthe==operator.Therearetwowaystoimplementthe==operator:Youcanimplementityourself,orformanytypes,youcanaskSwifttosynthesizeanimplementationforyou.Inbothcases,youaddconformancetothestandardlibrary’sEquatableprotocol.

Youprovideanimplementationofthe==operatorinthesamewayasyouimplementotherinfixoperators:

1 extensionVector2D:Equatable{

2 staticfunc==(left:Vector2D,right:Vector2D)->Bool{

3 return(left.x==right.x)&&(left.y==right.y)

4 }

5 }

Theexampleaboveimplementsan==operatortocheckwhethertwoVector2Dinstanceshaveequivalentvalues.InthecontextofVector2D,itmakessenseto

consider“equal”asmeaning“bothinstanceshavethesamexvaluesandyvalues”,andsothisisthelogicusedbytheoperatorimplementation.

YoucannowusethisoperatortocheckwhethertwoVector2Dinstancesareequivalent:

1 lettwoThree=Vector2D(x:2.0,y:3.0)

2 letanotherTwoThree=Vector2D(x:2.0,y:3.0)

3 iftwoThree==anotherTwoThree{

4 print("Thesetwovectorsareequivalent.")

5 }

6 //Prints"Thesetwovectorsareequivalent."

Inmanysimplecases,youcanaskSwifttoprovidesynthesizedimplementationsoftheequivalenceoperatorsforyou.Swiftprovidessynthesizedimplementationsforthefollowingkindsofcustomtypes:

StructuresthathaveonlystoredpropertiesthatconformtotheEquatableprotocol

EnumerationsthathaveonlyassociatedtypesthatconformtotheEquatableprotocol

Enumerationsthathavenoassociatedtypes

Toreceiveasynthesizedimplementationof==,declareEquatableconformanceinthefilethatcontainstheoriginaldeclaration,withoutimplementingan==operatoryourself.

TheexamplebelowdefinesaVector3Dstructureforathree-dimensionalpositionvector(x,y,z),similartotheVector2Dstructure.Becausethex,y,andzpropertiesareallofanEquatabletype,Vector3Dreceivessynthesizedimplementationsoftheequivalenceoperators.

1 structVector3D:Equatable{

2 varx=0.0,y=0.0,z=0.0

3 }

4

5 lettwoThreeFour=Vector3D(x:2.0,y:3.0,z:4.0)

6 letanotherTwoThreeFour=Vector3D(x:2.0,y:3.0,z:4.0)

7 iftwoThreeFour==anotherTwoThreeFour{

8 print("Thesetwovectorsarealsoequivalent.")

9 }

10 //Prints"Thesetwovectorsarealsoequivalent."

CustomOperators

YoucandeclareandimplementyourowncustomoperatorsinadditiontothestandardoperatorsprovidedbySwift.Foralistofcharactersthatcanbeusedtodefinecustomoperators,seeOperators.

Newoperatorsaredeclaredatagloballevelusingtheoperatorkeyword,andaremarkedwiththeprefix,infixorpostfixmodifiers:

prefixoperator+++

Theexampleabovedefinesanewprefixoperatorcalled+++.ThisoperatordoesnothaveanexistingmeaninginSwift,andsoitisgivenitsowncustommeaningbelowinthespecificcontextofworkingwithVector2Dinstances.Forthepurposesofthisexample,+++istreatedasanew“prefixdoubling”operator.ItdoublesthexandyvaluesofaVector2Dinstance,byaddingthevectortoitselfwiththeadditionassignmentoperatordefinedearlier.Toimplementthe+++operator,youaddatypemethodcalled+++toVector2Dasfollows:

1 extensionVector2D{

2 staticprefixfunc+++(vector:inoutVector2D)->Vector2D

{

3 vector+=vector

4 returnvector

5 }

6 }

7

8 vartoBeDoubled=Vector2D(x:1.0,y:4.0)

9 letafterDoubling=+++toBeDoubled

10 //toBeDoublednowhasvaluesof(2.0,8.0)

11 //afterDoublingalsohasvaluesof(2.0,8.0)

PrecedenceforCustomInfixOperatorsCustominfixoperatorseachbelongtoaprecedencegroup.Aprecedencegroupspecifiesanoperator’sprecedencerelativetootherinfixoperators,aswellastheoperator’sassociativity.SeePrecedenceandAssociativityforanexplanationofhowthesecharacteristicsaffectaninfixoperator’sinteractionwithotherinfixoperators.

Acustominfixoperatorthatisnotexplicitlyplacedintoaprecedencegroupisgivenadefaultprecedencegroupwithaprecedenceimmediatelyhigherthantheprecedenceoftheternaryconditionaloperator.

Thefollowingexampledefinesanewcustominfixoperatorcalled+-,whichbelongstotheprecedencegroupAdditionPrecedence:

1 infixoperator+-:AdditionPrecedence

2 extensionVector2D{

3 staticfunc+-(left:Vector2D,right:Vector2D)->

Vector2D{

4 returnVector2D(x:left.x+right.x,y:left.y-

right.y)

5 }

6 }

7 letfirstVector=Vector2D(x:1.0,y:2.0)

8 letsecondVector=Vector2D(x:3.0,y:4.0)

9 letplusMinusVector=firstVector+-secondVector

10 //plusMinusVectorisaVector2Dinstancewithvaluesof(4.0,

-2.0)

Thisoperatoraddstogetherthexvaluesoftwovectors,andsubtractstheyvalueofthesecondvectorfromthefirst.Becauseitisinessencean“additive”operator,ithasbeengiventhesameprecedencegroupasadditiveinfixoperatorssuchas+and-.ForinformationabouttheoperatorsprovidedbytheSwiftstandardlibrary,includingacompletelistoftheoperatorprecedencegroupsandassociativitysettings,seeOperatorDeclarations.Formoreinformationaboutprecedencegroupsandtoseethesyntaxfordefiningyourownoperatorsandprecedencegroups,seeOperatorDeclaration.

NOTE

Youdonotspecifyaprecedencewhendefiningaprefixorpostfixoperator.However,ifyouapplybothaprefixandapostfixoperatortothesameoperand,thepostfixoperatorisappliedfirst.

LanguageReference

AbouttheLanguageReference

ThispartofthebookdescribestheformalgrammaroftheSwiftprogramminglanguage.Thegrammardescribedhereisintendedtohelpyouunderstandthelanguageinmoredetail,ratherthantoallowyoutodirectlyimplementaparserorcompiler.

TheSwiftlanguageisrelativelysmall,becausemanycommontypes,functions,andoperatorsthatappearvirtuallyeverywhereinSwiftcodeareactuallydefinedintheSwiftstandardlibrary.Althoughthesetypes,functions,andoperatorsarenotpartoftheSwiftlanguageitself,theyareusedextensivelyinthediscussionsandcodeexamplesinthispartofthebook.

HowtoReadtheGrammar

ThenotationusedtodescribetheformalgrammaroftheSwiftprogramminglanguagefollowsafewconventions:

Anarrow(→)isusedtomarkgrammarproductionsandcanbereadas“canconsistof.”

Syntacticcategoriesareindicatedbyitalictextandappearonbothsidesofagrammarproductionrule.

Literalwordsandpunctuationareindicatedbyboldfaceconstantwidthtextandappearonlyontheright-handsideofagrammarproductionrule.

Alternativegrammarproductionsareseparatedbyverticalbars(|).Whenalternativeproductionsaretoolongtoreadeasily,theyarebrokenintomultiplegrammarproductionrulesonnewlines.

Inafewcases,regularfonttextisusedtodescribetheright-handsideofagrammarproductionrule.

Optionalsyntacticcategoriesandliteralsaremarkedbyatrailingsubscript,opt.

Asanexample,thegrammarofagetter-setterblockisdefinedasfollows:

GRAMMAR OF AGETTER -SETTER BLOCK

getter-setter-block → { getter-clause setter-clause opt } | { setter-clause getter-clause }

Thisdefinitionindicatesthatagetter-setterblockcanconsistofagetterclausefollowedbyanoptionalsetterclause,enclosedinbraces,orasetterclausefollowedbyagetterclause,enclosedinbraces.Thegrammarproductionaboveisequivalenttothefollowingtwoproductions,wherethealternativesarespelledoutexplicitly:

GRAMMAR OF AGETTER -SETTER BLOCK

getter-setter-block → { getter-clause setter-clause opt }getter-setter-block → { setter-clause getter-clause }

LexicalStructure

ThelexicalstructureofSwiftdescribeswhatsequenceofcharactersformvalidtokensofthelanguage.Thesevalidtokensformthelowest-levelbuildingblocksofthelanguageandareusedtodescribetherestofthelanguageinsubsequentchapters.Atokenconsistsofanidentifier,keyword,punctuation,literal,oroperator.

Inmostcases,tokensaregeneratedfromthecharactersofaSwiftsourcefilebyconsideringthelongestpossiblesubstringfromtheinputtext,withintheconstraintsofthegrammarthatarespecifiedbelow.Thisbehaviorisreferredtoaslongestmatchormaximalmunch.

WhitespaceandComments

Whitespacehastwouses:toseparatetokensinthesourcefileandtohelpdeterminewhetheranoperatorisaprefixorpostfix(seeOperators),butisotherwiseignored.Thefollowingcharactersareconsideredwhitespace:space(U+0020),linefeed(U+000A),carriagereturn(U+000D),horizontaltab(U+0009),verticaltab(U+000B),formfeed(U+000C)andnull(U+0000).

Commentsaretreatedaswhitespacebythecompiler.Singlelinecommentsbeginwith//andcontinueuntilalinefeed(U+000A)orcarriagereturn(U+000D).Multilinecommentsbeginwith/*andendwith*/.Nestingmultilinecommentsisallowed,butthecommentmarkersmustbebalanced.

Commentscancontainadditionalformattingandmarkup,asdescribedinMarkupFormattingReference.

GRAMMAR OF WH I TESPACE

whitespace → whitespace-item whitespace optwhitespace-item → line-breakwhitespace-item → commentwhitespace-item → multiline-commentwhitespace-item → U+0000,U+0009,U+000B,U+000C,orU+0020

line-break → U+000Aline-break → U+000Dline-break → U+000DfollowedbyU+000Acomment → // comment-text line-breakmultiline-comment → /* multiline-comment-text */comment-text → comment-text-item comment-text optcomment-text-item → AnyUnicodescalarvalueexceptU+000AorU+000Dmultiline-comment-text → multiline-comment-text-item multiline-comment-text optmultiline-comment-text-item → multiline-commentmultiline-comment-text-item → comment-text-itemmultiline-comment-text-item → AnyUnicodescalarvalueexcept /* or */

Identifiers

IdentifiersbeginwithanuppercaseorlowercaseletterAthroughZ,anunderscore(_),anoncombiningalphanumericUnicodecharacterintheBasicMultilingualPlane,oracharacteroutsidetheBasicMultilingualPlanethatisn’tinaPrivateUseArea.Afterthefirstcharacter,digitsandcombiningUnicodecharactersarealsoallowed.

Touseareservedwordasanidentifier,putabacktick(`)beforeandafterit.Forexample,classisnotavalididentifier,but`class`isvalid.Thebackticksaren’tconsideredpartoftheidentifier;`x`andxhavethesamemeaning.

Insideaclosurewithnoexplicitparameternames,theparametersareimplicitlynamed$0,$1,$2,andsoon.Thesenamesarevalididentifierswithinthescopeoftheclosure.

Thecompilersynthesizesidentifiersthatbeginwithadollarsign($)forpropertiesthathaveapropertywrapperprojection.Yourcodecaninteractwiththeseidentifiers,butyoucan’tdeclareidentifierswiththatprefix.Formoreinformation,seethepropertyWrappersectionoftheAttributeschapter.

GRAMMAR OF AN I DENT I F I ER

identifier → identifier-head identifier-characters optidentifier → ` identifier-head identifier-characters opt `identifier → implicit-parameter-nameidentifier → property-wrapper-projection

identifier-list → identifier | identifier , identifier-listidentifier-head → Upper-orlowercaseletterAthroughZidentifier-head → _identifier-head → U+00A8,U+00AA,U+00AD,U+00AF,U+00B2–U+00B5,orU+00B7–

U+00BAidentifier-head → U+00BC–U+00BE,U+00C0–U+00D6,U+00D8–U+00F6,orU+00F8–

U+00FFidentifier-head → U+0100–U+02FF,U+0370–U+167F,U+1681–U+180D,orU+180F–

U+1DBFidentifier-head → U+1E00–U+1FFFidentifier-head → U+200B–U+200D,U+202A–U+202E,U+203F–U+2040,U+2054,or

U+2060–U+206Fidentifier-head → U+2070–U+20CF,U+2100–U+218F,U+2460–U+24FF,orU+2776–

U+2793identifier-head → U+2C00–U+2DFForU+2E80–U+2FFFidentifier-head → U+3004–U+3007,U+3021–U+302F,U+3031–U+303F,orU+3040–

U+D7FFidentifier-head → U+F900–U+FD3D,U+FD40–U+FDCF,U+FDF0–U+FE1F,orU+FE30–

U+FE44identifier-head → U+FE47–U+FFFDidentifier-head → U+10000–U+1FFFD,U+20000–U+2FFFD,U+30000–U+3FFFD,or

U+40000–U+4FFFDidentifier-head → U+50000–U+5FFFD,U+60000–U+6FFFD,U+70000–U+7FFFD,or

U+80000–U+8FFFDidentifier-head → U+90000–U+9FFFD,U+A0000–U+AFFFD,U+B0000–U+BFFFD,or

U+C0000–U+CFFFDidentifier-head → U+D0000–U+DFFFDorU+E0000–U+EFFFDidentifier-character → Digit0through9identifier-character → U+0300–U+036F,U+1DC0–U+1DFF,U+20D0–U+20FF,or

U+FE20–U+FE2Fidentifier-character → identifier-headidentifier-characters → identifier-character identifier-characters optimplicit-parameter-name → $ decimal-digitsproperty-wrapper-projection → $ identifier-characters

KeywordsandPunctuation

Thefollowingkeywordsarereservedandcan’tbeusedasidentifiers,unlessthey’reescapedwithbackticks,asdescribedaboveinIdentifiers.Keywordsotherthaninout,var,andletcanbeusedasparameternamesinafunctiondeclarationorfunctioncallwithoutbeingescapedwithbackticks.Whenamemberhasthesamenameasakeyword,referencestothatmemberdon’tneedtobeescapedwithbackticks,exceptwhenthere’sambiguitybetweenreferring

tothememberandusingthekeyword—forexample,self,Type,andProtocolhavespecialmeaninginanexplicitmemberexpression,sotheymustbeescapedwithbackticksinthatcontext.

Keywordsusedindeclarations:associatedtype,class,deinit,enum,extension,fileprivate,func,import,init,inout,internal,let,open,operator,private,protocol,public,rethrows,static,struct,subscript,typealias,andvar.

Keywordsusedinstatements:break,case,continue,default,defer,do,else,fallthrough,for,guard,if,in,repeat,return,switch,where,andwhile.

Keywordsusedinexpressionsandtypes:as,Any,catch,false,is,nil,super,self,Self,throw,throws,true,andtry.

Keywordsusedinpatterns:_.

Keywordsthatbeginwithanumbersign(#):#available,#colorLiteral,#column,#else,#elseif,#endif,#error,#file,#fileLiteral,#function,#if,#imageLiteral,#line,#selector,#sourceLocation,and#warning.

Keywordsreservedinparticularcontexts:associativity,convenience,dynamic,didSet,final,get,infix,indirect,lazy,left,mutating,none,nonmutating,optional,override,postfix,precedence,prefix,Protocol,required,right,set,Type,unowned,weak,andwillSet.Outsidethecontextinwhichtheyappearinthegrammar,theycanbeusedasidentifiers.

Thefollowingtokensarereservedaspunctuationandcan’tbeusedascustomoperators:(,),{,},[,],.,,,:,;,=,@,#,&(asaprefixoperator),->,`,?,and!(asapostfixoperator).

Literals

Aliteralisthesourcecoderepresentationofavalueofatype,suchasanumberorstring.

Thefollowingareexamplesofliterals:

1 42//Integerliteral

2 3.14159//Floating-pointliteral

3 "Hello,world!"//Stringliteral

4 true//Booleanliteral

Aliteraldoesn’thaveatypeonitsown.Instead,aliteralisparsedashavinginfiniteprecisionandSwift’stypeinferenceattemptstoinferatypefortheliteral.Forexample,inthedeclarationletx:Int8=42,Swiftusestheexplicittypeannotation(:Int8)toinferthatthetypeoftheintegerliteral42isInt8.Ifthereisn’tsuitabletypeinformationavailable,Swiftinfersthattheliteral’stypeisoneofthedefaultliteraltypesdefinedintheSwiftstandardlibrary.ThedefaulttypesareIntforintegerliterals,Doubleforfloating-pointliterals,Stringforstringliterals,andBoolforBooleanliterals.Forexample,inthedeclarationletstr="Hello,world",thedefaultinferredtypeofthestringliteral"Hello,world"isString.

Whenspecifyingthetypeannotationforaliteralvalue,theannotation’stypemustbeatypethatcanbeinstantiatedfromthatliteralvalue.Thatis,thetypemustconformtooneofthefollowingSwiftstandardlibraryprotocols:ExpressibleByIntegerLiteralforintegerliterals,ExpressibleByFloatLiteralforfloating-pointliterals,ExpressibleByStringLiteralforstringliterals,ExpressibleByBooleanLiteralforBooleanliterals,ExpressibleByUnicodeScalarLiteralforstringliteralsthatcontainonlyasingleUnicodescalar,andExpressibleByExtendedGraphemeClusterLiteralforstringliteralsthatcontainonlyasingleextendedgraphemecluster.Forexample,Int8conformstotheExpressibleByIntegerLiteralprotocol,andthereforeitcanbeusedinthetypeannotationfortheintegerliteral42inthedeclarationletx:Int8=42.

GRAMMAR OF A L I TERAL

literal → numeric-literal | string-literal | boolean-literal | nil-literalnumeric-literal → -opt integer-literal | -opt floating-point-literalboolean-literal → true | falsenil-literal → nil

IntegerLiteralsIntegerliteralsrepresentintegervaluesofunspecifiedprecision.Bydefault,integerliteralsareexpressedindecimal;youcanspecifyanalternatebaseusingaprefix.Binaryliteralsbeginwith0b,octalliteralsbeginwith0o,andhexadecimalliteralsbeginwith0x.

Decimalliteralscontainthedigits0through9.Binaryliteralscontain0and1,octalliteralscontain0through7,andhexadecimalliteralscontain0through9aswellasAthroughFinupper-orlowercase.

Negativeintegersliteralsareexpressedbyprependingaminussign(-)toanintegerliteral,asin-42.

Underscores(_)areallowedbetweendigitsforreadability,butthey’reignoredandthereforedon’taffectthevalueoftheliteral.Integerliteralscanbeginwithleadingzeros(0),butthey’relikewiseignoredanddon’taffectthebaseorvalueoftheliteral.

Unlessotherwisespecified,thedefaultinferredtypeofanintegerliteralistheSwiftstandardlibrarytypeInt.TheSwiftstandardlibraryalsodefinestypesforvarioussizesofsignedandunsignedintegers,asdescribedinIntegers.

GRAMMAR OF AN I NTEGER L I TERAL

integer-literal → binary-literalinteger-literal → octal-literalinteger-literal → decimal-literalinteger-literal → hexadecimal-literalbinary-literal → 0b binary-digit binary-literal-characters optbinary-digit → Digit0or1binary-literal-character → binary-digit | _binary-literal-characters → binary-literal-character binary-literal-characters optoctal-literal → 0o octal-digit octal-literal-characters optoctal-digit → Digit0through7octal-literal-character → octal-digit | _octal-literal-characters → octal-literal-character octal-literal-characters optdecimal-literal → decimal-digit decimal-literal-characters optdecimal-digit → Digit0through9decimal-digits → decimal-digit decimal-digits optdecimal-literal-character → decimal-digit | _decimal-literal-characters → decimal-literal-character decimal-literal-characters opthexadecimal-literal → 0x hexadecimal-digit hexadecimal-literal-characters opt

hexadecimal-digit → Digit0through9,athroughf,orAthroughFhexadecimal-literal-character → hexadecimal-digit | _hexadecimal-literal-characters → hexadecimal-literal-character hexadecimal-literal-

characters opt

Floating-PointLiteralsFloating-pointliteralsrepresentfloating-pointvaluesofunspecifiedprecision.

Bydefault,floating-pointliteralsareexpressedindecimal(withnoprefix),buttheycanalsobeexpressedinhexadecimal(witha0xprefix).

Decimalfloating-pointliteralsconsistofasequenceofdecimaldigitsfollowedbyeitheradecimalfraction,adecimalexponent,orboth.Thedecimalfractionconsistsofadecimalpoint(.)followedbyasequenceofdecimaldigits.Theexponentconsistsofanupper-orlowercaseeprefixfollowedbyasequenceofdecimaldigitsthatindicateswhatpowerof10thevalueprecedingtheeismultipliedby.Forexample,1.25e2represents1.25x102,whichevaluatesto125.0.Similarly,1.25e-2represents1.25x10-2,whichevaluatesto0.0125.

Hexadecimalfloating-pointliteralsconsistofa0xprefix,followedbyanoptionalhexadecimalfraction,followedbyahexadecimalexponent.Thehexadecimalfractionconsistsofadecimalpointfollowedbyasequenceofhexadecimaldigits.Theexponentconsistsofanupper-orlowercasepprefixfollowedbyasequenceofdecimaldigitsthatindicateswhatpowerof2thevalueprecedingthepismultipliedby.Forexample,0xFp2represents15x22,whichevaluatesto60.Similarly,0xFp-2represents15x2-2,whichevaluatesto3.75.

Negativefloating-pointliteralsareexpressedbyprependingaminussign(-)toafloating-pointliteral,asin-42.5.

Underscores(_)areallowedbetweendigitsforreadability,butthey’reignoredandthereforedon’taffectthevalueoftheliteral.Floating-pointliteralscanbeginwithleadingzeros(0),butthey’relikewiseignoredanddon’taffectthebaseorvalueoftheliteral.

Unlessotherwisespecified,thedefaultinferredtypeofafloating-pointliteralistheSwiftstandardlibrarytypeDouble,whichrepresentsa64-bitfloating-pointnumber.TheSwiftstandardlibraryalsodefinesaFloattype,whichrepresentsa32-bitfloating-pointnumber.

GRAMMAR OF A F LOAT ING -PO INT L I TERAL

floating-point-literal → decimal-literal decimal-fraction opt decimal-exponent optfloating-point-literal → hexadecimal-literal hexadecimal-fraction opt hexadecimal-

exponentdecimal-fraction → . decimal-literaldecimal-exponent → floating-point-e sign opt decimal-literalhexadecimal-fraction → . hexadecimal-digit hexadecimal-literal-characters opthexadecimal-exponent → floating-point-p sign opt decimal-literalfloating-point-e → e | Efloating-point-p → p | Psign → + | -

StringLiteralsAstringliteralisasequenceofcharacterssurroundedbyquotationmarks.Asingle-linestringliteralissurroundedbydoublequotationmarksandhasthefollowingform:

" characters "

Stringliteralscan’tcontainanunescapeddoublequotationmark("),anunescapedbackslash(\),acarriagereturn,oralinefeed.

Amultilinestringliteralissurroundedbythreedoublequotationmarksandhasthefollowingform:

"""

characters

"""

Unlikeasingle-linestringliteral,amultilinestringliteralcancontainunescapeddoublequotationmarks("),carriagereturns,andlinefeeds.Itcan’tcontainthree

unescapeddoublequotationmarksnexttoeachother.

Thelinebreakafterthe"""thatbeginsthemultilinestringliteralisnotpartofthestring.Thelinebreakbeforethe"""thatendstheliteralisalsonotpartofthestring.Tomakeamultilinestringliteralthatbeginsorendswithalinefeed,writeablanklineasitsfirstorlastline.

Amultilinestringliteralcanbeindentedusinganycombinationofspacesandtabs;thisindentationisnotincludedinthestring.The"""thatendstheliteraldeterminestheindentation:Everynonblanklineintheliteralmustbeginwithexactlythesameindentationthatappearsbeforetheclosing""";there’snoconversionbetweentabsandspaces.Youcanincludeadditionalspacesandtabsafterthatindentation;thosespacesandtabsappearinthestring.

Linebreaksinamultilinestringliteralarenormalizedtousethelinefeedcharacter.Evenifyoursourcefilehasamixofcarriagereturnsandlinefeeds,allofthelinebreaksinthestringwillbethesame.

Inamultilinestringliteral,writingabackslash(\)attheendofalineomitsthatlinebreakfromthestring.Anywhitespacebetweenthebackslashandthelinebreakisalsoomitted.Youcanusethissyntaxtohardwrapamultilinestringliteralinyoursourcecode,withoutchangingthevalueoftheresultingstring.

Specialcharacterscanbeincludedinstringliteralsofboththesingle-lineandmultilineformsusingthefollowingescapesequences:

Nullcharacter(\0)

Backslash(\\)

Horizontaltab(\t)

Linefeed(\n)

Carriagereturn(\r)

Doublequotationmark(\")

Singlequotationmark(\')

Unicodescalar(\u{n}),wherenisahexadecimalnumberthathasonetoeightdigits

Thevalueofanexpressioncanbeinsertedintoastringliteralbyplacingtheexpressioninparenthesesafterabackslash(\).Theinterpolatedexpressioncancontainastringliteral,butcan’tcontainanunescapedbackslash,acarriagereturn,oralinefeed.

Forexample,allofthefollowingstringliteralshavethesamevalue:

1 "123"

2 "12\("3")"

3 "12\(3)"

4 "12\(1+2)"

5 letx=3;"12\(x)"

Astringdelimitedbyextendeddelimitersisasequenceofcharacterssurroundedbyquotationmarksandabalancedsetofoneormorenumbersigns(#).Astringdelimitedbyextendeddelimitershasthefollowingforms:

#" characters "#

#"""

characters

"""#

Specialcharactersinastringdelimitedbyextendeddelimitersappearintheresultingstringasnormalcharactersratherthanasspecialcharacters.Youcanuseextendeddelimiterstocreatestringswithcharactersthatwouldordinarilyhaveaspecialeffectsuchasgeneratingastringinterpolation,startinganescapesequence,orterminatingthestring.

Thefollowingexampleshowsastringliteralandastringdelimitedbyextendeddelimitersthatcreateequivalentstringvalues:

1 letstring=#"\(x)\"\u{2603}"#

2 letescaped="\\(x)\\\"\\u{2603}"

3 print(string)

4 //Prints"\(x)\"\u{2603}"

5 print(string==escaped)

6 //Prints"true"

Ifyouusemorethanonenumbersigntoformastringdelimitedbyextendeddelimiters,don’tplacewhitespaceinbetweenthenumbersigns:

1 print(###"Line1\###nLine2"###)//OK

2 print(###"Line1\###nLine2"###)//Error

Multilinestringliteralsthatyoucreateusingextendeddelimitershavethesameindentationrequirementsasregularmultilinestringliterals.

ThedefaultinferredtypeofastringliteralisString.FormoreinformationabouttheStringtype,seeStringsandCharactersandString.

Stringliteralsthatareconcatenatedbythe+operatorareconcatenatedatcompiletime.Forexample,thevaluesoftextAandtextBintheexamplebelowareidentical—noruntimeconcatenationisperformed.

1 lettextA="Hello"+"world"

2 lettextB="Helloworld"

GRAMMAR OF A STR ING L I TERAL

string-literal → static-string-literal | interpolated-string-literalstring-literal-opening-delimiter → extended-string-literal-delimiter opt "string-literal-closing-delimiter → " extended-string-literal-delimiter optstatic-string-literal → string-literal-opening-delimiter quoted-text opt string-literal-closing-

delimiterstatic-string-literal → multiline-string-literal-opening-delimiter multiline-quoted-text opt

multiline-string-literal-closing-delimitermultiline-string-literal-opening-delimiter → extended-string-literal-delimiter """multiline-string-literal-closing-delimiter → """ extended-string-literal-delimiter

extended-string-literal-delimiter → # extended-string-literal-delimiter optquoted-text → quoted-text-item quoted-text optquoted-text-item → escaped-characterquoted-text-item → AnyUnicodescalarvalueexcept " , \ ,U+000A,orU+000Dmultiline-quoted-text → multiline-quoted-text-item multiline-quoted-text optmultiline-quoted-text-item → escaped-charactermultiline-quoted-text-item → AnyUnicodescalarvalueexcept \multiline-quoted-text-item → escaped-newlineinterpolated-string-literal → string-literal-opening-delimiter interpolated-text opt string-

literal-closing-delimiterinterpolated-string-literal → multiline-string-literal-opening-delimiter interpolated-text opt

multiline-string-literal-closing-delimiterinterpolated-text → interpolated-text-item interpolated-text optinterpolated-text-item → \( expression ) | quoted-text-itemmultiline-interpolated-text → multiline-interpolated-text-item multiline-interpolated-text optmultiline-interpolated-text-item → \( expression ) |multiline-quoted-text-itemescape-sequence → \ extended-string-literal-delimiterescaped-character → escape-sequence 0 | escape-sequence \ | escape-sequence t |

escape-sequence n | escape-sequence r | escape-sequence " | escape-sequence'

escaped-character → escape-sequence u { unicode-scalar-digits }unicode-scalar-digits → Betweenoneandeighthexadecimaldigitsescaped-newline → escape-sequence whitespace opt line-break

Operators

TheSwiftstandardlibrarydefinesanumberofoperatorsforyouruse,manyofwhicharediscussedinBasicOperatorsandAdvancedOperators.Thepresentsectiondescribeswhichcharacterscanbeusedtodefinecustomoperators.

CustomoperatorscanbeginwithoneoftheASCIIcharacters/,=,-,+,!,*,%,<,>,&,|,^,?,or~,oroneoftheUnicodecharactersdefinedinthegrammarbelow(whichincludecharactersfromtheMathematicalOperators,MiscellaneousSymbols,andDingbatsUnicodeblocks,amongothers).Afterthefirstcharacter,combiningUnicodecharactersarealsoallowed.

Youcanalsodefinecustomoperatorsthatbeginwithadot(.).Theseoperatorscancontainadditionaldots.Forexample,.+.istreatedasasingleoperator.Ifanoperatordoesn’tbeginwithadot,itcan’tcontainadotelsewhere.Forexample,

+.+istreatedasthe+operatorfollowedbythe.+operator.

Althoughyoucandefinecustomoperatorsthatcontainaquestionmark(?),theycan’tconsistofasinglequestionmarkcharacteronly.Additionally,althoughoperatorscancontainanexclamationmark(!),postfixoperatorscan’tbeginwitheitheraquestionmarkoranexclamationmark.

NOTE

Thetokens=,->,//,/*,*/,.,theprefixoperators<,&,and?,theinfixoperator?,andthepostfixoperators>,!,and?arereserved.Thesetokenscan’tbeoverloaded,norcantheybeusedascustomoperators.

Thewhitespacearoundanoperatorisusedtodeterminewhetheranoperatorisusedasaprefixoperator,apostfixoperator,orabinaryoperator.Thisbehaviorissummarizedinthefollowingrules:

Ifanoperatorhaswhitespacearoundbothsidesoraroundneitherside,it’streatedasabinaryoperator.Asanexample,the+++operatorina+++banda+++bistreatedasabinaryoperator.

Ifanoperatorhaswhitespaceontheleftsideonly,it’streatedasaprefixunaryoperator.Asanexample,the+++operatorina+++bistreatedasaprefixunaryoperator.

Ifanoperatorhaswhitespaceontherightsideonly,it’streatedasapostfixunaryoperator.Asanexample,the+++operatorina+++bistreatedasapostfixunaryoperator.

Ifanoperatorhasnowhitespaceontheleftbutisfollowedimmediatelybyadot(.),it’streatedasapostfixunaryoperator.Asanexample,the+++operatorina+++.bistreatedasapostfixunaryoperator(a+++.bratherthana+++.b).

Forthepurposesoftheserules,thecharacters(,[,and{beforeanoperator,thecharacters),],and}afteranoperator,andthecharacters,,;,and:arealsoconsideredwhitespace.

There’sonecaveattotherulesabove.Ifthe!or?predefinedoperatorhasno

whitespaceontheleft,it’streatedasapostfixoperator,regardlessofwhetherithaswhitespaceontheright.Tousethe?astheoptional-chainingoperator,itmustnothavewhitespaceontheleft.Touseitintheternaryconditional(?:)operator,itmusthavewhitespacearoundbothsides.

Incertainconstructs,operatorswithaleading<or>maybesplitintotwoormoretokens.Theremainderistreatedthesamewayandmaybesplitagain.Asaresult,there’snoneedtousewhitespacetodisambiguatebetweentheclosing>charactersinconstructslikeDictionary<String,Array<Int>>.Inthisexample,theclosing>charactersarenottreatedasasingletokenthatmaythenbemisinterpretedasabitshift>>operator.

Tolearnhowtodefinenew,customoperators,seeCustomOperatorsandOperatorDeclaration.Tolearnhowtooverloadexistingoperators,seeOperatorMethods.

GRAMMAR OF OPERATORS

operator → operator-head operator-characters optoperator → dot-operator-head dot-operator-charactersoperator-head → / | = | - | + | ! | * | % | < | > | & | | | ^ | ~ | ?operator-head → U+00A1–U+00A7operator-head → U+00A9orU+00ABoperator-head → U+00ACorU+00AEoperator-head → U+00B0–U+00B1operator-head → U+00B6,U+00BB,U+00BF,U+00D7,orU+00F7operator-head → U+2016–U+2017operator-head → U+2020–U+2027operator-head → U+2030–U+203Eoperator-head → U+2041–U+2053operator-head → U+2055–U+205Eoperator-head → U+2190–U+23FFoperator-head → U+2500–U+2775operator-head → U+2794–U+2BFFoperator-head → U+2E00–U+2E7Foperator-head → U+3001–U+3003operator-head → U+3008–U+3020operator-head → U+3030operator-character → operator-headoperator-character → U+0300–U+036Foperator-character → U+1DC0–U+1DFFoperator-character → U+20D0–U+20FFoperator-character → U+FE00–U+FE0Foperator-character → U+FE20–U+FE2Foperator-character → U+E0100–U+E01EF

operator-characters → operator-character operator-characters optdot-operator-head → .dot-operator-character → . | operator-characterdot-operator-characters → dot-operator-character dot-operator-characters optbinary-operator → operatorprefix-operator → operatorpostfix-operator → operator

Types

InSwift,therearetwokindsoftypes:namedtypesandcompoundtypes.Anamedtypeisatypethatcanbegivenaparticularnamewhenit’sdefined.Namedtypesincludeclasses,structures,enumerations,andprotocols.Forexample,instancesofauser-definedclassnamedMyClasshavethetypeMyClass.Inadditiontouser-definednamedtypes,theSwiftstandardlibrarydefinesmanycommonlyusednamedtypes,includingthosethatrepresentarrays,dictionaries,andoptionalvalues.

Datatypesthatarenormallyconsideredbasicorprimitiveinotherlanguages—suchastypesthatrepresentnumbers,characters,andstrings—areactuallynamedtypes,definedandimplementedintheSwiftstandardlibraryusingstructures.Becausethey’renamedtypes,youcanextendtheirbehaviortosuittheneedsofyourprogram,usinganextensiondeclaration,discussedinExtensionsandExtensionDeclaration.

Acompoundtypeisatypewithoutaname,definedintheSwiftlanguageitself.Therearetwocompoundtypes:functiontypesandtupletypes.Acompoundtypemaycontainnamedtypesandothercompoundtypes.Forexample,thetupletype(Int,(Int,Int))containstwoelements:ThefirstisthenamedtypeInt,andthesecondisanothercompoundtype(Int,Int).

Youcanputparenthesesaroundanamedtypeoracompoundtype.However,addingparenthesesaroundatypedoesn’thaveanyeffect.Forexample,(Int)isequivalenttoInt.

ThischapterdiscussesthetypesdefinedintheSwiftlanguageitselfanddescribesthetypeinferencebehaviorofSwift.

GRAMMAR OF ATYPE

type → function-typetype → array-typetype → dictionary-typetype → type-identifiertype → tuple-typetype → optional-typetype → implicitly-unwrapped-optional-type

type → protocol-composition-typetype → opaque-typetype → metatype-typetype → self-typetype → Anytype → ( type )

TypeAnnotation

Atypeannotationexplicitlyspecifiesthetypeofavariableorexpression.Typeannotationsbeginwithacolon(:)andendwithatype,asthefollowingexamplesshow:

1 letsomeTuple:(Double,Double)=(3.14159,2.71828)

2 funcsomeFunction(a:Int){/*...*/}

Inthefirstexample,theexpressionsomeTupleisspecifiedtohavethetupletype(Double,Double).Inthesecondexample,theparameteratothefunctionsomeFunctionisspecifiedtohavethetypeInt.

Typeannotationscancontainanoptionallistoftypeattributesbeforethetype.

GRAMMAR OF ATYPE ANNOTAT ION

type-annotation → : attributes opt inoutopt type

TypeIdentifier

Atypeidentifierreferstoeitheranamedtypeoratypealiasofanamedorcompoundtype.

Mostofthetime,atypeidentifierdirectlyreferstoanamedtypewiththesamenameastheidentifier.Forexample,IntisatypeidentifierthatdirectlyreferstothenamedtypeInt,andthetypeidentifierDictionary<String,Int>directly

referstothenamedtypeDictionary<String,Int>.

Therearetwocasesinwhichatypeidentifierdoesn’trefertoatypewiththesamename.Inthefirstcase,atypeidentifierreferstoatypealiasofanamedorcompoundtype.Forinstance,intheexamplebelow,theuseofPointinthetypeannotationreferstothetupletype(Int,Int).

1 typealiasPoint=(Int,Int)

2 letorigin:Point=(0,0)

Inthesecondcase,atypeidentifierusesdot(.)syntaxtorefertonamedtypesdeclaredinothermodulesornestedwithinothertypes.Forexample,thetypeidentifierinthefollowingcodereferencesthenamedtypeMyTypethatisdeclaredintheExampleModulemodule.

varsomeValue:ExampleModule.MyType

GRAMMAR OF ATYPE I DENT I F I ER

type-identifier → type-name generic-argument-clause opt | type-name generic-argument-clause opt . type-identifier

type-name → identifier

TupleType

Atupletypeisacomma-separatedlistoftypes,enclosedinparentheses.

Youcanuseatupletypeasthereturntypeofafunctiontoenablethefunctiontoreturnasingletuplecontainingmultiplevalues.Youcanalsonametheelementsofatupletypeandusethosenamestorefertothevaluesoftheindividualelements.Anelementnameconsistsofanidentifierfollowedimmediatelybyacolon(:).Foranexamplethatdemonstratesbothofthesefeatures,seeFunctionswithMultipleReturnValues.

Whenanelementofatupletypehasaname,thatnameispartofthetype.

1 varsomeTuple=(top:10,bottom:12)//someTupleisoftype

(top:Int,bottom:Int)

2 someTuple=(top:4,bottom:42)//OK:namesmatch

3 someTuple=(9,99)//OK:namesareinferred

4 someTuple=(left:5,right:5)//Error:namesdon'tmatch

Alltupletypescontaintwoormoretypes,exceptforVoidwhichisatypealiasfortheemptytupletype,().

GRAMMAR OF ATUPLE TYPE

tuple-type → ( ) | ( tuple-type-element , tuple-type-element-list )tuple-type-element-list → tuple-type-element | tuple-type-element , tuple-type-element-

listtuple-type-element → element-name type-annotation | typeelement-name → identifier

FunctionType

Afunctiontyperepresentsthetypeofafunction,method,orclosureandconsistsofaparameterandreturntypeseparatedbyanarrow(->):

( parametertype )-> returntype

Theparametertypeiscomma-separatedlistoftypes.Becausethereturntypecanbeatupletype,functiontypessupportfunctionsandmethodsthatreturnmultiplevalues.

Aparameterofthefunctiontype()->T(whereTisanytype)canapplytheautoclosureattributetoimplicitlycreateaclosureatitscallsites.Thisprovidesasyntacticallyconvenientwaytodefertheevaluationofanexpressionwithoutneedingtowriteanexplicitclosurewhenyoucallthefunction.Foranexampleofanautoclosurefunctiontypeparameter,seeAutoclosures.

Afunctiontypecanhaveavariadicparameterinitsparametertype.Syntactically,avariadicparameterconsistsofabasetypenamefollowed

immediatelybythreedots(...),asinInt....Avariadicparameteristreatedasanarraythatcontainselementsofthebasetypename.Forinstance,thevariadicparameterInt...istreatedas[Int].Foranexamplethatusesavariadicparameter,seeVariadicParameters.

Tospecifyanin-outparameter,prefixtheparametertypewiththeinoutkeyword.Youcan’tmarkavariadicparameterorareturntypewiththeinoutkeyword.In-outparametersarediscussedinIn-OutParameters.

Ifafunctiontypehasonlyoneparameterandthatparameter’stypeisatupletype,thenthetupletypemustbeparenthesizedwhenwritingthefunction’stype.Forexample,((Int,Int))->Voidisthetypeofafunctionthattakesasingleparameterofthetupletype(Int,Int)anddoesn’treturnanyvalue.Incontrast,withoutparentheses,(Int,Int)->VoidisthetypeofafunctionthattakestwoIntparametersanddoesn’treturnanyvalue.Likewise,becauseVoidisatypealiasfor(),thefunctiontype(Void)->Voidisthesameas(())->()—afunctionthattakesasingleargumentthatisanemptytuple.Thesetypesarenotthesameas()->()—afunctionthattakesnoarguments.

Argumentnamesinfunctionsandmethodsarenotpartofthecorrespondingfunctiontype.Forexample:

1 funcsomeFunction(left:Int,right:Int){}

2 funcanotherFunction(left:Int,right:Int){}

3 funcfunctionWithDifferentLabels(top:Int,bottom:Int){}

4

5 varf=someFunction//Thetypeoffis(Int,Int)->Void,

not(left:Int,right:Int)->Void.

6 f=anotherFunction//OK

7 f=functionWithDifferentLabels//OK

8

9 funcfunctionWithDifferentArgumentTypes(left:Int,right:

String){}

10 f=functionWithDifferentArgumentTypes//Error

11

12 funcfunctionWithDifferentNumberOfArguments(left:Int,right:

Int,top:Int){}

13 f=functionWithDifferentNumberOfArguments//Error

Becauseargumentlabelsarenotpartofafunction’stype,youomitthemwhenwritingafunctiontype.

1 varoperation:(lhs:Int,rhs:Int)->Int//Error

2 varoperation:(_lhs:Int,_rhs:Int)->Int//OK

3 varoperation:(Int,Int)->Int//OK

Ifafunctiontypeincludesmorethanasinglearrow(->),thefunctiontypesaregroupedfromrighttoleft.Forexample,thefunctiontype(Int)->(Int)->Intisunderstoodas(Int)->((Int)->Int)—thatis,afunctionthattakesanIntandreturnsanotherfunctionthattakesandreturnsanInt.

Functiontypesthatcanthroworrethrowanerrormustbemarkedwiththethrowskeyword.Thethrowskeywordispartofafunction’stype,andnonthrowingfunctionsaresubtypesofthrowingfunctions.Asaresult,youcanuseanonthrowingfunctioninthesameplacesasathrowingone.ThrowingandrethrowingfunctionsaredescribedinThrowingFunctionsandMethodsandRethrowingFunctionsandMethods.

RestrictionsforNonescapingClosuresAparameterthat’sanonescapingfunctioncan’tbestoredinaproperty,variable,orconstantoftypeAny,becausethatmightallowthevaluetoescape.

Aparameterthat’sanonescapingfunctioncan’tbepassedasanargumenttoanothernonescapingfunctionparameter.ThisrestrictionhelpsSwiftperformmoreofitschecksforconflictingaccesstomemoryatcompiletimeinsteadofatruntime.Forexample:

1 letexternal:(()->Void)->Void={_in()}

2 functakesTwoFunctions(first:(()->Void)->Void,second:(()

->Void)->Void){

3 first{first{}}//Error

4 second{second{}}//Error

5

6 first{second{}}//Error

7 second{first{}}//Error

8

9 first{external{}}//OK

10 external{first{}}//OK

11 }

Inthecodeabove,bothoftheparameterstotakesTwoFunctions(first:second:)arefunctions.Neitherparameterismarked@escaping,sothey’rebothnonescapingasaresult.

Thefourfunctioncallsmarked“Error”intheexampleabovecausecompilererrors.Becausethefirstandsecondparametersarenonescapingfunctions,theycan’tbepassedasargumentstoanothernonescapingfunctionparameter.Incontrast,thetwofunctioncallsmarked“OK”don’tcauseacompilererror.Thesefunctioncallsdon’tviolatetherestrictionbecauseexternalisn’toneoftheparametersoftakesTwoFunctions(first:second:).

Ifyouneedtoavoidthisrestriction,markoneoftheparametersasescaping,ortemporarilyconvertoneofthenonescapingfunctionparameterstoanescapingfunctionbyusingthewithoutActuallyEscaping(_:do:)function.Forinformationaboutavoidingconflictingaccesstomemory,seeMemorySafety.

GRAMMAR OF A FUNCT ION TYPE

function-type → attributes opt function-type-argument-clause throwsopt -> typefunction-type-argument-clause → ( )function-type-argument-clause → ( function-type-argument-list ...opt )function-type-argument-list → function-type-argument | function-type-argument ,

function-type-argument-list

function-type-argument → attributes opt inoutopt type | argument-label type-annotation

argument-label → identifier

ArrayType

TheSwiftlanguageprovidesthefollowingsyntacticsugarfortheSwiftstandardlibraryArray<Element>type:

[ type ]

Inotherwords,thefollowingtwodeclarationsareequivalent:

1 letsomeArray:Array<String>=["Alex","Brian","Dave"]

2 letsomeArray:[String]=["Alex","Brian","Dave"]

Inbothcases,theconstantsomeArrayisdeclaredasanarrayofstrings.Theelementsofanarraycanbeaccessedthroughsubscriptingbyspecifyingavalidindexvalueinsquarebrackets:someArray[0]referstotheelementatindex0,"Alex".

Youcancreatemultidimensionalarraysbynestingpairsofsquarebrackets,wherethenameofthebasetypeoftheelementsiscontainedintheinnermostpairofsquarebrackets.Forexample,youcancreateathree-dimensionalarrayofintegersusingthreesetsofsquarebrackets:

vararray3D:[[[Int]]]=[[[1,2],[3,4]],[[5,6],[7,8]]]

Whenaccessingtheelementsinamultidimensionalarray,theleft-mostsubscriptindexreferstotheelementatthatindexintheoutermostarray.Thenextsubscriptindextotherightreferstotheelementatthatindexinthearraythat’snestedonelevelin.Andsoon.Thismeansthatintheexampleabove,array3D[0]refersto[[1,2],[3,4]],array3D[0][1]refersto[3,4],andarray3D[0][1][1]referstothevalue4.

ForadetaileddiscussionoftheSwiftstandardlibraryArraytype,seeArrays.

GRAMMAR OF AN ARRAY TYPE

array-type → [ type ]

DictionaryType

TheSwiftlanguageprovidesthefollowingsyntacticsugarfortheSwiftstandardlibraryDictionary<Key,Value>type:

[ keytype : valuetype ]

Inotherwords,thefollowingtwodeclarationsareequivalent:

1 letsomeDictionary:[String:Int]=["Alex":31,"Paul":39]

2 letsomeDictionary:Dictionary<String,Int>=["Alex":31,

"Paul":39]

Inbothcases,theconstantsomeDictionaryisdeclaredasadictionarywithstringsaskeysandintegersasvalues.

Thevaluesofadictionarycanbeaccessedthroughsubscriptingbyspecifyingthecorrespondingkeyinsquarebrackets:someDictionary["Alex"]referstothevalueassociatedwiththekey"Alex".Thesubscriptreturnsanoptionalvalueofthedictionary’svaluetype.Ifthespecifiedkeyisn’tcontainedinthedictionary,thesubscriptreturnsnil.

ThekeytypeofadictionarymustconformtotheSwiftstandardlibraryHashableprotocol.

ForadetaileddiscussionoftheSwiftstandardlibraryDictionarytype,seeDictionaries.

GRAMMAR OF AD ICT IONARY TYPE

dictionary-type → [ type : type ]

OptionalType

TheSwiftlanguagedefinesthepostfix?assyntacticsugarforthenamedtypeOptional<Wrapped>,whichisdefinedintheSwiftstandardlibrary.Inotherwords,thefollowingtwodeclarationsareequivalent:

1 varoptionalInteger:Int?

2 varoptionalInteger:Optional<Int>

Inbothcases,thevariableoptionalIntegerisdeclaredtohavethetypeofanoptionalinteger.Notethatnowhitespacemayappearbetweenthetypeandthe?.

ThetypeOptional<Wrapped>isanenumerationwithtwocases,noneandsome(Wrapped),whichareusedtorepresentvaluesthatmayormaynotbepresent.Anytypecanbeexplicitlydeclaredtobe(orimplicitlyconvertedto)anoptionaltype.Ifyoudon’tprovideaninitialvaluewhenyoudeclareanoptionalvariableorproperty,itsvalueautomaticallydefaultstonil.

Ifaninstanceofanoptionaltypecontainsavalue,youcanaccessthatvalueusingthepostfixoperator!,asshownbelow:

1 optionalInteger=42

2 optionalInteger!//42

Usingthe!operatortounwrapanoptionalthathasavalueofnilresultsinaruntimeerror.

Youcanalsouseoptionalchainingandoptionalbindingtoconditionallyperformanoperationonanoptionalexpression.Ifthevalueisnil,nooperationisperformedandthereforenoruntimeerrorisproduced.

Formoreinformationandtoseeexamplesthatshowhowtouseoptionaltypes,

seeOptionals.

GRAMMAR OF AN OPT IONALTYPE

optional-type → type ?

ImplicitlyUnwrappedOptionalType

TheSwiftlanguagedefinesthepostfix!assyntacticsugarforthenamedtypeOptional<Wrapped>,whichisdefinedintheSwiftstandardlibrary,withtheadditionalbehaviorthatit’sautomaticallyunwrappedwhenit’saccessed.Ifyoutrytouseanimplicitlyunwrappedoptionalthathasavalueofnil,you’llgetaruntimeerror.Withtheexceptionoftheimplicitunwrappingbehavior,thefollowingtwodeclarationsareequivalent:

1 varimplicitlyUnwrappedString:String!

2 varexplicitlyUnwrappedString:Optional<String>

Notethatnowhitespacemayappearbetweenthetypeandthe!.

Becauseimplicitunwrappingchangesthemeaningofthedeclarationthatcontainsthattype,optionaltypesthatarenestedinsideatupletypeoragenerictype—suchastheelementtypesofadictionaryorarray—can’tbemarkedasimplicitlyunwrapped.Forexample:

1 lettupleOfImplicitlyUnwrappedElements:(Int!,Int!)//Error

2 letimplicitlyUnwrappedTuple:(Int,Int)!//OK

3

4 letarrayOfImplicitlyUnwrappedElements:[Int!]//Error

5 letimplicitlyUnwrappedArray:[Int]!//OK

BecauseimplicitlyunwrappedoptionalshavethesameOptional<Wrapped>typeasoptionalvalues,youcanuseimplicitlyunwrappedoptionalsinallthesameplacesinyourcodethatyoucanuseoptionals.Forexample,youcanassign

valuesofimplicitlyunwrappedoptionalstovariables,constants,andpropertiesofoptionals,andviceversa.

Aswithoptionals,ifyoudon’tprovideaninitialvaluewhenyoudeclareanimplicitlyunwrappedoptionalvariableorproperty,itsvalueautomaticallydefaultstonil.

Useoptionalchainingtoconditionallyperformanoperationonanimplicitlyunwrappedoptionalexpression.Ifthevalueisnil,nooperationisperformedandthereforenoruntimeerrorisproduced.

Formoreinformationaboutimplicitlyunwrappedoptionaltypes,seeImplicitlyUnwrappedOptionals.

GRAMMAR OF AN IMPL IC I T LY UNWRAPPED OPT IONALTYPE

implicitly-unwrapped-optional-type → type !

ProtocolCompositionType

Aprotocolcompositiontypedefinesatypethatconformstoeachprotocolinalistofspecifiedprotocols,oratypethatisasubclassofagivenclassandconformstoeachprotocolinalistofspecifiedprotocols.Protocolcompositiontypesmaybeusedonlywhenspecifyingatypeintypeannotations,ingenericparameterclauses,andingenericwhereclauses.

Protocolcompositiontypeshavethefollowingform:

Protocol1 & Protocol2

Aprotocolcompositiontypeallowsyoutospecifyavaluewhosetypeconformstotherequirementsofmultipleprotocolswithoutexplicitlydefininganew,namedprotocolthatinheritsfromeachprotocolyouwantthetypetoconformto.Forexample,youcanusetheprotocolcompositiontypeProtocolA&ProtocolB&ProtocolCinsteadofdeclaringanewprotocolthatinheritsfromProtocolA,ProtocolB,andProtocolC.Likewise,youcanuseSuperClass&ProtocolA

insteadofdeclaringanewprotocolthatisasubclassofSuperClassandconformstoProtocolA.

Eachiteminaprotocolcompositionlistisoneofthefollowing;thelistcancontainatmostoneclass:

Thenameofaclass

Thenameofaprotocol

Atypealiaswhoseunderlyingtypeisaprotocolcompositiontype,aprotocol,oraclass.

Whenaprotocolcompositiontypecontainstypealiases,it’spossibleforthesameprotocoltoappearmorethanonceinthedefinitions—duplicatesareignored.Forexample,thedefinitionofPQRinthecodebelowisequivalenttoP&Q&R.

1 typealiasPQ=P&Q

2 typealiasPQR=PQ&Q&R

GRAMMAR OF A PROTOCOLCOMPOS I T ION TYPE

protocol-composition-type → type-identifier & protocol-composition-continuationprotocol-composition-continuation → type-identifier | protocol-composition-type

OpaqueType

Anopaquetypedefinesatypethatconformstoaprotocolorprotocolcomposition,withoutspecifyingtheunderlyingconcretetype.

Opaquetypesappearasthereturntypeofafunctionorsubscript,orthetypeofaproperty.Opaquetypescan’tappearaspartofatupletypeoragenerictype,suchastheelementtypeofanarrayorthewrappedtypeofanoptional.

Opaquetypeshavethefollowingform:

some constraint

Theconstraintisaclasstype,protocoltype,protocolcompositiontype,orAny.Avaluecanbeusedasaninstanceoftheopaquetypeonlyifit’saninstanceofatypethatconformstothelistedprotocolorprotocolcomposition,orinheritsfromthelistedclass.Codethatinteractswithanopaquevaluecanusethevalueonlyinwaysthatarepartoftheinterfacedefinedbytheconstraint.

Protocoldeclarationscan’tincludeopaquetypes.Classescan’tuseanopaquetypeasthereturntypeofanonfinalmethod.

Afunctionthatusesanopaquetypeasitsreturntypemustreturnvaluesthatshareasingleunderlyingtype.Thereturntypecanincludetypesthatarepartofthefunction’sgenerictypeparameters.Forexample,afunctionsomeFunction<T>()couldreturnavalueoftypeTorDictionary<String,T>.

GRAMMAR OF AN OPAQUE TYPE

opaque-type → some type

MetatypeType

Ametatypetypereferstothetypeofanytype,includingclasstypes,structuretypes,enumerationtypes,andprotocoltypes.

Themetatypeofaclass,structure,orenumerationtypeisthenameofthattypefollowedby.Type.Themetatypeofaprotocoltype—nottheconcretetypethatconformstotheprotocolatruntime—isthenameofthatprotocolfollowedby.Protocol.Forexample,themetatypeoftheclasstypeSomeClassisSomeClass.TypeandthemetatypeoftheprotocolSomeProtocolisSomeProtocol.Protocol.

Youcanusethepostfixselfexpressiontoaccessatypeasavalue.Forexample,SomeClass.selfreturnsSomeClassitself,notaninstanceofSomeClass.AndSomeProtocol.selfreturnsSomeProtocolitself,notaninstanceofatypethatconformstoSomeProtocolatruntime.Youcancallthetype(of:)functionwith

aninstanceofatypetoaccessthatinstance’sdynamic,runtimetypeasavalue,asthefollowingexampleshows:

1 classSomeBaseClass{

2 classfuncprintClassName(){

3 print("SomeBaseClass")

4 }

5 }

6 classSomeSubClass:SomeBaseClass{

7 overrideclassfuncprintClassName(){

8 print("SomeSubClass")

9 }

10 }

11 letsomeInstance:SomeBaseClass=SomeSubClass()

12 //Thecompile-timetypeofsomeInstanceisSomeBaseClass,

13 //andtheruntimetypeofsomeInstanceisSomeSubClass

14 type(of:someInstance).printClassName()

15 //Prints"SomeSubClass"

Formoreinformation,seetype(of:)intheSwiftstandardlibrary.

Useaninitializerexpressiontoconstructaninstanceofatypefromthattype’smetatypevalue.Forclassinstances,theinitializerthat’scalledmustbemarkedwiththerequiredkeywordortheentireclassmarkedwiththefinalkeyword.

1 classAnotherSubClass:SomeBaseClass{

2 letstring:String

3 requiredinit(string:String){

4 self.string=string

5 }

6 overrideclassfuncprintClassName(){

7 print("AnotherSubClass")

8 }

9 }

10 letmetatype:AnotherSubClass.Type=AnotherSubClass.self

11 letanotherInstance=metatype.init(string:"somestring")

GRAMMAR OF AMETATYPE TYPE

metatype-type → type . Type | type . Protocol

SelfType

TheSelftypeisn’taspecifictype,butratherletsyouconvenientlyrefertothecurrenttypewithoutrepeatingorknowingthattype’sname.

Inaprotocoldeclarationoraprotocolmemberdeclaration,theSelftypereferstotheeventualtypethatconformstotheprotocol.

Inastructure,class,orenumerationdeclaration,theSelftypereferstothetypeintroducedbythedeclaration.Insidethedeclarationforamemberofatype,theSelftypereferstothattype.Inthemembersofaclassdeclaration,Selfcanappearasthereturntypeofamethodandinthebodyofamethod,butnotinanyothercontext.Forexample,thecodebelowshowsaninstancemethodfwhosereturntypeisSelf.

1 classSuperclass{

2 funcf()->Self{returnself}

3 }

4 letx=Superclass()

5 print(type(of:x.f()))

6 //Prints"Superclass"

7

8 classSubclass:Superclass{}

9 lety=Subclass()

10 print(type(of:y.f()))

11 //Prints"Subclass"

12

13 letz:Superclass=Subclass()

14 print(type(of:z.f()))

15 //Prints"Subclass"

ThelastpartoftheexampleaboveshowsthatSelfreferstotheruntimetypeSubclassofthevalueofz,notthecompile-timetypeSuperclassofthevariableitself.

Insideanestedtypedeclaration,theSelftypereferstothetypeintroducedbytheinnermosttypedeclaration.

TheSelftypereferstothesametypeasthetype(of:)functionintheSwiftstandardlibrary.WritingSelf.someStaticMembertoaccessamemberofthecurrenttypeisthesameaswritingtype(of:self).someStaticMember.

GRAMMAR OF A SELF TYPE

self-type → Self

TypeInheritanceClause

Atypeinheritanceclauseisusedtospecifywhichclassanamedtypeinheritsfromandwhichprotocolsanamedtypeconformsto.Atypeinheritanceclausebeginswithacolon(:),followedbyalistoftypeidentifiers.

Classtypescaninheritfromasinglesuperclassandconformtoanynumberofprotocols.Whendefiningaclass,thenameofthesuperclassmustappearfirstinthelistoftypeidentifiers,followedbyanynumberofprotocolstheclassmustconformto.Iftheclassdoesn’tinheritfromanotherclass,thelistcanbeginwithaprotocolinstead.Foranextendeddiscussionandseveralexamplesofclassinheritance,seeInheritance.

Othernamedtypescanonlyinheritfromorconformtoalistofprotocols.Protocoltypescaninheritfromanynumberofotherprotocols.Whenaprotocoltypeinheritsfromotherprotocols,thesetofrequirementsfromthoseotherprotocolsareaggregatedtogether,andanytypethatinheritsfromthecurrentprotocolmustconformtoallofthoserequirements.

Atypeinheritanceclauseinanenumerationdefinitioncanbeeitheralistofprotocols,orinthecaseofanenumerationthatassignsrawvaluestoitscases,asingle,namedtypethatspecifiesthetypeofthoserawvalues.Foranexampleofanenumerationdefinitionthatusesatypeinheritanceclausetospecifythetypeofitsrawvalues,seeRawValues.

GRAMMAR OF ATYPE I NHER I TANCE CLAUSE

type-inheritance-clause → : type-inheritance-listtype-inheritance-list → type-identifier | type-identifier , type-inheritance-list

TypeInference

Swiftusestypeinferenceextensively,allowingyoutoomitthetypeorpartofthetypeofmanyvariablesandexpressionsinyourcode.Forexample,insteadofwritingvarx:Int=0,youcanwritevarx=0,omittingthetypecompletely—thecompilercorrectlyinfersthatxnamesavalueoftypeInt.Similarly,youcanomitpartofatypewhenthefulltypecanbeinferredfromcontext.Forexample,ifyouwriteletdict:Dictionary=["A":1],thecompilerinfersthatdicthasthetypeDictionary<String,Int>.

Inbothoftheexamplesabove,thetypeinformationispassedupfromtheleavesoftheexpressiontreetoitsroot.Thatis,thetypeofxinvarx:Int=0isinferredbyfirstcheckingthetypeof0andthenpassingthistypeinformationuptotheroot(thevariablex).

InSwift,typeinformationcanalsoflowintheoppositedirection—fromtherootdowntotheleaves.Inthefollowingexample,forinstance,theexplicittypeannotation(:Float)ontheconstanteFloatcausesthenumericliteral2.71828tohaveaninferredtypeofFloatinsteadofDouble.

1 lete=2.71828//ThetypeofeisinferredtobeDouble.

2 leteFloat:Float=2.71828//ThetypeofeFloatisFloat.

TypeinferenceinSwiftoperatesatthelevelofasingleexpressionorstatement.Thismeansthatalloftheinformationneededtoinferanomittedtypeorpartofatypeinanexpressionmustbeaccessiblefromtype-checkingtheexpressionoroneofitssubexpressions.

Expressions

InSwift,therearefourkindsofexpressions:prefixexpressions,binaryexpressions,primaryexpressions,andpostfixexpressions.Evaluatinganexpressionreturnsavalue,causesasideeffect,orboth.

Prefixandbinaryexpressionsletyouapplyoperatorstosmallerexpressions.Primaryexpressionsareconceptuallythesimplestkindofexpression,andtheyprovideawaytoaccessvalues.Postfixexpressions,likeprefixandbinaryexpressions,letyoubuildupmorecomplexexpressionsusingpostfixessuchasfunctioncallsandmemberaccess.Eachkindofexpressionisdescribedindetailinthesectionsbelow.

GRAMMAR OF AN EXPRESS ION

expression → try-operator opt prefix-expression binary-expressions optexpression-list → expression | expression , expression-list

PrefixExpressions

Prefixexpressionscombineanoptionalprefixoperatorwithanexpression.Prefixoperatorstakeoneargument,theexpressionthatfollowsthem.

Forinformationaboutthebehavioroftheseoperators,seeBasicOperatorsandAdvancedOperators.

ForinformationabouttheoperatorsprovidedbytheSwiftstandardlibrary,seeOperatorDeclarations.

Inadditiontothestandardlibraryoperators,youuse&immediatelybeforethenameofavariablethat’sbeingpassedasanin-outargumenttoafunctioncallexpression.Formoreinformationandtoseeanexample,seeIn-OutParameters.

GRAMMAR OF A PREF IX EXPRESS ION

prefix-expression → prefix-operator opt postfix-expression

prefix-expression → in-out-expressionin-out-expression → & identifier

TryOperatorAtryexpressionconsistsofthetryoperatorfollowedbyanexpressionthatcanthrowanerror.Ithasthefollowingform:

try expression

Anoptional-tryexpressionconsistsofthetry?operatorfollowedbyanexpressionthatcanthrowanerror.Ithasthefollowingform:

try? expression

Iftheexpressiondoesnotthrowanerror,thevalueoftheoptional-tryexpressionisanoptionalcontainingthevalueoftheexpression.Otherwise,thevalueoftheoptional-tryexpressionisnil.

Aforced-tryexpressionconsistsofthetry!operatorfollowedbyanexpressionthatcanthrowanerror.Ithasthefollowingform:

try! expression

Iftheexpressionthrowsanerror,aruntimeerrorisproduced.

Whentheexpressionontheleft-handsideofabinaryoperatorismarkedwithtry,try?,ortry!,thatoperatorappliestothewholebinaryexpression.Thatsaid,youcanuseparenthesestobeexplicitaboutthescopeoftheoperator’sapplication.

1 sum=trysomeThrowingFunction()+anotherThrowingFunction()

//tryappliestobothfunctioncalls

2 sum=try(someThrowingFunction()+anotherThrowingFunction())

//tryappliestobothfunctioncalls

3 sum=(trysomeThrowingFunction())+anotherThrowingFunction()

//Error:tryappliesonlytothefirstfunctioncall

Atryexpressioncan’tappearontheright-handsideofabinaryoperator,unlessthebinaryoperatoristheassignmentoperatororthetryexpressionisenclosedinparentheses.

Formoreinformationandtoseeexamplesofhowtousetry,try?,andtry!,seeErrorHandling.

GRAMMAR OF ATRY EXPRESS ION

try-operator → try | try ? | try !

BinaryExpressions

Binaryexpressionscombineaninfixbinaryoperatorwiththeexpressionthatittakesasitsleft-handandright-handarguments.Ithasthefollowingform:

left-handargument operator right-handargument

Forinformationaboutthebehavioroftheseoperators,seeBasicOperatorsandAdvancedOperators.

ForinformationabouttheoperatorsprovidedbytheSwiftstandardlibrary,seeOperatorDeclarations.

NOTE

Atparsetime,anexpressionmadeupofbinaryoperatorsisrepresentedasaflatlist.Thislististransformedintoatreebyapplyingoperatorprecedence.Forexample,theexpression2+3*5isinitiallyunderstoodasaflatlistoffiveitems,2,+,3,*,and5.Thisprocesstransformsitintothetree(2+(3*5)).

GRAMMAR OF A B INARY EXPRESS ION

binary-expression → binary-operator prefix-expressionbinary-expression → assignment-operator try-operator opt prefix-expression

binary-expression → conditional-operator try-operator opt prefix-expressionbinary-expression → type-casting-operatorbinary-expressions → binary-expression binary-expressions opt

AssignmentOperatorTheassignmentoperatorsetsanewvalueforagivenexpression.Ithasthefollowingform:

expression = value

Thevalueoftheexpressionissettothevalueobtainedbyevaluatingthevalue.Iftheexpressionisatuple,thevaluemustbeatuplewiththesamenumberofelements.(Nestedtuplesareallowed.)Assignmentisperformedfromeachpartofthevaluetothecorrespondingpartoftheexpression.Forexample:

1 (a,_,(b,c))=("test",9.45,(12,3))

2 //ais"test",bis12,cis3,and9.45isignored

Theassignmentoperatordoesnotreturnanyvalue.

GRAMMAR OF AN ASS IGNMENT OPERATOR

assignment-operator → =

TernaryConditionalOperatorTheternaryconditionaloperatorevaluatestooneoftwogivenvaluesbasedonthevalueofacondition.Ithasthefollowingform:

condition ? expressionusediftrue :

expressionusediffalse

Iftheconditionevaluatestotrue,theconditionaloperatorevaluatesthefirstexpressionandreturnsitsvalue.Otherwise,itevaluatesthesecondexpression

andreturnsitsvalue.Theunusedexpressionisnotevaluated.

Foranexamplethatusestheternaryconditionaloperator,seeTernaryConditionalOperator.

GRAMMAR OF ACOND I T IONALOPERATOR

conditional-operator → ? expression :

Type-CastingOperatorsTherearefourtype-castingoperators:theisoperator,theasoperator,theas?operator,andtheas!operator.

Theyhavethefollowingform:

expression is type

expression as type

expression as? type

expression as! type

Theisoperatorchecksatruntimewhethertheexpressioncanbecasttothespecifiedtype.Itreturnstrueiftheexpressioncanbecasttothespecifiedtype;otherwise,itreturnsfalse.

Theasoperatorperformsacastwhenitisknownatcompiletimethatthecastalwayssucceeds,suchasupcastingorbridging.Upcastingletsyouuseanexpressionasaninstanceofitstype’ssupertype,withoutusinganintermediatevariable.Thefollowingapproachesareequivalent:

1 funcf(_any:Any){print("FunctionforAny")}

2 funcf(_int:Int){print("FunctionforInt")}

3 letx=10

4 f(x)

5 //Prints"FunctionforInt"

6

7 lety:Any=x

8 f(y)

9 //Prints"FunctionforAny"

10

11 f(xasAny)

12 //Prints"FunctionforAny"

BridgingletsyouuseanexpressionofaSwiftstandardlibrarytypesuchasStringasitscorrespondingFoundationtypesuchasNSStringwithoutneedingtocreateanewinstance.Formoreinformationonbridging,seeWorkingwithFoundationTypes.

Theas?operatorperformsaconditionalcastoftheexpressiontothespecifiedtype.Theas?operatorreturnsanoptionalofthespecifiedtype.Atruntime,ifthecastsucceeds,thevalueofexpressioniswrappedinanoptionalandreturned;otherwise,thevaluereturnedisnil.Ifcastingtothespecifiedtypeisguaranteedtofailorisguaranteedtosucceed,acompile-timeerrorisraised.

Theas!operatorperformsaforcedcastoftheexpressiontothespecifiedtype.Theas!operatorreturnsavalueofthespecifiedtype,notanoptionaltype.Ifthecastfails,aruntimeerrorisraised.Thebehaviorofxas!Tisthesameasthebehaviorof(xas?T)!.

Formoreinformationabouttypecastingandtoseeexamplesthatusethetype-castingoperators,seeTypeCasting.

GRAMMAR OF ATYPE -CAST ING OPERATOR

type-casting-operator → is typetype-casting-operator → as typetype-casting-operator → as ? typetype-casting-operator → as ! type

PrimaryExpressions

Primaryexpressionsarethemostbasickindofexpression.Theycanbeusedasexpressionsontheirown,andtheycanbecombinedwithothertokenstomakeprefixexpressions,binaryexpressions,andpostfixexpressions.

GRAMMAR OF A PR IMARY EXPRESS ION

primary-expression → identifier generic-argument-clause optprimary-expression → literal-expressionprimary-expression → self-expressionprimary-expression → superclass-expressionprimary-expression → closure-expressionprimary-expression → parenthesized-expressionprimary-expression → tuple-expressionprimary-expression → implicit-member-expressionprimary-expression → wildcard-expressionprimary-expression → key-path-expressionprimary-expression → selector-expressionprimary-expression → key-path-string-expression

LiteralExpressionAliteralexpressionconsistsofeitheranordinaryliteral(suchasastringoranumber),anarrayordictionaryliteral,aplaygroundliteral,oroneofthefollowingspecialliterals:

Literal Type Value

#file String Thenameofthefileinwhichitappears.

#line Int Thelinenumberonwhichitappears.

#column Int Thecolumnnumberinwhichitbegins.

#function String Thenameofthedeclarationinwhichitappears.

#dsohandle UnsafeRawPointer TheDSO(dynamicsharedobject)handleinusewhereitappears.

Insideafunction,thevalueof#functionisthenameofthatfunction,insideamethoditisthenameofthatmethod,insideapropertygetterorsetteritisthenameofthatproperty,insidespecialmemberslikeinitorsubscriptitisthenameofthatkeyword,andatthetoplevelofafileitisthenameofthecurrentmodule.

Whenusedasthedefaultvalueofafunctionormethodparameter,thespecialliteral’svalueisdeterminedwhenthedefaultvalueexpressionisevaluatedatthecallsite.

1 funclogFunctionName(string:String=#function){

2 print(string)

3 }

4 funcmyFunction(){

5 logFunctionName()//Prints"myFunction()".

6 }

Anarrayliteralisanorderedcollectionofvalues.Ithasthefollowingform:

[ value1 , value2 , ... ]

Thelastexpressioninthearraycanbefollowedbyanoptionalcomma.Thevalueofanarrayliteralhastype[T],whereTisthetypeoftheexpressionsinsideit.Ifthereareexpressionsofmultipletypes,Tistheirclosestcommonsupertype.Emptyarrayliteralsarewrittenusinganemptypairofsquarebracketsandcanbeusedtocreateanemptyarrayofaspecifiedtype.

varemptyArray:[Double]=[]

Adictionaryliteralisanunorderedcollectionofkey-valuepairs.Ithasthefollowingform:

[ key1 : value1 , key2 : value2 , ... ]

Thelastexpressioninthedictionarycanbefollowedbyanoptionalcomma.Thevalueofadictionaryliteralhastype[Key:Value],whereKeyisthetypeofitskeyexpressionsandValueisthetypeofitsvalueexpressions.Ifthereareexpressionsofmultipletypes,KeyandValuearetheclosestcommonsupertypefortheirrespectivevalues.Anemptydictionaryliteraliswrittenasacoloninsideapairofbrackets([:])todistinguishitfromanemptyarrayliteral.Youcanuseanemptydictionaryliteraltocreateanemptydictionaryliteralofspecifiedkeyandvaluetypes.

varemptyDictionary:[String:Double]=[:]

AplaygroundliteralisusedbyXcodetocreateaninteractiverepresentationofacolor,file,orimagewithintheprogrameditor.PlaygroundliteralsinplaintextoutsideofXcodearerepresentedusingaspecialliteralsyntax.

ForinformationonusingplaygroundliteralsinXcode,seeAddacolor,file,orimageliteralinXcodeHelp.

GRAMMAR OF A L I TERALEXPRESS ION

literal-expression → literalliteral-expression → array-literal | dictionary-literal | playground-literalliteral-expression → #file | #line | #column | #function | #dsohandlearray-literal → [ array-literal-items opt ]array-literal-items → array-literal-item ,opt | array-literal-item , array-literal-itemsarray-literal-item → expressiondictionary-literal → [ dictionary-literal-items ] | [ : ]dictionary-literal-items → dictionary-literal-item ,opt | dictionary-literal-item , dictionary-

literal-itemsdictionary-literal-item → expression : expressionplayground-literal → #colorLiteral ( red : expression , green : expression

, blue : expression , alpha : expression )playground-literal → #fileLiteral ( resourceName : expression )playground-literal → #imageLiteral ( resourceName : expression )

SelfExpression

Theselfexpressionisanexplicitreferencetothecurrenttypeorinstanceofthetypeinwhichitoccurs.Ithasthefollowingforms:

self

self. membername

self[ subscriptindex ]

self( initializerarguments )

self.init( initializerarguments )

Inaninitializer,subscript,orinstancemethod,selfreferstothecurrentinstanceofthetypeinwhichitoccurs.Inatypemethod,selfreferstothecurrenttypeinwhichitoccurs.

Theselfexpressionisusedtospecifyscopewhenaccessingmembers,providingdisambiguationwhenthereisanothervariableofthesamenameinscope,suchasafunctionparameter.Forexample:

1 classSomeClass{

2 vargreeting:String

3 init(greeting:String){

4 self.greeting=greeting

5 }

6 }

Inamutatingmethodofavaluetype,youcanassignanewinstanceofthatvaluetypetoself.Forexample:

1 structPoint{

2 varx=0.0,y=0.0

3 mutatingfuncmoveBy(xdeltaX:Double,ydeltaY:Double){

4 self=Point(x:x+deltaX,y:y+deltaY)

5 }

6 }

GRAMMAR OF A SELF EXPRESS ION

self-expression → self | self-method-expression | self-subscript-expression | self-initializer-expression

self-method-expression → self . identifierself-subscript-expression → self [ function-call-argument-list ]self-initializer-expression → self . init

SuperclassExpressionAsuperclassexpressionletsaclassinteractwithitssuperclass.Ithasoneofthefollowingforms:

super. membername

super[ subscriptindex ]

super.init( initializerarguments )

Thefirstformisusedtoaccessamemberofthesuperclass.Thesecondformisusedtoaccessthesuperclass’ssubscriptimplementation.Thethirdformisusedtoaccessaninitializerofthesuperclass.

Subclassescanuseasuperclassexpressionintheirimplementationofmembers,subscripting,andinitializerstomakeuseoftheimplementationintheirsuperclass.

GRAMMAR OF A SUPERCLASS EXPRESS ION

superclass-expression → superclass-method-expression | superclass-subscript-expression | superclass-initializer-expression

superclass-method-expression → super . identifiersuperclass-subscript-expression → super [ function-call-argument-list ]superclass-initializer-expression → super . init

ClosureExpressionAclosureexpressioncreatesaclosure,alsoknownasalambdaorananonymousfunctioninotherprogramminglanguages.Likeafunctiondeclaration,aclosurecontainsstatements,anditcapturesconstantsandvariablesfromitsenclosing

scope.Ithasthefollowingform:

{( parameters )-> returntype in

statements

}

Theparametershavethesameformastheparametersinafunctiondeclaration,asdescribedinFunctionDeclaration.

Thereareseveralspecialformsthatallowclosurestobewrittenmoreconcisely:

Aclosurecanomitthetypesofitsparameters,itsreturntype,orboth.Ifyouomittheparameternamesandbothtypes,omittheinkeywordbeforethestatements.Iftheomittedtypescan’tbeinferred,acompile-timeerrorisraised.

Aclosuremayomitnamesforitsparameters.Itsparametersarethenimplicitlynamed$followedbytheirposition:$0,$1,$2,andsoon.

Aclosurethatconsistsofonlyasingleexpressionisunderstoodtoreturnthevalueofthatexpression.Thecontentsofthisexpressionarealsoconsideredwhenperformingtypeinferenceonthesurroundingexpression.

Thefollowingclosureexpressionsareequivalent:

1 myFunction{(x:Int,y:Int)->Intin

2 returnx+y

3 }

4

5 myFunction{x,yin

6 returnx+y

7 }

8

9 myFunction{return$0+$1}

10

11 myFunction{$0+$1}

Forinformationaboutpassingaclosureasanargumenttoafunction,seeFunctionCallExpression.

Closureexpressionscanbeusedwithoutbeingstoredinavariableorconstant,suchaswhenyouimmediatelyuseaclosureaspartofafunctioncall.TheclosureexpressionspassedtomyFunctionincodeaboveareexamplesofthiskindofimmediateuse.Asaresult,whetheraclosureexpressionisescapingornonescapingdependsonthesurroundingcontextoftheexpression.Aclosureexpressionisnonescapingifitiscalledimmediatelyorpassedasanonescapingfunctionargument.Otherwise,theclosureexpressionisescaping.

Formoreinformationaboutescapingclosures,seeEscapingClosures.

CaptureLists

Bydefault,aclosureexpressioncapturesconstantsandvariablesfromitssurroundingscopewithstrongreferencestothosevalues.Youcanuseacapturelisttoexplicitlycontrolhowvaluesarecapturedinaclosure.

Acapturelistiswrittenasacomma-separatedlistofexpressionssurroundedbysquarebrackets,beforethelistofparameters.Ifyouuseacapturelist,youmustalsousetheinkeyword,evenifyouomittheparameternames,parametertypes,andreturntype.

Theentriesinthecapturelistareinitializedwhentheclosureiscreated.Foreachentryinthecapturelist,aconstantisinitializedtothevalueoftheconstantorvariablethathasthesamenameinthesurroundingscope.Forexampleinthecodebelow,aisincludedinthecapturelistbutbisnot,whichgivesthemdifferentbehavior.

1 vara=0

2 varb=0

3 letclosure={[a]in

4 print(a,b)

5 }

6

7 a=10

8 b=10

9 closure()

10 //Prints"010"

Therearetwodifferentthingsnameda,thevariableinthesurroundingscopeandtheconstantintheclosure’sscope,butonlyonevariablenamedb.Theaintheinnerscopeisinitializedwiththevalueoftheaintheouterscopewhentheclosureiscreated,buttheirvaluesarenotconnectedinanyspecialway.Thismeansthatachangetothevalueofaintheouterscopedoesnotaffectthevalueofaintheinnerscope,nordoesachangetoainsidetheclosureaffectthevalueofaoutsidetheclosure.Incontrast,thereisonlyonevariablenamedb—thebintheouterscope—sochangesfrominsideoroutsidetheclosurearevisibleinbothplaces.

Thisdistinctionisnotvisiblewhenthecapturedvariable’stypehasreferencesemantics.Forexample,therearetwothingsnamedxinthecodebelow,avariableintheouterscopeandaconstantintheinnerscope,buttheybothrefertothesameobjectbecauseofreferencesemantics.

1 classSimpleClass{

2 varvalue:Int=0

3 }

4 varx=SimpleClass()

5 vary=SimpleClass()

6 letclosure={[x]in

7 print(x.value,y.value)

8 }

9

10 x.value=10

11 y.value=10

12 closure()

13 //Prints"1010"

Ifthetypeoftheexpression’svalueisaclass,youcanmarktheexpressioninacapturelistwithweakorunownedtocaptureaweakorunownedreferencetotheexpression’svalue.

1 myFunction{print(self.title)}//implicit

strongcapture

2 myFunction{[self]inprint(self.title)}//explicit

strongcapture

3 myFunction{[weakself]inprint(self!.title)}//weak

capture

4 myFunction{[unownedself]inprint(self.title)}//unowned

capture

Youcanalsobindanarbitraryexpressiontoanamedvalueinacapturelist.Theexpressionisevaluatedwhentheclosureiscreated,andthevalueiscapturedwiththespecifiedstrength.Forexample:

1 //Weakcaptureof"self.parent"as"parent"

2 myFunction{[weakparent=self.parent]in

print(parent!.title)}

Formoreinformationandexamplesofclosureexpressions,seeClosureExpressions.Formoreinformationandexamplesofcapturelists,seeResolvingStrongReferenceCyclesforClosures.

GRAMMAR OF ACLOSURE EXPRESS ION

closure-expression → { closure-signature opt statements opt }closure-signature → capture-list opt closure-parameter-clause throwsopt function-

result opt inclosure-signature → capture-list inclosure-parameter-clause → ( ) | ( closure-parameter-list ) | identifier-listclosure-parameter-list → closure-parameter | closure-parameter , closure-parameter-

listclosure-parameter → closure-parameter-name type-annotation optclosure-parameter → closure-parameter-name type-annotation ...closure-parameter-name → identifiercapture-list → [ capture-list-items ]capture-list-items → capture-list-item | capture-list-item , capture-list-itemscapture-list-item → capture-specifier opt expressioncapture-specifier → weak | unowned | unowned(safe) | unowned(unsafe)

ImplicitMemberExpressionAnimplicitmemberexpressionisanabbreviatedwaytoaccessamemberofatype,suchasanenumerationcaseoratypemethod,inacontextwheretypeinferencecandeterminetheimpliedtype.Ithasthefollowingform:

. membername

Forexample:

1 varx=MyEnumeration.someValue

2 x=.anotherValue

GRAMMAR OF A IMPL IC I T MEMBER EXPRESS ION

implicit-member-expression → . identifier

ParenthesizedExpressionAparenthesizedexpressionconsistsofanexpressionsurroundedbyparentheses.Youcanuseparenthesestospecifytheprecedenceofoperationsbyexplicitlygroupingexpressions.Groupingparenthesesdon’tchangeanexpression’stype—forexample,thetypeof(1)issimplyInt.

GRAMMAR OF A PARENTHES I ZED EXPRESS ION

parenthesized-expression → ( expression )

TupleExpressionAtupleexpressionconsistsofacomma-separatedlistofexpressionssurroundedbyparentheses.Eachexpressioncanhaveanoptionalidentifierbeforeit,separatedbyacolon(:).Ithasthefollowingform:

( identifier1 : expression1 , identifier2 : expression2 ,

... )

Eachidentifierinatupleexpressionmustbeuniquewithinthescopeofthetupleexpression.Inanestedtupleexpression,identifiersatthesamelevelofnestingmustbeunique.Forexample,(a:10,a:20)isinvalidbecausethelabelaappearstwiceatthesamelevel.However,(a:10,b:(a:1,x:2))isvalid—althoughaappearstwice,itappearsonceintheoutertupleandonceintheinnertuple.

Atupleexpressioncancontainzeroexpressions,oritcancontaintwoormoreexpressions.Asingleexpressioninsideparenthesesisaparenthesizedexpression.

NOTE

Bothanemptytupleexpressionandanemptytupletypearewritten()inSwift.BecauseVoidisatypealiasfor(),youcanuseittowriteanemptytupletype.However,likealltypealiases,Voidisalwaysatype—youcan’tuseittowriteanemptytupleexpression.

GRAMMAR OF ATUPLE EXPRESS ION

tuple-expression → ( ) | ( tuple-element , tuple-element-list )tuple-element-list → tuple-element | tuple-element , tuple-element-listtuple-element → expression | identifier : expression

WildcardExpressionAwildcardexpressionisusedtoexplicitlyignoreavalueduringanassignment.Forexample,inthefollowingassignment10isassignedtoxand20isignored:

1 (x,_)=(10,20)

2 //xis10,and20isignored

GRAMMAR OF AW I LDCARD EXPRESS ION

wildcard-expression → _

Key-PathExpressionAkey-pathexpressionreferstoapropertyorsubscriptofatype.Youusekey-pathexpressionsindynamicprogrammingtasks,suchaskey-valueobserving.Theyhavethefollowingform:

\ typename . path

Thetypenameisthenameofaconcretetype,includinganygenericparameters,suchasString,[Int],orSet<Int>.

Thepathconsistsofpropertynames,subscripts,optional-chainingexpressions,andforcedunwrappingexpressions.Eachofthesekey-pathcomponentscanberepeatedasmanytimesasneeded,inanyorder.

Atcompiletime,akey-pathexpressionisreplacedbyaninstanceoftheKeyPathclass.

Toaccessavalueusingakeypath,passthekeypathtothesubscript(keyPath:)subscript,whichisavailableonalltypes.Forexample:

1 structSomeStructure{

2 varsomeValue:Int

3 }

4

5 lets=SomeStructure(someValue:12)

6 letpathToProperty=\SomeStructure.someValue

7

8 letvalue=s[keyPath:pathToProperty]

9 //valueis12

Thetypenamecanbeomittedincontextswheretypeinferencecandeterminetheimpliedtype.Thefollowingcodeuses\.somePropertyinsteadof\SomeClass.someProperty:

1 classSomeClass:NSObject{

2 @objcvarsomeProperty:Int

3 init(someProperty:Int){

4 self.someProperty=someProperty

5 }

6 }

7

8 letc=SomeClass(someProperty:10)

9 c.observe(\.someProperty){object,changein

10 //...

11 }

Thepathcanrefertoselftocreatetheidentitykeypath(\.self).Theidentitykeypathreferstoawholeinstance,soyoucanuseittoaccessandchangeallofthedatastoredinavariableinasinglestep.Forexample:

1 varcompoundValue=(a:1,b:2)

2 //EquivalenttocompoundValue=(a:10,b:20)

3 compoundValue[keyPath:\.self]=(a:10,b:20)

Thepathcancontainmultiplepropertynames,separatedbyperiods,torefertoapropertyofaproperty’svalue.Thiscodeusesthekeypathexpression\OuterStructure.outer.someValuetoaccessthesomeValuepropertyoftheOuterStructuretype’souterproperty:

1 structOuterStructure{

2 varouter:SomeStructure

3 init(someValue:Int){

4 self.outer=SomeStructure(someValue:someValue)

5 }

6 }

7

8 letnested=OuterStructure(someValue:24)

9 letnestedKeyPath=\OuterStructure.outer.someValue

10

11 letnestedValue=nested[keyPath:nestedKeyPath]

12 //nestedValueis24

Thepathcanincludesubscriptsusingbrackets,aslongasthesubscript’sparametertypeconformstotheHashableprotocol.Thisexampleusesasubscriptinakeypathtoaccessthesecondelementofanarray:

1 letgreetings=["hello","hola","bonjour","" ]

2 letmyGreeting=greetings[keyPath:\[String].[1]]

3 //myGreetingis'hola'

Thevalueusedinasubscriptcanbeanamedvalueoraliteral.Valuesarecapturedinkeypathsusingvaluesemantics.Thefollowingcodeusesthevariableindexinbothakey-pathexpressionandinaclosuretoaccessthethirdelementofthegreetingsarray.Whenindexismodified,thekey-pathexpressionstillreferencesthethirdelement,whiletheclosureusesthenewindex.

1 varindex=2

2 letpath=\[String].[index]

3 letfn:([String])->String={stringsinstrings[index]}

4

5 print(greetings[keyPath:path])

6 //Prints"bonjour"

7 print(fn(greetings))

8 //Prints"bonjour"

9

10 //Setting'index'toanewvaluedoesn'taffect'path'

11 index+=1

12 print(greetings[keyPath:path])

13 //Prints"bonjour"

14

15 //Because'fn'closesover'index',itusesthenewvalue

16 print(fn(greetings))

17 //Prints""

Thepathcanuseoptionalchainingandforcedunwrapping.Thiscodeusesoptionalchaininginakeypathtoaccessapropertyofanoptionalstring:

1 letfirstGreeting:String?=greetings.first

2 print(firstGreeting?.countasAny)

3 //Prints"Optional(5)"

4

5 //Dothesamethingusingakeypath.

6 letcount=greetings[keyPath:\[String].first?.count]

7 print(countasAny)

8 //Prints"Optional(5)"

Youcanmixandmatchcomponentsofkeypathstoaccessvaluesthataredeeplynestedwithinatype.Thefollowingcodeaccessesdifferentvaluesandpropertiesofadictionaryofarraysbyusingkey-pathexpressionsthatcombinethesecomponents.

1 letinterestingNumbers=["prime":[2,3,5,7,11,13,17],

2 "triangular":[1,3,6,10,15,21,

28],

3 "hexagonal":[1,6,15,28,45,66,

91]]

4 print(interestingNumbers[keyPath:\[String:[Int]].["prime"]]

asAny)

5 //Prints"Optional([2,3,5,7,11,13,17])"

6 print(interestingNumbers[keyPath:\[String:[Int]].["prime"]!

[0]])

7 //Prints"2"

8 print(interestingNumbers[keyPath:\[String:[Int]].

["hexagonal"]!.count])

9 //Prints"7"

10 print(interestingNumbers[keyPath:\[String:[Int]].

["hexagonal"]!.count.bitWidth])

11 //Prints"64"

FormoreinformationaboutusingkeypathsincodethatinteractswithObjective-CAPIs,seeUsingObjective-CRuntimeFeaturesinSwift.Forinformationaboutkey-valuecodingandkey-valueobserving,seeKey-ValueCodingProgrammingGuideandKey-ValueObservingProgrammingGuide.

GRAMMAR OF A KEY-PATH EXPRESS ION

key-path-expression → \ type opt . key-path-componentskey-path-components → key-path-component | key-path-component . key-path-

componentskey-path-component → identifier key-path-postfixes opt | key-path-postfixeskey-path-postfixes → key-path-postfix key-path-postfixes optkey-path-postfix → ? | ! | self | [ function-call-argument-list ]

SelectorExpressionAselectorexpressionletsyouaccesstheselectorusedtorefertoamethodortoaproperty’sgetterorsetterinObjective-C.Ithasthefollowingform:

#selector( methodname )

#selector(getter: propertyname )

#selector(setter: propertyname )

ThemethodnameandpropertynamemustbeareferencetoamethodorapropertythatisavailableintheObjective-Cruntime.ThevalueofaselectorexpressionisaninstanceoftheSelectortype.Forexample:

1 classSomeClass:NSObject{

2 @objcletproperty:String

3 @objc(doSomethingWithInt:)

4 funcdoSomething(_x:Int){}

5

6 init(property:String){

7 self.property=property

8 }

9 }

10 letselectorForMethod=#selector(SomeClass.doSomething(_:))

11 letselectorForPropertyGetter=#selector(getter:

SomeClass.property)

Whencreatingaselectorforaproperty’sgetter,thepropertynamecanbeareferencetoavariableorconstantproperty.Incontrast,whencreatingaselectorforaproperty’ssetter,thepropertynamemustbeareferencetoavariablepropertyonly.

Themethodnamecancontainparenthesesforgrouping,aswelltheasoperatortodisambiguatebetweenmethodsthatshareanamebuthavedifferenttypesignatures.Forexample:

1 extensionSomeClass{

2 @objc(doSomethingWithString:)

3 funcdoSomething(_x:String){}

4 }

5 letanotherSelector=#selector(SomeClass.doSomething(_:)as

(SomeClass)->(String)->Void)

Becauseaselectoriscreatedatcompiletime,notatruntime,thecompilercancheckthatamethodorpropertyexistsandthatthey’reexposedtotheObjective-Cruntime.

NOTE

Althoughthemethodnameandthepropertynameareexpressions,they’reneverevaluated.

FormoreinformationaboutusingselectorsinSwiftcodethatinteractswithObjective-CAPIs,seeUsingObjective-CRuntimeFeaturesinSwift.

GRAMMAR OF A SELECTOR EXPRESS ION

selector-expression → #selector ( expression )selector-expression → #selector ( getter: expression )selector-expression → #selector ( setter: expression )

Key-PathStringExpressionAkey-pathstringexpressionletsyouaccessthestringusedtorefertoapropertyinObjective-C,foruseinkey-valuecodingandkey-valueobservingAPIs.Ithasthefollowingform:

#keyPath( propertyname )

ThepropertynamemustbeareferencetoapropertythatisavailableintheObjective-Cruntime.Atcompiletime,thekey-pathstringexpressionisreplacedbyastringliteral.Forexample:

1 classSomeClass:NSObject{

2 @objcvarsomeProperty:Int

3 init(someProperty:Int){

4 self.someProperty=someProperty

5 }

6 }

7

8 letc=SomeClass(someProperty:12)

9 letkeyPath=#keyPath(SomeClass.someProperty)

10

11 ifletvalue=c.value(forKey:keyPath){

12 print(value)

13 }

14 //Prints"12"

Whenyouuseakey-pathstringexpressionwithinaclass,youcanrefertoapropertyofthatclassbywritingjustthepropertyname,withouttheclassname.

1 extensionSomeClass{

2 funcgetSomeKeyPath()->String{

3 return#keyPath(someProperty)

4 }

5 }

6 print(keyPath==c.getSomeKeyPath())

7 //Prints"true"

Becausethekeypathstringiscreatedatcompiletime,notatruntime,thecompilercancheckthatthepropertyexistsandthatthepropertyisexposedtotheObjective-Cruntime.

FormoreinformationaboutusingkeypathsinSwiftcodethatinteractswithObjective-CAPIs,seeUsingObjective-CRuntimeFeaturesinSwift.Forinformationaboutkey-valuecodingandkey-valueobserving,seeKey-ValueCodingProgrammingGuideandKey-ValueObservingProgrammingGuide.

NOTE

Althoughthepropertynameisanexpression,itisneverevaluated.

GRAMMAR OF A KEY-PATH STR ING EXPRESS ION

key-path-string-expression → #keyPath ( expression )

PostfixExpressions

Postfixexpressionsareformedbyapplyingapostfixoperatororotherpostfixsyntaxtoanexpression.Syntactically,everyprimaryexpressionisalsoapostfixexpression.

Forinformationaboutthebehavioroftheseoperators,seeBasicOperatorsandAdvancedOperators.

ForinformationabouttheoperatorsprovidedbytheSwiftstandardlibrary,seeOperatorDeclarations.

GRAMMAR OF A POSTF IX EXPRESS ION

postfix-expression → primary-expressionpostfix-expression → postfix-expression postfix-operatorpostfix-expression → function-call-expressionpostfix-expression → initializer-expressionpostfix-expression → explicit-member-expressionpostfix-expression → postfix-self-expressionpostfix-expression → subscript-expressionpostfix-expression → forced-value-expressionpostfix-expression → optional-chaining-expression

FunctionCallExpressionAfunctioncallexpressionconsistsofafunctionnamefollowedbyacomma-separatedlistofthefunction’sargumentsinparentheses.Functioncallexpressionshavethefollowingform:

functionname ( argumentvalue1 , argumentvalue2 )

Thefunctionnamecanbeanyexpressionwhosevalueisofafunctiontype.

Ifthefunctiondefinitionincludesnamesforitsparameters,thefunctioncallmustincludenamesbeforeitsargumentvaluesseparatedbyacolon(:).Thiskindoffunctioncallexpressionhasthefollowingform:

functionname ( argumentname1 : argumentvalue1 ,

argumentname2 : argumentvalue2 )

Afunctioncallexpressioncanincludeatrailingclosureintheformofaclosureexpressionimmediatelyaftertheclosingparenthesis.Thetrailingclosureisunderstoodasanargumenttothefunction,addedafterthelastparenthesizedargument.Thefollowingfunctioncallsareequivalent:

1 //someFunctiontakesanintegerandaclosureasitsarguments

2 someFunction(x:x,f:{$0==13})

3 someFunction(x:x){$0==13}

Ifthetrailingclosureisthefunction’sonlyargument,theparenthesescanbeomitted.

1 //someMethodtakesaclosureasitsonlyargument

2 myData.someMethod(){$0==13}

3 myData.someMethod{$0==13}

GRAMMAR OF A FUNCT ION CALL EXPRESS ION

function-call-expression → postfix-expression function-call-argument-clausefunction-call-expression → postfix-expression function-call-argument-clause opt trailing-

closurefunction-call-argument-clause → ( ) | ( function-call-argument-list )function-call-argument-list → function-call-argument | function-call-argument , function-

call-argument-listfunction-call-argument → expression | identifier : expressionfunction-call-argument → operator | identifier : operatortrailing-closure → closure-expression

InitializerExpression

Aninitializerexpressionprovidesaccesstoatype’sinitializer.Ithasthefollowingform:

expression .init( initializerarguments )

Youusetheinitializerexpressioninafunctioncallexpressiontoinitializeanewinstanceofatype.Youalsouseaninitializerexpressiontodelegatetotheinitializerofasuperclass.

1 classSomeSubClass:SomeSuperClass{

2 overrideinit(){

3 //subclassinitializationgoeshere

4 super.init()

5 }

6 }

Likeafunction,aninitializercanbeusedasavalue.Forexample:

1 //TypeannotationisrequiredbecauseStringhasmultiple

initializers.

2 letinitializer:(Int)->String=String.init

3 letoneTwoThree=[1,2,3].map(initializer).reduce("",+)

4 print(oneTwoThree)

5 //Prints"123"

Ifyouspecifyatypebyname,youcanaccessthetype’sinitializerwithoutusinganinitializerexpression.Inallothercases,youmustuseaninitializerexpression.

1 lets1=SomeType.init(data:3)//Valid

2 lets2=SomeType(data:1)//Alsovalid

3

4 lets3=type(of:someValue).init(data:7)//Valid

5 lets4=type(of:someValue)(data:5)//Error

GRAMMAR OF AN I N I T I A L I ZER EXPRESS ION

initializer-expression → postfix-expression . initinitializer-expression → postfix-expression . init ( argument-names )

ExplicitMemberExpressionAnexplicitmemberexpressionallowsaccesstothemembersofanamedtype,atuple,oramodule.Itconsistsofaperiod(.)betweentheitemandtheidentifierofitsmember.

expression . membername

Themembersofanamedtypearenamedaspartofthetype’sdeclarationorextension.Forexample:

1 classSomeClass{

2 varsomeProperty=42

3 }

4 letc=SomeClass()

5 lety=c.someProperty//Memberaccess

Themembersofatupleareimplicitlynamedusingintegersintheordertheyappear,startingfromzero.Forexample:

1 vart=(10,20,30)

2 t.0=t.1

3 //Nowtis(20,20,30)

Themembersofamoduleaccessthetop-leveldeclarationsofthatmodule.

TypesdeclaredwiththedynamicMemberLookupattributeincludemembersthatarelookedupatruntime,asdescribedinAttributes.

Todistinguishbetweenmethodsorinitializerswhosenamesdifferonlybythenamesoftheirarguments,includetheargumentnamesinparentheses,witheachargumentnamefollowedbyacolon(:).Writeanunderscore(_)foranargumentwithnoname.Todistinguishbetweenoverloadedmethods,useatypeannotation.Forexample:

1 classSomeClass{

2 funcsomeMethod(x:Int,y:Int){}

3 funcsomeMethod(x:Int,z:Int){}

4 funcoverloadedMethod(x:Int,y:Int){}

5 funcoverloadedMethod(x:Int,y:Bool){}

6 }

7 letinstance=SomeClass()

8

9 leta=instance.someMethod//Ambiguous

10 letb=instance.someMethod(x:y:)//Unambiguous

11

12 letd=instance.overloadedMethod//Ambiguous

13 letd=instance.overloadedMethod(x:y:)//Stillambiguous

14 letd:(Int,Bool)->Void=instance.overloadedMethod(x:y:)

//Unambiguous

Ifaperiodappearsatthebeginningofaline,itisunderstoodaspartofanexplicitmemberexpression,notasanimplicitmemberexpression.Forexample,thefollowinglistingshowschainedmethodcallssplitoverseverallines:

1 letx=[10,3,20,15,4]

2 .sorted()

3 .filter{$0>5}

4 .map{$0*100}

GRAMMAR OF AN EXPL IC I T MEMBER EXPRESS ION

explicit-member-expression → postfix-expression . decimal-digitsexplicit-member-expression → postfix-expression . identifier generic-argument-clause

optexplicit-member-expression → postfix-expression . identifier ( argument-names )argument-names → argument-name argument-names optargument-name → identifier :

PostfixSelfExpressionApostfixselfexpressionconsistsofanexpressionorthenameofatype,immediatelyfollowedby.self.Ithasthefollowingforms:

expression .self

type .self

Thefirstformevaluatestothevalueoftheexpression.Forexample,x.selfevaluatestox.

Thesecondformevaluatestothevalueofthetype.Usethisformtoaccessatypeasavalue.Forexample,becauseSomeClass.selfevaluatestotheSomeClasstypeitself,youcanpassittoafunctionormethodthatacceptsatype-levelargument.

GRAMMAR OF A POSTF IX SELF EXPRESS ION

postfix-self-expression → postfix-expression . self

SubscriptExpressionAsubscriptexpressionprovidessubscriptaccessusingthegetterandsetterofthecorrespondingsubscriptdeclaration.Ithasthefollowingform:

expression [ indexexpressions ]

Toevaluatethevalueofasubscriptexpression,thesubscriptgetterfortheexpression’stypeiscalledwiththeindexexpressionspassedasthesubscriptparameters.Tosetitsvalue,thesubscriptsetteriscalledinthesameway.

Forinformationaboutsubscriptdeclarations,seeProtocolSubscriptDeclaration.

GRAMMAR OF A SUBSCR IPT EXPRESS ION

subscript-expression → postfix-expression [ function-call-argument-list ]

Forced-ValueExpressionAforced-valueexpressionunwrapsanoptionalvaluethatyouarecertainisnotnil.Ithasthefollowingform:

expression !

Ifthevalueoftheexpressionisnotnil,theoptionalvalueisunwrappedandreturnedwiththecorrespondingnon-optionaltype.Otherwise,aruntimeerrorisraised.

Theunwrappedvalueofaforced-valueexpressioncanbemodified,eitherbymutatingthevalueitself,orbyassigningtooneofthevalue’smembers.Forexample:

1 varx:Int?=0

2 x!+=1

3 //xisnow1

4

5 varsomeDictionary=["a":[1,2,3],"b":[10,20]]

6 someDictionary["a"]![0]=100

7 //someDictionaryisnow["a":[100,2,3],"b":[10,20]]

GRAMMAR OF A FORCED -VALUE EXPRESS ION

forced-value-expression → postfix-expression !

Optional-ChainingExpression

Anoptional-chainingexpressionprovidesasimplifiedsyntaxforusingoptionalvaluesinpostfixexpressions.Ithasthefollowingform:

expression ?

Thepostfix?operatormakesanoptional-chainingexpressionfromanexpressionwithoutchangingtheexpression’svalue.

Optional-chainingexpressionsmustappearwithinapostfixexpression,andtheycausethepostfixexpressiontobeevaluatedinaspecialway.Ifthevalueoftheoptional-chainingexpressionisnil,alloftheotheroperationsinthepostfixexpressionareignoredandtheentirepostfixexpressionevaluatestonil.Ifthevalueoftheoptional-chainingexpressionisnotnil,thevalueoftheoptional-chainingexpressionisunwrappedandusedtoevaluatetherestofthepostfixexpression.Ineithercase,thevalueofthepostfixexpressionisstillofanoptionaltype.

Ifapostfixexpressionthatcontainsanoptional-chainingexpressionisnestedinsideotherpostfixexpressions,onlytheoutermostexpressionreturnsanoptionaltype.Intheexamplebelow,whencisnotnil,itsvalueisunwrappedandusedtoevaluate.property,thevalueofwhichisusedtoevaluate.performAction().Theentireexpressionc?.property.performAction()hasavalueofanoptionaltype.

1 varc:SomeClass?

2 varresult:Bool?=c?.property.performAction()

Thefollowingexampleshowsthebehavioroftheexampleabovewithoutusingoptionalchaining.

1 varresult:Bool?

2 ifletunwrappedC=c{

3 result=unwrappedC.property.performAction()

4 }

Theunwrappedvalueofanoptional-chainingexpressioncanbemodified,either

bymutatingthevalueitself,orbyassigningtooneofthevalue’smembers.Ifthevalueoftheoptional-chainingexpressionisnil,theexpressionontheright-handsideoftheassignmentoperatorisnotevaluated.Forexample:

1 funcsomeFunctionWithSideEffects()->Int{

2 return42//Noactualsideeffects.

3 }

4 varsomeDictionary=["a":[1,2,3],"b":[10,20]]

5

6 someDictionary["nothere"]?[0]=someFunctionWithSideEffects()

7 //someFunctionWithSideEffectsisnotevaluated

8 //someDictionaryisstill["a":[1,2,3],"b":[10,20]]

9

10 someDictionary["a"]?[0]=someFunctionWithSideEffects()

11 //someFunctionWithSideEffectsisevaluatedandreturns42

12 //someDictionaryisnow["a":[42,2,3],"b":[10,20]]

GRAMMAR OF AN OPT IONAL -CHA IN ING EXPRESS ION

optional-chaining-expression → postfix-expression ?

Statements

InSwift,therearethreekindsofstatements:simplestatements,compilercontrolstatements,andcontrolflowstatements.Simplestatementsarethemostcommonandconsistofeitheranexpressionoradeclaration.Compilercontrolstatementsallowtheprogramtochangeaspectsofthecompiler’sbehaviorandincludeaconditionalcompilationblockandalinecontrolstatement.

Controlflowstatementsareusedtocontroltheflowofexecutioninaprogram.ThereareseveraltypesofcontrolflowstatementsinSwift,includingloopstatements,branchstatements,andcontroltransferstatements.Loopstatementsallowablockofcodetobeexecutedrepeatedly,branchstatementsallowacertainblockofcodetobeexecutedonlywhencertainconditionsaremet,andcontroltransferstatementsprovideawaytoaltertheorderinwhichcodeisexecuted.Inaddition,Swiftprovidesadostatementtointroducescope,andcatchandhandleerrors,andadeferstatementforrunningcleanupactionsjustbeforethecurrentscopeexits.

Asemicolon(;)canoptionallyappearafteranystatementandisusedtoseparatemultiplestatementsiftheyappearonthesameline.

GRAMMAR OF A STATEMENT

statement → expression ;optstatement → declaration ;optstatement → loop-statement ;optstatement → branch-statement ;optstatement → labeled-statement ;optstatement → control-transfer-statement ;optstatement → defer-statement ;optstatement → do-statement ;optstatement → compiler-control-statementstatements → statement statements opt

LoopStatements

Loopstatementsallowablockofcodetobeexecutedrepeatedly,dependingontheconditionsspecifiedintheloop.Swifthasthreeloopstatements:afor-instatement,awhilestatement,andarepeat-whilestatement.

ControlflowinaloopstatementcanbechangedbyabreakstatementandacontinuestatementandisdiscussedinBreakStatementandContinueStatementbelow.

GRAMMAR OF A LOOP STATEMENT

loop-statement → for-in-statementloop-statement → while-statementloop-statement → repeat-while-statement

For-InStatementAfor-instatementallowsablockofcodetobeexecutedonceforeachiteminacollection(oranytype)thatconformstotheSequenceprotocol.

Afor-instatementhasthefollowingform:

for item in collection {

statements

}

ThemakeIterator()methodiscalledonthecollectionexpressiontoobtainavalueofaniteratortype—thatis,atypethatconformstotheIteratorProtocolprotocol.Theprogrambeginsexecutingaloopbycallingthenext()methodontheiterator.Ifthevaluereturnedisnotnil,itisassignedtotheitempattern,theprogramexecutesthestatements,andthencontinuesexecutionatthebeginningoftheloop.Otherwise,theprogramdoesnotperformassignmentorexecutethestatements,anditisfinishedexecutingthefor-instatement.

GRAMMAR OF A FOR - IN STATEMENT

for-in-statement → for caseopt pattern in expression where-clause opt code-block

WhileStatementAwhilestatementallowsablockofcodetobeexecutedrepeatedly,aslongasaconditionremainstrue.

Awhilestatementhasthefollowingform:

while condition {

statements

}

Awhilestatementisexecutedasfollows:

1. Theconditionisevaluated.

Iftrue,executioncontinuestostep2.Iffalse,theprogramisfinishedexecutingthewhilestatement.

2. Theprogramexecutesthestatements,andexecutionreturnstostep1.

Becausethevalueoftheconditionisevaluatedbeforethestatementsareexecuted,thestatementsinawhilestatementcanbeexecutedzeroormoretimes.

ThevalueoftheconditionmustbeoftypeBooloratypebridgedtoBool.Theconditioncanalsobeanoptionalbindingdeclaration,asdiscussedinOptionalBinding.

GRAMMAR OF AWH I LE STATEMENT

while-statement → while condition-list code-blockcondition-list → condition | condition , condition-listcondition → expression | availability-condition | case-condition | optional-binding-

conditioncase-condition → case pattern initializeroptional-binding-condition → let pattern initializer | var pattern initializer

Repeat-WhileStatement

Arepeat-whilestatementallowsablockofcodetobeexecutedoneormoretimes,aslongasaconditionremainstrue.

Arepeat-whilestatementhasthefollowingform:

repeat{

statements

}while condition

Arepeat-whilestatementisexecutedasfollows:

1. Theprogramexecutesthestatements,andexecutioncontinuestostep2.

2. Theconditionisevaluated.

Iftrue,executionreturnstostep1.Iffalse,theprogramisfinishedexecutingtherepeat-whilestatement.

Becausethevalueoftheconditionisevaluatedafterthestatementsareexecuted,thestatementsinarepeat-whilestatementareexecutedatleastonce.

ThevalueoftheconditionmustbeoftypeBooloratypebridgedtoBool.Theconditioncanalsobeanoptionalbindingdeclaration,asdiscussedinOptionalBinding.

GRAMMAR OF AREPEAT-WH I LE STATEMENT

repeat-while-statement → repeat code-block while expression

BranchStatements

Branchstatementsallowtheprogramtoexecutecertainpartsofcodedependingonthevalueofoneormoreconditions.Thevaluesoftheconditionsspecifiedinabranchstatementcontrolhowtheprogrambranchesand,therefore,whatblockofcodeisexecuted.Swifthasthreebranchstatements:anifstatement,aguardstatement,andaswitchstatement.

ControlflowinanifstatementoraswitchstatementcanbechangedbyabreakstatementandisdiscussedinBreakStatementbelow.

GRAMMAR OF A BRANCH STATEMENT

branch-statement → if-statementbranch-statement → guard-statementbranch-statement → switch-statement

IfStatementAnifstatementisusedforexecutingcodebasedontheevaluationofoneormoreconditions.

Therearetwobasicformsofanifstatement.Ineachform,theopeningandclosingbracesarerequired.

Thefirstformallowscodetobeexecutedonlywhenaconditionistrueandhasthefollowingform:

if condition {

statements

}

Thesecondformofanifstatementprovidesanadditionalelseclause(introducedbytheelsekeyword)andisusedforexecutingonepartofcodewhentheconditionistrueandanotherpartofcodewhenthesameconditionisfalse.Whenasingleelseclauseispresent,anifstatementhasthefollowingform:

if condition {

statementstoexecuteifconditionistrue

}else{

statementstoexecuteifconditionisfalse

}

Theelseclauseofanifstatementcancontainanotherifstatementtotestmore

thanonecondition.Anifstatementchainedtogetherinthiswayhasthefollowingform:

if condition1 {

statementstoexecuteifcondition1istrue

}elseif condition2 {

statementstoexecuteifcondition2istrue

}else{

statementstoexecuteifbothconditionsarefalse

}

ThevalueofanyconditioninanifstatementmustbeoftypeBooloratypebridgedtoBool.Theconditioncanalsobeanoptionalbindingdeclaration,asdiscussedinOptionalBinding.

GRAMMAR OF AN I F STATEMENT

if-statement → if condition-list code-block else-clause optelse-clause → else code-block | else if-statement

GuardStatementAguardstatementisusedtotransferprogramcontroloutofascopeifoneormoreconditionsaren’tmet.

Aguardstatementhasthefollowingform:

guard condition else{

statements

}

ThevalueofanyconditioninaguardstatementmustbeoftypeBooloratypebridgedtoBool.Theconditioncanalsobeanoptionalbindingdeclaration,asdiscussedinOptionalBinding.

Anyconstantsorvariablesassignedavaluefromanoptionalbindingdeclaration

inaguardstatementconditioncanbeusedfortherestoftheguardstatement’senclosingscope.

Theelseclauseofaguardstatementisrequired,andmusteithercallafunctionwiththeNeverreturntypeortransferprogramcontroloutsidetheguardstatement’senclosingscopeusingoneofthefollowingstatements:

return

break

continue

throw

ControltransferstatementsarediscussedinControlTransferStatementsbelow.FormoreinformationonfunctionswiththeNeverreturntype,seeFunctionsthatNeverReturn.

GRAMMAR OF AGUARD STATEMENT

guard-statement → guard condition-list else code-block

SwitchStatementAswitchstatementallowscertainblocksofcodetobeexecuteddependingonthevalueofacontrolexpression.

Aswitchstatementhasthefollowingform:

switch controlexpression {

case pattern1 :

statements

case pattern2 where condition :

statements

case pattern3 where condition ,

pattern4 where condition :

statements

default:

statements

}

Thecontrolexpressionoftheswitchstatementisevaluatedandthencomparedwiththepatternsspecifiedineachcase.Ifamatchisfound,theprogramexecutesthestatementslistedwithinthescopeofthatcase.Thescopeofeachcasecan’tbeempty.Asaresult,youmustincludeatleastonestatementfollowingthecolon(:)ofeachcaselabel.Useasinglebreakstatementifyoudon’tintendtoexecuteanycodeinthebodyofamatchedcase.

Thevaluesofexpressionsyourcodecanbranchonareveryflexible.Forexample,inadditiontothevaluesofscalartypes,suchasintegersandcharacters,yourcodecanbranchonthevaluesofanytype,includingfloating-pointnumbers,strings,tuples,instancesofcustomclasses,andoptionals.Thevalueofthecontrolexpressioncanevenbematchedtothevalueofacaseinanenumerationandcheckedforinclusioninaspecifiedrangeofvalues.Forexamplesofhowtousethesevarioustypesofvaluesinswitchstatements,seeSwitchinControlFlow.

Aswitchcasecanoptionallycontainawhereclauseaftereachpattern.Awhereclauseisintroducedbythewherekeywordfollowedbyanexpression,andisusedtoprovideanadditionalconditionbeforeapatterninacaseisconsideredmatchedtothecontrolexpression.Ifawhereclauseispresent,thestatementswithintherelevantcaseareexecutedonlyifthevalueofthecontrolexpressionmatchesoneofthepatternsofthecaseandtheexpressionofthewhereclauseevaluatestotrue.Forexample,acontrolexpressionmatchesthecaseintheexamplebelowonlyifitisatuplethatcontainstwoelementsofthesamevalue,suchas(1,1).

caselet(x,y)wherex==y:

Astheaboveexampleshows,patternsinacasecanalsobindconstantsusingtheletkeyword(theycanalsobindvariablesusingthevarkeyword).Theseconstants(orvariables)canthenbereferencedinacorrespondingwhereclause

andthroughouttherestofthecodewithinthescopeofthecase.Ifthecasecontainsmultiplepatternsthatmatchthecontrolexpression,allofthepatternsmustcontainthesameconstantorvariablebindings,andeachboundvariableorconstantmusthavethesametypeinallofthecase’spatterns.

Aswitchstatementcanalsoincludeadefaultcase,introducedbythedefaultkeyword.Thecodewithinadefaultcaseisexecutedonlyifnoothercasesmatchthecontrolexpression.Aswitchstatementcanincludeonlyonedefaultcase,whichmustappearattheendoftheswitchstatement.

Althoughtheactualexecutionorderofpattern-matchingoperations,andinparticulartheevaluationorderofpatternsincases,isunspecified,patternmatchinginaswitchstatementbehavesasiftheevaluationisperformedinsourceorder—thatis,theorderinwhichtheyappearinsourcecode.Asaresult,ifmultiplecasescontainpatternsthatevaluatetothesamevalue,andthuscanmatchthevalueofthecontrolexpression,theprogramexecutesonlythecodewithinthefirstmatchingcaseinsourceorder.

SwitchStatementsMustBeExhaustive

InSwift,everypossiblevalueofthecontrolexpression’stypemustmatchthevalueofatleastonepatternofacase.Whenthissimplyisn’tfeasible(forexample,whenthecontrolexpression’stypeisInt),youcanincludeadefaultcasetosatisfytherequirement.

SwitchingOverFutureEnumerationCases

Anonfrozenenumerationisaspecialkindofenumerationthatmaygainnewenumerationcasesinthefuture—evenafteryoucompileandshipanapp.Switchingoveranonfrozenenumerationrequiresextraconsideration.Whenalibrary’sauthorsmarkanenumerationasnonfrozen,theyreservetherighttoaddnewenumerationcases,andanycodethatinteractswiththatenumerationmustbeabletohandlethosefuturecaseswithoutbeingrecompiled.Codethat’scompiledinlibraryevolutionmode,codeinthestandardlibrary,SwiftoverlaysforAppleframeworks,andCandObjective-Ccodecandeclarenonfrozenenumerations.Forinformationaboutfrozenandnonfrozenenumerations,seefrozen.

Whenswitchingoveranonfrozenenumerationvalue,youalwaysneedtoincludeadefaultcase,evenifeverycaseoftheenumerationalreadyhasacorrespondingswitchcase.Youcanapplythe@unknownattributetothedefaultcase,whichindicatesthatthedefaultcaseshouldmatchonlyenumerationcasesthatareaddedinthefuture.Swiftproducesawarningifthedefaultcasematchesanyenumerationcasethatisknownatcompilertime.Thisfuturewarninginformsyouthatthelibraryauthoraddedanewcasetotheenumerationthatdoesn’thaveacorrespondingswitchcase.

Thefollowingexampleswitchesoverallthreeexistingcasesofthestandardlibrary’sMirror.AncestorRepresentationenumeration.Ifyouaddadditionalcasesinthefuture,thecompilergeneratesawarningtoindicatethatyouneedtoupdatetheswitchstatementtotakethenewcasesintoaccount.

1 letrepresentation:Mirror.AncestorRepresentation=.generated

2 switchrepresentation{

3 case.customized:

4 print("Usethenearestancestor’simplementation.")

5 case.generated:

6 print("Generateadefaultmirrorforallancestor

classes.")

7 case.suppressed:

8 print("Suppresstherepresentationofallancestor

classes.")

9 @unknowndefault:

10 print("Usearepresentationthatwasunknownwhenthis

codewascompiled.")

11 }

12 //Prints"Generateadefaultmirrorforallancestor

classes."

ExecutionDoesNotFallThroughCasesImplicitly

Afterthecodewithinamatchedcasehasfinishedexecuting,theprogramexitsfromtheswitchstatement.Programexecutiondoesnotcontinueor“fallthrough”tothenextcaseordefaultcase.Thatsaid,ifyouwantexecutiontocontinuefromonecasetothenext,explicitlyincludeafallthroughstatement,whichsimplyconsistsofthefallthroughkeyword,inthecasefromwhichyouwantexecutiontocontinue.Formoreinformationaboutthefallthroughstatement,seeFallthroughStatementbelow.

GRAMMAR OF A SW ITCH STATEMENT

switch-statement → switch expression { switch-cases opt }switch-cases → switch-case switch-cases optswitch-case → case-label statementsswitch-case → default-label statementsswitch-case → conditional-switch-casecase-label → attributes opt case case-item-list :case-item-list → pattern where-clause opt | pattern where-clause opt , case-item-listdefault-label → attributes opt default :where-clause → where where-expressionwhere-expression → expressionconditional-switch-case → switch-if-directive-clause switch-elseif-directive-clauses opt

switch-else-directive-clause opt endif-directiveswitch-if-directive-clause → if-directive compilation-condition switch-cases optswitch-elseif-directive-clauses → elseif-directive-clause switch-elseif-directive-clauses optswitch-elseif-directive-clause → elseif-directive compilation-condition switch-cases optswitch-else-directive-clause → else-directive switch-cases opt

LabeledStatement

Youcanprefixaloopstatement,anifstatement,aswitchstatement,oradostatementwithastatementlabel,whichconsistsofthenameofthelabelfollowedimmediatelybyacolon(:).Usestatementlabelswithbreakandcontinuestatementstobeexplicitabouthowyouwanttochangecontrolflowinaloopstatementoraswitchstatement,asdiscussedinBreakStatementandContinueStatementbelow.

Thescopeofalabeledstatementistheentirestatementfollowingthestatementlabel.Youcannestlabeledstatements,butthenameofeachstatementlabelmust

beunique.

Formoreinformationandtoseeexamplesofhowtousestatementlabels,seeLabeledStatementsinControlFlow.

GRAMMAR OF A LABELED STATEMENT

labeled-statement → statement-label loop-statementlabeled-statement → statement-label if-statementlabeled-statement → statement-label switch-statementlabeled-statement → statement-label do-statementstatement-label → label-name :label-name → identifier

ControlTransferStatements

Controltransferstatementscanchangetheorderinwhichcodeinyourprogramisexecutedbyunconditionallytransferringprogramcontrolfromonepieceofcodetoanother.Swifthasfivecontroltransferstatements:abreakstatement,acontinuestatement,afallthroughstatement,areturnstatement,andathrowstatement.

GRAMMAR OF ACONTROLTRANSFER STATEMENT

control-transfer-statement → break-statementcontrol-transfer-statement → continue-statementcontrol-transfer-statement → fallthrough-statementcontrol-transfer-statement → return-statementcontrol-transfer-statement → throw-statement

BreakStatementAbreakstatementendsprogramexecutionofaloop,anifstatement,oraswitchstatement.Abreakstatementcanconsistofonlythebreakkeyword,oritcanconsistofthebreakkeywordfollowedbythenameofastatementlabel,asshownbelow.

break

break labelname

Whenabreakstatementisfollowedbythenameofastatementlabel,itendsprogramexecutionoftheloop,ifstatement,orswitchstatementnamedbythatlabel.

Whenabreakstatementisnotfollowedbythenameofastatementlabel,itendsprogramexecutionoftheswitchstatementortheinnermostenclosingloopstatementinwhichitoccurs.Youcan’tuseanunlabeledbreakstatementtobreakoutofanifstatement.

Inbothcases,programcontrolisthentransferredtothefirstlineofcodefollowingtheenclosinglooporswitchstatement,ifany.

Forexamplesofhowtouseabreakstatement,seeBreakandLabeledStatementsinControlFlow.

GRAMMAR OF A BREAK STATEMENT

break-statement → break label-name opt

ContinueStatementAcontinuestatementendsprogramexecutionofthecurrentiterationofaloopstatementbutdoesnotstopexecutionoftheloopstatement.Acontinuestatementcanconsistofonlythecontinuekeyword,oritcanconsistofthecontinuekeywordfollowedbythenameofastatementlabel,asshownbelow.

continue

continue labelname

Whenacontinuestatementisfollowedbythenameofastatementlabel,itendsprogramexecutionofthecurrentiterationoftheloopstatementnamedbythatlabel.

Whenacontinuestatementisnotfollowedbythenameofastatementlabel,itendsprogramexecutionofthecurrentiterationoftheinnermostenclosingloop

statementinwhichitoccurs.

Inbothcases,programcontrolisthentransferredtotheconditionoftheenclosingloopstatement.

Inaforstatement,theincrementexpressionisstillevaluatedafterthecontinuestatementisexecuted,becausetheincrementexpressionisevaluatedaftertheexecutionoftheloop’sbody.

Forexamplesofhowtouseacontinuestatement,seeContinueandLabeledStatementsinControlFlow.

GRAMMAR OF ACONT INUE STATEMENT

continue-statement → continue label-name opt

FallthroughStatementAfallthroughstatementconsistsofthefallthroughkeywordandoccursonlyinacaseblockofaswitchstatement.Afallthroughstatementcausesprogramexecutiontocontinuefromonecaseinaswitchstatementtothenextcase.Programexecutioncontinuestothenextcaseevenifthepatternsofthecaselabeldonotmatchthevalueoftheswitchstatement’scontrolexpression.

Afallthroughstatementcanappearanywhereinsideaswitchstatement,notjustasthelaststatementofacaseblock,butitcan’tbeusedinthefinalcaseblock.Italsocannottransfercontrolintoacaseblockwhosepatterncontainsvaluebindingpatterns.

Foranexampleofhowtouseafallthroughstatementinaswitchstatement,seeControlTransferStatementsinControlFlow.

GRAMMAR OF A FALLTHROUGH STATEMENT

fallthrough-statement → fallthrough

ReturnStatement

Areturnstatementoccursinthebodyofafunctionormethoddefinitionandcausesprogramexecutiontoreturntothecallingfunctionormethod.Programexecutioncontinuesatthepointimmediatelyfollowingthefunctionormethodcall.

Areturnstatementcanconsistofonlythereturnkeyword,oritcanconsistofthereturnkeywordfollowedbyanexpression,asshownbelow.

return

return expression

Whenareturnstatementisfollowedbyanexpression,thevalueoftheexpressionisreturnedtothecallingfunctionormethod.Ifthevalueoftheexpressiondoesnotmatchthevalueofthereturntypedeclaredinthefunctionormethoddeclaration,theexpression’svalueisconvertedtothereturntypebeforeitisreturnedtothecallingfunctionormethod.

NOTE

AsdescribedinFailableInitializers,aspecialformofthereturnstatement(returnnil)canbeusedinafailableinitializertoindicateinitializationfailure.

Whenareturnstatementisnotfollowedbyanexpression,itcanbeusedonlytoreturnfromafunctionormethodthatdoesnotreturnavalue(thatis,whenthereturntypeofthefunctionormethodisVoidor()).

GRAMMAR OF ARETURN STATEMENT

return-statement → return expression opt

ThrowStatementAthrowstatementoccursinthebodyofathrowingfunctionormethod,orinthebodyofaclosureexpressionwhosetypeismarkedwiththethrowskeyword.

Athrowstatementcausesaprogramtoendexecutionofthecurrentscopeandbeginerrorpropagationtoitsenclosingscope.Theerrorthat’sthrowncontinues

topropagateuntilit’shandledbyacatchclauseofadostatement.

Athrowstatementconsistsofthethrowkeywordfollowedbyanexpression,asshownbelow.

throw expression

ThevalueoftheexpressionmusthaveatypethatconformstotheErrorprotocol.

Foranexampleofhowtouseathrowstatement,seePropagatingErrorsUsingThrowingFunctionsinErrorHandling.

GRAMMAR OF ATHROWSTATEMENT

throw-statement → throw expression

DeferStatement

Adeferstatementisusedforexecutingcodejustbeforetransferringprogramcontroloutsideofthescopethatthedeferstatementappearsin.

Adeferstatementhasthefollowingform:

defer{

statements

}

Thestatementswithinthedeferstatementareexecutednomatterhowprogramcontrolistransferred.Thismeansthatadeferstatementcanbeused,forexample,toperformmanualresourcemanagementsuchasclosingfiledescriptors,andtoperformactionsthatneedtohappenevenifanerroristhrown.

Ifmultipledeferstatementsappearinthesamescope,theordertheyappearisthereverseoftheordertheyareexecuted.Executingthelastdeferstatementina

givenscopefirstmeansthatstatementsinsidethatlastdeferstatementcanrefertoresourcesthatwillbecleanedupbyotherdeferstatements.

1 funcf(){

2 defer{print("Firstdefer")}

3 defer{print("Seconddefer")}

4 print("Endoffunction")

5 }

6 f()

7 //Prints"Endoffunction"

8 //Prints"Seconddefer"

9 //Prints"Firstdefer"

Thestatementsinthedeferstatementcan’ttransferprogramcontroloutsideofthedeferstatement.

GRAMMAR OF ADEFER STATEMENT

defer-statement → defer code-block

DoStatement

Thedostatementisusedtointroduceanewscopeandcanoptionallycontainoneormorecatchclauses,whichcontainpatternsthatmatchagainstdefinederrorconditions.Variablesandconstantsdeclaredinthescopeofadostatementcanbeaccessedonlywithinthatscope.

AdostatementinSwiftissimilartocurlybraces({})inCusedtodelimitacodeblock,anddoesnotincuraperformancecostatruntime.

Adostatementhasthefollowingform:

do{

try expression

statements

}catch pattern1 {

statements

}catch pattern2 where condition {

statements

}

Likeaswitchstatement,thecompilerattemptstoinferwhethercatchclausesareexhaustive.Ifsuchadeterminationcanbemade,theerrorisconsideredhandled.Otherwise,theerrorcanpropagateoutofthecontainingscope,whichmeanstheerrormustbehandledbyanenclosingcatchclauseorthecontainingfunctionmustbedeclaredwiththrows.

Toensurethatanerrorishandled,useacatchclausewithapatternthatmatchesallerrors,suchasawildcardpattern(_).Ifacatchclausedoesnotspecifyapattern,thecatchclausematchesandbindsanyerrortoalocalconstantnamederror.Formoreinformationaboutthepatternsyoucanuseinacatchclause,seePatterns.

Toseeanexampleofhowtouseadostatementwithseveralcatchclauses,seeHandlingErrors.

GRAMMAR OF ADO STATEMENT

do-statement → do code-block catch-clauses optcatch-clauses → catch-clause catch-clauses optcatch-clause → catch pattern opt where-clause opt code-block

CompilerControlStatements

Compilercontrolstatementsallowtheprogramtochangeaspectsofthecompiler’sbehavior.Swifthasthreecompilercontrolstatements:aconditionalcompilationblockalinecontrolstatement,andacompile-timediagnosticstatement.

GRAMMAR OF ACOMP I LER CONTROLSTATEMENT

compiler-control-statement → conditional-compilation-blockcompiler-control-statement → line-control-statementcompiler-control-statement → diagnostic-statement

ConditionalCompilationBlockAconditionalcompilationblockallowscodetobeconditionallycompileddependingonthevalueofoneormorecompilationconditions.

Everyconditionalcompilationblockbeginswiththe#ifcompilationdirectiveandendswiththe#endifcompilationdirective.Asimpleconditionalcompilationblockhasthefollowingform:

#if compilationcondition

statements

#endif

Unliketheconditionofanifstatement,thecompilationconditionisevaluatedatcompiletime.Asaresult,thestatementsarecompiledandexecutedonlyifthecompilationconditionevaluatestotrueatcompiletime.

ThecompilationconditioncanincludethetrueandfalseBooleanliterals,anidentifierusedwiththe-Dcommandlineflag,oranyoftheplatformconditionslistedinthetablebelow.

Platformcondition Validarguments

os() macOS,iOS,watchOS,tvOS,Linux

arch() i386,x86_64,arm,arm64

swift() >=or<followedbyaversionnumber

compiler() >=or<followedbyaversionnumber

canImport() Amodulename

targetEnvironment() simulator,macCatalyst

Theversionnumberfortheswift()andcompiler()platformconditionsconsistsofamajornumber,optionalminornumber,optionalpatchnumber,andsoon,withadot(.)separatingeachpartoftheversionnumber.Theremustnotbewhitespacebetweenthecomparisonoperatorandtheversionnumber.Theversionforcompiler()isthecompilerversion,regardlessoftheSwiftversionsettingpassedtothecompiler.Theversionforswift()isthelanguageversioncurrentlybeingcompiled.Forexample,ifyoucompileyourcodeusingtheSwift5compilerinSwift4.2mode,thecompilerversionis5andthelanguageversionis4.2.Withthosesettings,thefollowingcodeprintsallthreemessages:

1 #ifcompiler(>=5)

2 print("CompiledwiththeSwift5compilerorlater")

3 #endif

4 #ifswift(>=4.2)

5 print("CompiledinSwift4.2modeorlater")

6 #endif

7 #ifcompiler(>=5)&&swift(<5)

8 print("CompiledwiththeSwift5compilerorlaterinaSwift

modeearlierthan5")

9 #endif

10 //Prints"CompiledwiththeSwift5compilerorlater"

11 //Prints"CompiledinSwift4.2modeorlater"

12 //Prints"CompiledwiththeSwift5compilerorlaterina

Swiftmodeearlierthan5"

TheargumentforthecanImport()platformconditionisthenameofamodulethatmaynotbepresentonallplatforms.Thisconditiontestswhetherit’spossibletoimportthemodule,butdoesn’tactuallyimportit.Ifthemoduleispresent,theplatformconditionreturnstrue;otherwise,itreturnsfalse.

ThetargetEnvironment()platformconditionreturnstruewhencodeiscompiledforasimulator;otherwise,itreturnsfalse.

NOTE

Thearch(arm)platformconditiondoesnotreturntrueforARM64devices.Thearch(i386)platformconditionreturnstruewhencodeiscompiledforthe32–bitiOSsimulator.

Youcancombinecompilationconditionsusingthelogicaloperators&&,||,and!anduseparenthesesforgrouping.TheseoperatorshavethesameassociativityandprecedenceasthelogicaloperatorsthatareusedtocombineordinaryBooleanexpressions.

Similartoanifstatement,youcanaddmultipleconditionalbranchestotestfordifferentcompilationconditions.Youcanaddanynumberofadditionalbranchesusing#elseifclauses.Youcanalsoaddafinaladditionalbranchusingan#elseclause.Conditionalcompilationblocksthatcontainmultiplebrancheshavethefollowingform:

#if compilationcondition1

statementstocompileifcompilationcondition1istrue

#elseif compilationcondition2

statementstocompileifcompilationcondition2istrue

#else

statementstocompileifbothcompilationconditionsarefalse

#endif

NOTE

Eachstatementinthebodyofaconditionalcompilationblockisparsedevenifit’snotcompiled.However,thereisanexceptionifthecompilationconditionincludesaswift()platformcondition:Thestatementsareparsedonlyifthecompiler’sversionofSwiftmatcheswhatisspecifiedinthe

platformcondition.Thisexceptionensuresthatanoldercompilerdoesn’tattempttoparsesyntaxintroducedinanewerversionofSwift.

GRAMMAR OF ACOND I T IONALCOMP I LAT ION BLOCK

conditional-compilation-block → if-directive-clause elseif-directive-clauses opt else-directive-clause opt endif-directive

if-directive-clause → if-directive compilation-condition statements optelseif-directive-clauses → elseif-directive-clause elseif-directive-clauses optelseif-directive-clause → elseif-directive compilation-condition statements optelse-directive-clause → else-directive statements optif-directive → #ifelseif-directive → #elseifelse-directive → #elseendif-directive → #endifcompilation-condition → platform-conditioncompilation-condition → identifiercompilation-condition → boolean-literalcompilation-condition → ( compilation-condition )compilation-condition → ! compilation-conditioncompilation-condition → compilation-condition && compilation-conditioncompilation-condition → compilation-condition || compilation-conditionplatform-condition → os ( operating-system )platform-condition → arch ( architecture )platform-condition → swift ( >= swift-version ) | swift ( < swift-version )platform-condition → compiler ( >= swift-version ) | compiler ( < swift-version

)

platform-condition → canImport ( module-name )platform-condition → targetEnvironment ( environment )operating-system → macOS | iOS | watchOS | tvOSarchitecture → i386 | x86_64 | arm | arm64swift-version → decimal-digits swift-version-continuation optswift-version-continuation → . decimal-digits swift-version-continuation optmodule-name → identifierenvironment → simulator

LineControlStatementAlinecontrolstatementisusedtospecifyalinenumberandfilenamethatcanbedifferentfromthelinenumberandfilenameofthesourcecodebeingcompiled.UsealinecontrolstatementtochangethesourcecodelocationusedbySwiftfordiagnosticanddebuggingpurposes.

Alinecontrolstatementhasthefollowingforms:

#sourceLocation(file: filename ,line: linenumber )

#sourceLocation()

Thefirstformofalinecontrolstatementchangesthevaluesofthe#lineand#fileliteralexpressions,beginningwiththelineofcodefollowingthelinecontrolstatement.Thelinenumberchangesthevalueof#lineandisanyintegerliteralgreaterthanzero.Thefilenamechangesthevalueof#fileandisastringliteral.

Thesecondformofalinecontrolstatement,#sourceLocation(),resetsthesourcecodelocationbacktothedefaultlinenumberingandfilename.

GRAMMAR OF A L I NE CONTROLSTATEMENT

line-control-statement → #sourceLocation ( file: file-name , line: line-number )

line-control-statement → #sourceLocation ( )line-number → Adecimalintegergreaterthanzerofile-name → static-string-literal

Compile-TimeDiagnosticStatementAcompile-timediagnosticstatementcausesthecompilertoemitanerrororawarningduringcompilation.Acompile-timediagnosticstatementhasthefollowingforms:

#error(" errormessage ")

#warning(" warningmessage ")

Thefirstformemitstheerrormessageasafatalerrorandterminatesthecompilationprocess.Thesecondformemitsthewarningmessageasanonfatalwarningandallowscompilationtoproceed.Youwritethediagnosticmessageasastaticstringliteral.Staticstringliteralscan’tusefeatureslikestringinterpolationorconcatenation,buttheycanusethemultilinestringliteralsyntax.

GRAMMAR OF ACOMP I LE - T IME D I AGNOST IC STATEMENT

diagnostic-statement → #error ( diagnostic-message )diagnostic-statement → #warning ( diagnostic-message )diagnostic-message → static-string-literal

AvailabilityCondition

Anavailabilityconditionisusedasaconditionofanif,while,andguardstatementtoquerytheavailabilityofAPIsatruntime,basedonspecifiedplatformsarguments.

Anavailabilityconditionhasthefollowingform:

if#available( platformname version , ... ,*){

statementstoexecuteiftheAPIsareavailable

}else{

fallbackstatementstoexecuteiftheAPIsareunavailable

}

Youuseanavailabilityconditiontoexecuteablockofcode,dependingonwhethertheAPIsyouwanttouseareavailableatruntime.ThecompilerusestheinformationfromtheavailabilityconditionwhenitverifiesthattheAPIsinthatblockofcodeareavailable.

Theavailabilityconditiontakesacomma-separatedlistofplatformnamesandversions.UseiOS,macOS,watchOS,andtvOSfortheplatformnames,andincludethecorrespondingversionnumbers.The*argumentisrequiredandspecifiesthatonanyotherplatform,thebodyofthecodeblockguardedbytheavailabilityconditionexecutesontheminimumdeploymenttargetspecifiedbyyourtarget.

UnlikeBooleanconditions,youcan’tcombineavailabilityconditionsusing

logicaloperatorssuchas&&and||.

GRAMMAR OF AN AVA I LAB I L I TY COND I T ION

availability-condition → #available ( availability-arguments )availability-arguments → availability-argument | availability-argument , availability-

argumentsavailability-argument → platform-name platform-versionavailability-argument → *platform-name → iOS | iOSApplicationExtensionplatform-name → macOS | macOSApplicationExtensionplatform-name → watchOSplatform-name → tvOSplatform-version → decimal-digitsplatform-version → decimal-digits . decimal-digitsplatform-version → decimal-digits . decimal-digits . decimal-digits

Declarations

Adeclarationintroducesanewnameorconstructintoyourprogram.Forexample,youusedeclarationstointroducefunctionsandmethods,tointroducevariablesandconstants,andtodefineenumeration,structure,class,andprotocoltypes.Youcanalsouseadeclarationtoextendthebehaviorofanexistingnamedtypeandtoimportsymbolsintoyourprogramthataredeclaredelsewhere.

InSwift,mostdeclarationsarealsodefinitionsinthesensethattheyareimplementedorinitializedatthesametimetheyaredeclared.Thatsaid,becauseprotocolsdon’timplementtheirmembers,mostprotocolmembersaredeclarationsonly.Forconvenienceandbecausethedistinctionisn’tthatimportantinSwift,thetermdeclarationcoversbothdeclarationsanddefinitions.

GRAMMAR OF ADECLARAT ION

declaration → import-declarationdeclaration → constant-declarationdeclaration → variable-declarationdeclaration → typealias-declarationdeclaration → function-declarationdeclaration → enum-declarationdeclaration → struct-declarationdeclaration → class-declarationdeclaration → protocol-declarationdeclaration → initializer-declarationdeclaration → deinitializer-declarationdeclaration → extension-declarationdeclaration → subscript-declarationdeclaration → operator-declarationdeclaration → precedence-group-declarationdeclarations → declaration declarations opt

Top-LevelCode

Thetop-levelcodeinaSwiftsourcefileconsistsofzeroormorestatements,declarations,andexpressions.Bydefault,variables,constants,andothernameddeclarationsthataredeclaredatthetop-levelofasourcefileareaccessibleto

codeineverysourcefilethatispartofthesamemodule.Youcanoverridethisdefaultbehaviorbymarkingthedeclarationwithanaccess-levelmodifier,asdescribedinAccessControlLevels.

GRAMMAR OF ATOP - LEVELDECLARAT ION

top-level-declaration → statements opt

CodeBlocks

Acodeblockisusedbyavarietyofdeclarationsandcontrolstructurestogroupstatementstogether.Ithasthefollowingform:

{

statements

}

Thestatementsinsideacodeblockincludedeclarations,expressions,andotherkindsofstatementsandareexecutedinorderoftheirappearanceinsourcecode.

GRAMMAR OF ACODE BLOCK

code-block → { statements opt }

ImportDeclaration

Animportdeclarationletsyouaccesssymbolsthataredeclaredoutsidethecurrentfile.Thebasicformimportstheentiremodule;itconsistsoftheimportkeywordfollowedbyamodulename:

import module

Providingmoredetaillimitswhichsymbolsareimported—youcanspecifya

specificsubmoduleoraspecificdeclarationwithinamoduleorsubmodule.Whenthisdetailedformisused,onlytheimportedsymbol(andnotthemodulethatdeclaresit)ismadeavailableinthecurrentscope.

import importkind module . symbolname

import module . submodule

GRAMMAR OF AN IMPORT DECLARAT ION

import-declaration → attributes opt import import-kind opt import-pathimport-kind → typealias | struct | class | enum | protocol | let | var |

func

import-path → import-path-identifier | import-path-identifier . import-pathimport-path-identifier → identifier | operator

ConstantDeclaration

Aconstantdeclarationintroducesaconstantnamedvalueintoyourprogram.Constantdeclarationsaredeclaredusingtheletkeywordandhavethefollowingform:

let constantname : type = expression

Aconstantdeclarationdefinesanimmutablebindingbetweentheconstantnameandthevalueoftheinitializerexpression;afterthevalueofaconstantisset,itcannotbechanged.Thatsaid,ifaconstantisinitializedwithaclassobject,theobjectitselfcanchange,butthebindingbetweentheconstantnameandtheobjectitreferstocan’t.

Whenaconstantisdeclaredatglobalscope,itmustbeinitializedwithavalue.Whenaconstantdeclarationoccursinthecontextofafunctionormethod,itcanbeinitializedlater,aslongasitisguaranteedtohaveavaluesetbeforethefirsttimeitsvalueisread.Whenaconstantdeclarationoccursinthecontextofaclassorstructuredeclaration,itisconsideredaconstantproperty.Constantdeclarationsarenotcomputedpropertiesandthereforedonothavegettersorsetters.

Iftheconstantnameofaconstantdeclarationisatuplepattern,thenameofeachiteminthetupleisboundtothecorrespondingvalueintheinitializerexpression.

let(firstNumber,secondNumber)=(10,42)

Inthisexample,firstNumberisanamedconstantforthevalue10,andsecondNumberisanamedconstantforthevalue42.Bothconstantscannowbeusedindependently:

1 print("Thefirstnumberis\(firstNumber).")

2 //Prints"Thefirstnumberis10."

3 print("Thesecondnumberis\(secondNumber).")

4 //Prints"Thesecondnumberis42."

Thetypeannotation(:type)isoptionalinaconstantdeclarationwhenthetypeoftheconstantnamecanbeinferred,asdescribedinTypeInference.

Todeclareaconstanttypeproperty,markthedeclarationwiththestaticdeclarationmodifier.Aconstanttypepropertyofaclassisalwaysimplicitlyfinal;youcan’tmarkitwiththeclassorfinaldeclarationmodifiertoallowordisallowoverridingbysubclasses.TypepropertiesarediscussedinTypeProperties.

Formoreinformationaboutconstantsandforguidanceaboutwhentousethem,seeConstantsandVariablesandStoredProperties.

GRAMMAR OF ACONSTANT DECLARAT ION

constant-declaration → attributes opt declaration-modifiers opt let pattern-initializer-list

pattern-initializer-list → pattern-initializer | pattern-initializer , pattern-initializer-listpattern-initializer → pattern initializer optinitializer → = expression

VariableDeclaration

Avariabledeclarationintroducesavariablenamedvalueintoyourprogramandisdeclaredusingthevarkeyword.

Variabledeclarationshaveseveralformsthatdeclaredifferentkindsofnamed,mutablevalues,includingstoredandcomputedvariablesandproperties,storedvariableandpropertyobservers,andstaticvariableproperties.Theappropriateformtousedependsonthescopeatwhichthevariableisdeclaredandthekindofvariableyouintendtodeclare.

NOTE

Youcanalsodeclarepropertiesinthecontextofaprotocoldeclaration,asdescribedinProtocolPropertyDeclaration.

Youcanoverrideapropertyinasubclassbymarkingthesubclass’spropertydeclarationwiththeoverridedeclarationmodifier,asdescribedinOverriding.

StoredVariablesandStoredVariablePropertiesThefollowingformdeclaresastoredvariableorstoredvariableproperty:

var variablename : type = expression

Youdefinethisformofavariabledeclarationatglobalscope,thelocalscopeofafunction,orinthecontextofaclassorstructuredeclaration.Whenavariabledeclarationofthisformisdeclaredatglobalscopeorthelocalscopeofafunction,itisreferredtoasastoredvariable.Whenitisdeclaredinthecontextofaclassorstructuredeclaration,itisreferredtoasastoredvariableproperty.

Theinitializerexpressioncan’tbepresentinaprotocoldeclaration,butinallothercontexts,theinitializerexpressionisoptional.Thatsaid,ifnoinitializerexpressionispresent,thevariabledeclarationmustincludeanexplicittypeannotation(:type).

Aswithconstantdeclarations,ifthevariablenameisatuplepattern,thenameofeachiteminthetupleisboundtothecorrespondingvalueintheinitializerexpression.

Astheirnamessuggest,thevalueofastoredvariableorastoredvariablepropertyisstoredinmemory.

ComputedVariablesandComputedPropertiesThefollowingformdeclaresacomputedvariableorcomputedproperty:

var variablename : type {

get{

statements

}

set( settername ){

statements

}

}

Youdefinethisformofavariabledeclarationatglobalscope,thelocalscopeofafunction,orinthecontextofaclass,structure,enumeration,orextensiondeclaration.Whenavariabledeclarationofthisformisdeclaredatglobalscopeorthelocalscopeofafunction,itisreferredtoasacomputedvariable.Whenitisdeclaredinthecontextofaclass,structure,orextensiondeclaration,itisreferredtoasacomputedproperty.

Thegetterisusedtoreadthevalue,andthesetterisusedtowritethevalue.Thesetterclauseisoptional,andwhenonlyagetterisneeded,youcanomitbothclausesandsimplyreturntherequestedvaluedirectly,asdescribedinRead-OnlyComputedProperties.Butifyouprovideasetterclause,youmustalsoprovideagetterclause.

Thesetternameandenclosingparenthesesisoptional.Ifyouprovideasettername,itisusedasthenameoftheparametertothesetter.Ifyoudonotprovideasettername,thedefaultparameternametothesetterisnewValue,asdescribedinShorthandSetterDeclaration.

Unlikestorednamedvaluesandstoredvariableproperties,thevalueofa

computednamedvalueoracomputedpropertyisnotstoredinmemory.

Formoreinformationandtoseeexamplesofcomputedproperties,seeComputedProperties.

StoredVariableObserversandPropertyObserversYoucanalsodeclareastoredvariableorpropertywithwillSetanddidSetobservers.Astoredvariableorpropertydeclaredwithobservershasthefollowingform:

var variablename : type = expression {

willSet( settername ){

statements

}

didSet( settername ){

statements

}

}

Youdefinethisformofavariabledeclarationatglobalscope,thelocalscopeofafunction,orinthecontextofaclassorstructuredeclaration.Whenavariabledeclarationofthisformisdeclaredatglobalscopeorthelocalscopeofafunction,theobserversarereferredtoasstoredvariableobservers.Whenitisdeclaredinthecontextofaclassorstructuredeclaration,theobserversarereferredtoaspropertyobservers.

Youcanaddpropertyobserverstoanystoredproperty.Youcanalsoaddpropertyobserverstoanyinheritedproperty(whetherstoredorcomputed)byoverridingthepropertywithinasubclass,asdescribedinOverridingPropertyObservers.

Theinitializerexpressionisoptionalinthecontextofaclassorstructuredeclaration,butrequiredelsewhere.Thetypeannotationisoptionalwhenthetypecanbeinferredfromtheinitializerexpression.

ThewillSetanddidSetobserversprovideawaytoobserve(andtorespondappropriately)whenthevalueofavariableorpropertyisbeingset.Theobserversarenotcalledwhenthevariableorpropertyisfirstinitialized.Instead,theyarecalledonlywhenthevalueissetoutsideofaninitializationcontext.

AwillSetobserveriscalledjustbeforethevalueofthevariableorpropertyisset.ThenewvalueispassedtothewillSetobserverasaconstant,andthereforeitcan’tbechangedintheimplementationofthewillSetclause.ThedidSetobserveriscalledimmediatelyafterthenewvalueisset.IncontrasttothewillSetobserver,theoldvalueofthevariableorpropertyispassedtothedidSetobserverincaseyoustillneedaccesstoit.Thatsaid,ifyouassignavaluetoavariableorpropertywithinitsowndidSetobserverclause,thatnewvaluethatyouassignwillreplacetheonethatwasjustsetandpassedtothewillSetobserver.

ThesetternameandenclosingparenthesesinthewillSetanddidSetclausesareoptional.Ifyouprovidesetternames,theyareusedastheparameternamestothewillSetanddidSetobservers.Ifyoudonotprovidesetternames,thedefaultparameternametothewillSetobserverisnewValueandthedefaultparameternametothedidSetobserverisoldValue.

ThedidSetclauseisoptionalwhenyouprovideawillSetclause.Likewise,thewillSetclauseisoptionalwhenyouprovideadidSetclause.

Formoreinformationandtoseeanexampleofhowtousepropertyobservers,seePropertyObservers.

TypeVariablePropertiesTodeclareatypevariableproperty,markthedeclarationwiththestaticdeclarationmodifier.Classescanmarktypecomputedpropertieswiththeclassdeclarationmodifierinsteadtoallowsubclassestooverridethesuperclass’simplementation.TypepropertiesarediscussedinTypeProperties.

GRAMMAR OF A VAR IABLE DECLARAT ION

variable-declaration → variable-declaration-head pattern-initializer-listvariable-declaration → variable-declaration-head variable-name type-annotation code-

blockvariable-declaration → variable-declaration-head variable-name type-annotation getter-

setter-blockvariable-declaration → variable-declaration-head variable-name type-annotation getter-

setter-keyword-blockvariable-declaration → variable-declaration-head variable-name initializer willSet-didSet-

blockvariable-declaration → variable-declaration-head variable-name type-annotation

initializer opt willSet-didSet-blockvariable-declaration-head → attributes opt declaration-modifiers opt varvariable-name → identifiergetter-setter-block → code-blockgetter-setter-block → { getter-clause setter-clause opt }getter-setter-block → { setter-clause getter-clause }getter-clause → attributes opt mutation-modifier opt get code-blocksetter-clause → attributes opt mutation-modifier opt set setter-name opt code-blocksetter-name → ( identifier )getter-setter-keyword-block → { getter-keyword-clause setter-keyword-clause opt }getter-setter-keyword-block → { setter-keyword-clause getter-keyword-clause }getter-keyword-clause → attributes opt mutation-modifier opt getsetter-keyword-clause → attributes opt mutation-modifier opt setwillSet-didSet-block → { willSet-clause didSet-clause opt }willSet-didSet-block → { didSet-clause willSet-clause opt }willSet-clause → attributes opt willSet setter-name opt code-blockdidSet-clause → attributes opt didSet setter-name opt code-block

TypeAliasDeclaration

Atypealiasdeclarationintroducesanamedaliasofanexistingtypeintoyourprogram.Typealiasdeclarationsaredeclaredusingthetypealiaskeywordandhavethefollowingform:

typealias name = existingtype

Afteratypealiasisdeclared,thealiasednamecanbeusedinsteadoftheexistingtypeeverywhereinyourprogram.Theexistingtypecanbeanamedtypeoracompoundtype.Typealiasesdonotcreatenewtypes;theysimplyallowanametorefertoanexistingtype.

Atypealiasdeclarationcanusegenericparameterstogiveanametoanexistinggenerictype.Thetypealiascanprovideconcretetypesforsomeorallofthegenericparametersoftheexistingtype.Forexample:

1 typealiasStringDictionary<Value>=Dictionary<String,Value>

2

3 //Thefollowingdictionarieshavethesametype.

4 vardictionary1:StringDictionary<Int>=[:]

5 vardictionary2:Dictionary<String,Int>=[:]

Whenatypealiasisdeclaredwithgenericparameters,theconstraintsonthoseparametersmustmatchexactlytheconstraintsontheexistingtype’sgenericparameters.Forexample:

typealiasDictionaryOfInts<Key:Hashable>=Dictionary<Key,

Int>

Becausethetypealiasandtheexistingtypecanbeusedinterchangeably,thetypealiascan’tintroduceadditionalgenericconstraints.

Atypealiascanforwardanexistingtype’sgenericparametersbyomittingallgenericparametersfromthedeclaration.Forexample,theDiccionariotypealiasdeclaredherehasthesamegenericparametersandconstraintsasDictionary.

typealiasDiccionario=Dictionary

Insideaprotocoldeclaration,atypealiascangiveashorterandmoreconvenientnametoatypethatisusedfrequently.Forexample:

1 protocolSequence{

2 associatedtypeIterator:IteratorProtocol

3 typealiasElement=Iterator.Element

4 }

5

6 funcsum<T:Sequence>(_sequence:T)->IntwhereT.Element==

Int{

7 //...

8 }

Withoutthistypealias,thesumfunctionwouldhavetorefertotheassociatedtypeasT.Iterator.ElementinsteadofT.Element.

SeealsoProtocolAssociatedTypeDeclaration.

GRAMMAR OF ATYPE AL I AS DECLARAT ION

typealias-declaration → attributes opt access-level-modifier opt typealias typealias-name generic-parameter-clause opt typealias-assignment

typealias-name → identifiertypealias-assignment → = type

FunctionDeclaration

Afunctiondeclarationintroducesafunctionormethodintoyourprogram.Afunctiondeclaredinthecontextofclass,structure,enumeration,orprotocolisreferredtoasamethod.Functiondeclarationsaredeclaredusingthefunckeywordandhavethefollowingform:

func functionname ( parameters )-> returntype {

statements

}

IfthefunctionhasareturntypeofVoid,thereturntypecanbeomittedasfollows:

func functionname ( parameters ){

statements

}

Thetypeofeachparametermustbeincluded—itcan’tbeinferred.Ifyouwriteinoutinfrontofaparameter’stype,theparametercanbemodifiedinsidethescopeofthefunction.In-outparametersarediscussedindetailinIn-OutParameters,below.

Afunctiondeclarationwhosestatementsincludeonlyasingleexpressionisunderstoodtoreturnthevalueofthatexpression.

Functionscanreturnmultiplevaluesusingatupletypeasthereturntypeofthefunction.

Afunctiondefinitioncanappearinsideanotherfunctiondeclaration.Thiskindoffunctionisknownasanestedfunction.

Anestedfunctionisnonescapingifitcapturesavaluethatisguaranteedtoneverescape—suchasanin-outparameter—orpassedasanonescapingfunctionargument.Otherwise,thenestedfunctionisanescapingfunction.

Foradiscussionofnestedfunctions,seeNestedFunctions.

ParameterNamesFunctionparametersareacomma-separatedlistwhereeachparameterhasoneofseveralforms.Theorderofargumentsinafunctioncallmustmatchtheorderofparametersinthefunction’sdeclaration.Thesimplestentryinaparameterlisthasthefollowingform:

parametername : parametertype

Aparameterhasaname,whichisusedwithinthefunctionbody,aswellasanargumentlabel,whichisusedwhencallingthefunctionormethod.Bydefault,parameternamesarealsousedasargumentlabels.Forexample:

1 funcf(x:Int,y:Int)->Int{returnx+y}

2 f(x:1,y:2)//bothxandyarelabeled

Youcanoverridethedefaultbehaviorforargumentlabelswithoneofthefollowingforms:

argumentlabel parametername : parametertype

_ parametername : parametertype

Anamebeforetheparameternamegivestheparameteranexplicitargumentlabel,whichcanbedifferentfromtheparametername.Thecorrespondingargumentmustusethegivenargumentlabelinfunctionormethodcalls.

Anunderscore(_)beforeaparameternamesuppressestheargumentlabel.Thecorrespondingargumentmusthavenolabelinfunctionormethodcalls.

1 funcrepeatGreeting(_greeting:String,countn:Int){/*

Greetntimes*/}

2 repeatGreeting("Hello,world!",count:2)//countislabeled,

greetingisnot

In-OutParametersIn-outparametersarepassedasfollows:

1. Whenthefunctioniscalled,thevalueoftheargumentiscopied.

2. Inthebodyofthefunction,thecopyismodified.

3. Whenthefunctionreturns,thecopy’svalueisassignedtotheoriginalargument.

Thisbehaviorisknownascopy-incopy-outorcallbyvalueresult.Forexample,whenacomputedpropertyorapropertywithobserversispassedasanin-outparameter,itsgetteriscalledaspartofthefunctioncallanditssetteriscalledaspartofthefunctionreturn.

Asanoptimization,whentheargumentisavaluestoredataphysicaladdressinmemory,thesamememorylocationisusedbothinsideandoutsidethefunction

body.Theoptimizedbehaviorisknownascallbyreference;itsatisfiesalloftherequirementsofthecopy-incopy-outmodelwhileremovingtheoverheadofcopying.Writeyourcodeusingthemodelgivenbycopy-incopy-out,withoutdependingonthecall-by-referenceoptimization,sothatitbehavescorrectlywithorwithouttheoptimization.

Withinafunction,don’taccessavaluethatwaspassedasanin-outargument,eveniftheoriginalvalueisavailableinthecurrentscope.Accessingtheoriginalisasimultaneousaccessofthevalue,whichviolatesSwift’smemoryexclusivityguarantee.Forthesamereason,youcan’tpassthesamevaluetomultiplein-outparameters.

Formoreinformationaboutmemorysafetyandmemoryexclusivity,seeMemorySafety.

Aclosureornestedfunctionthatcapturesanin-outparametermustbenonescaping.Ifyouneedtocaptureanin-outparameterwithoutmutatingitortoobservechangesmadebyothercode,useacapturelisttoexplicitlycapturetheparameterimmutably.

1 funcsomeFunction(a:inoutInt)->()->Int{

2 return{[a]inreturna+1}

3 }

Ifyouneedtocaptureandmutateanin-outparameter,useanexplicitlocalcopy,suchasinmultithreadedcodethatensuresallmutationhasfinishedbeforethefunctionreturns.

1 funcmultithreadedFunction(queue:DispatchQueue,x:inoutInt)

{

2 //Makealocalcopyandmanuallycopyitback.

3 varlocalX=x

4 defer{x=localX}

5

6 //OperateonlocalXasynchronously,thenwaitbefore

returning.

7 queue.async{someMutatingOperation(&localX)}

8 queue.sync{}

9 }

Formorediscussionandexamplesofin-outparameters,seeIn-OutParameters.

SpecialKindsofParametersParameterscanbeignored,takeavariablenumberofvalues,andprovidedefaultvaluesusingthefollowingforms:

_: parametertype

parametername : parametertype ...

parametername : parametertype = defaultargumentvalue

Anunderscore(_)parameterisexplicitlyignoredandcan’tbeaccessedwithinthebodyofthefunction.

Aparameterwithabasetypenamefollowedimmediatelybythreedots(...)isunderstoodasavariadicparameter.Afunctioncanhaveatmostonevariadicparameter.Avariadicparameteristreatedasanarraythatcontainselementsofthebasetypename.Forexample,thevariadicparameterInt...istreatedas[Int].Foranexamplethatusesavariadicparameter,seeVariadicParameters.

Aparameterwithanequalssign(=)andanexpressionafteritstypeisunderstoodtohaveadefaultvalueofthegivenexpression.Thegivenexpressionisevaluatedwhenthefunctioniscalled.Iftheparameterisomittedwhencallingthefunction,thedefaultvalueisusedinstead.

1 funcf(x:Int=42)->Int{returnx}

2 f()//Valid,usesdefaultvalue

3 f(x:7)//Valid,usesthevalueprovided

4 f(7)//Invalid,missingargumentlabel

SpecialKindsofMethodsMethodsonanenumerationorastructurethatmodifyselfmustbemarkedwiththemutatingdeclarationmodifier.

Methodsthatoverrideasuperclassmethodmustbemarkedwiththeoverridedeclarationmodifier.It’sacompile-timeerrortooverrideamethodwithouttheoverridemodifierortousetheoverridemodifieronamethodthatdoesn’toverrideasuperclassmethod.

Methodsassociatedwithatyperatherthananinstanceofatypemustbemarkedwiththestaticdeclarationmodifierforenumerationsandstructures,orwitheitherthestaticorclassdeclarationmodifierforclasses.Aclasstypemethodmarkedwiththeclassdeclarationmodifiercanbeoverriddenbyasubclassimplementation;aclasstypemethodmarkedwithclassfinalorstaticcan’tbeoverridden.

ThrowingFunctionsandMethodsFunctionsandmethodsthatcanthrowanerrormustbemarkedwiththethrowskeyword.Thesefunctionsandmethodsareknownasthrowingfunctionsandthrowingmethods.Theyhavethefollowingform:

func functionname ( parameters )throws-> returntype {

statements

}

Callstoathrowingfunctionormethodmustbewrappedinatryortry!expression(thatis,inthescopeofatryortry!operator).

Thethrowskeywordispartofafunction’stype,andnonthrowingfunctionsaresubtypesofthrowingfunctions.Asaresult,youcanuseanonthrowingfunctioninthesameplacesasathrowingone.

Youcan’toverloadafunctionbasedonlyonwhetherthefunctioncanthrowanerror.Thatsaid,youcanoverloadafunctionbasedonwhetherafunctionparametercanthrowanerror.

Athrowingmethodcan’toverrideanonthrowingmethod,andathrowingmethodcan’tsatisfyaprotocolrequirementforanonthrowingmethod.Thatsaid,anonthrowingmethodcanoverrideathrowingmethod,andanonthrowingmethodcansatisfyaprotocolrequirementforathrowingmethod.

RethrowingFunctionsandMethodsAfunctionormethodcanbedeclaredwiththerethrowskeywordtoindicatethatitthrowsanerroronlyifoneofitsfunctionparametersthrowsanerror.Thesefunctionsandmethodsareknownasrethrowingfunctionsandrethrowingmethods.Rethrowingfunctionsandmethodsmusthaveatleastonethrowingfunctionparameter.

1 funcsomeFunction(callback:()throws->Void)rethrows{

2 trycallback()

3 }

Arethrowingfunctionormethodcancontainathrowstatementonlyinsideacatchclause.Thisletsyoucallthethrowingfunctioninsideado-catchblockandhandleerrorsinthecatchclausebythrowingadifferenterror.Inaddition,thecatchclausemusthandleonlyerrorsthrownbyoneoftherethrowingfunction’sthrowingparameters.Forexample,thefollowingisinvalidbecausethecatchclausewouldhandletheerrorthrownbyalwaysThrows().

1 funcalwaysThrows()throws{

2 throwSomeError.error

3 }

4 funcsomeFunction(callback:()throws->Void)rethrows{

5 do{

6 trycallback()

7 tryalwaysThrows()//Invalid,alwaysThrows()isn'ta

throwingparameter

8 }catch{

9 throwAnotherError.error

10 }

11 }

Athrowingmethodcan’toverridearethrowingmethod,andathrowingmethodcan’tsatisfyaprotocolrequirementforarethrowingmethod.Thatsaid,arethrowingmethodcanoverrideathrowingmethod,andarethrowingmethodcansatisfyaprotocolrequirementforathrowingmethod.

FunctionsthatNeverReturnSwiftdefinesaNevertype,whichindicatesthatafunctionormethoddoesn’treturntoitscaller.FunctionsandmethodswiththeNeverreturntypearecallednonreturning.Nonreturningfunctionsandmethodseithercauseanirrecoverableerrororbeginasequenceofworkthatcontinuesindefinitely.Thismeansthatcodethatwouldotherwiserunimmediatelyafterthecallisneverexecuted.Throwingandrethrowingfunctionscantransferprogramcontroltoanappropriatecatchblock,evenwhentheyarenonreturning.

Anonreturningfunctionormethodcanbecalledtoconcludetheelseclauseofaguardstatement,asdiscussedinGuardStatement.

Youcanoverrideanonreturningmethod,butthenewmethodmustpreserveitsreturntypeandnonreturningbehavior.

GRAMMAR OF A FUNCT ION DECLARAT ION

function-declaration → function-head function-name generic-parameter-clause optfunction-signature generic-where-clause opt function-body opt

function-head → attributes opt declaration-modifiers opt funcfunction-name → identifier | operatorfunction-signature → parameter-clause throwsopt function-result optfunction-signature → parameter-clause rethrows function-result optfunction-result → -> attributes opt typefunction-body → code-blockparameter-clause → ( ) | ( parameter-list )parameter-list → parameter | parameter , parameter-listparameter → external-parameter-name opt local-parameter-name type-annotation

default-argument-clause optparameter → external-parameter-name opt local-parameter-name type-annotationparameter → external-parameter-name opt local-parameter-name type-annotation ...external-parameter-name → identifierlocal-parameter-name → identifierdefault-argument-clause → = expression

EnumerationDeclaration

Anenumerationdeclarationintroducesanamedenumerationtypeintoyourprogram.

Enumerationdeclarationshavetwobasicformsandaredeclaredusingtheenumkeyword.Thebodyofanenumerationdeclaredusingeitherformcontainszeroormorevalues—calledenumerationcases—andanynumberofdeclarations,includingcomputedproperties,instancemethods,typemethods,initializers,typealiases,andevenotherenumeration,structure,andclassdeclarations.Enumerationdeclarationscan’tcontaindeinitializerorprotocoldeclarations.

Enumerationtypescanadoptanynumberofprotocols,butcan’tinheritfromclasses,structures,orotherenumerations.

Unlikeclassesandstructures,enumerationtypesdonothaveanimplicitlyprovideddefaultinitializer;allinitializersmustbedeclaredexplicitly.Initializerscandelegatetootherinitializersintheenumeration,buttheinitializationprocessiscompleteonlyafteraninitializerassignsoneoftheenumerationcasestoself.

Likestructuresbutunlikeclasses,enumerationsarevaluetypes;instancesofanenumerationarecopiedwhenassignedtovariablesorconstants,orwhenpassedasargumentstoafunctioncall.Forinformationaboutvaluetypes,seeStructuresandEnumerationsAreValueTypes.

Youcanextendthebehaviorofanenumerationtypewithanextensiondeclaration,asdiscussedinExtensionDeclaration.

EnumerationswithCasesofAnyTypeThefollowingformdeclaresanenumerationtypethatcontainsenumerationcasesofanytype:

enum enumerationname : adoptedprotocols {

case enumerationcase1

case enumerationcase2 ( associatedvaluetypes )

}

Enumerationsdeclaredinthisformaresometimescalleddiscriminatedunionsinotherprogramminglanguages.

Inthisform,eachcaseblockconsistsofthecasekeywordfollowedbyoneormoreenumerationcases,separatedbycommas.Thenameofeachcasemustbeunique.Eachcasecanalsospecifythatitstoresvaluesofagiventype.Thesetypesarespecifiedintheassociatedvaluetypestuple,immediatelyfollowingthenameofthecase.

Enumerationcasesthatstoreassociatedvaluescanbeusedasfunctionsthatcreateinstancesoftheenumerationwiththespecifiedassociatedvalues.Andjustlikefunctions,youcangetareferencetoanenumerationcaseandapplyitlaterinyourcode.

1 enumNumber{

2 caseinteger(Int)

3 casereal(Double)

4 }

5 letf=Number.integer

6 //fisafunctionoftype(Int)->Number

7

8 //ApplyftocreateanarrayofNumberinstanceswithinteger

values

9 letevenInts:[Number]=[0,2,4,6].map(f)

Formoreinformationandtoseeexamplesofcaseswithassociatedvaluetypes,seeAssociatedValues.

EnumerationswithIndirection

Enumerationscanhavearecursivestructure,thatis,theycanhavecaseswithassociatedvaluesthatareinstancesoftheenumerationtypeitself.However,instancesofenumerationtypeshavevaluesemantics,whichmeanstheyhaveafixedlayoutinmemory.Tosupportrecursion,thecompilermustinsertalayerofindirection.

Toenableindirectionforaparticularenumerationcase,markitwiththeindirectdeclarationmodifier.Anindirectcasemusthaveanassociatedvalue.

1 enumTree<T>{

2 caseempty

3 indirectcasenode(value:T,left:Tree,right:Tree)

4 }

Toenableindirectionforallthecasesofanenumerationthathaveanassociatedvalue,marktheentireenumerationwiththeindirectmodifier—thisisconvenientwhentheenumerationcontainsmanycasesthatwouldeachneedtobemarkedwiththeindirectmodifier.

Anenumerationthatismarkedwiththeindirectmodifiercancontainamixtureofcasesthathaveassociatedvaluesandcasesthosethatdon’t.Thatsaid,itcan’tcontainanycasesthatarealsomarkedwiththeindirectmodifier.

EnumerationswithCasesofaRaw-ValueTypeThefollowingformdeclaresanenumerationtypethatcontainsenumerationcasesofthesamebasictype:

enum enumerationname : raw-valuetype , adoptedprotocols {

case enumerationcase1 = rawvalue1

case enumerationcase2 = rawvalue2

}

Inthisform,eachcaseblockconsistsofthecasekeyword,followedbyoneormoreenumerationcases,separatedbycommas.Unlikethecasesinthefirstform,eachcasehasanunderlyingvalue,calledarawvalue,ofthesamebasictype.Thetypeofthesevaluesisspecifiedintheraw-valuetypeandmustrepresentaninteger,floating-pointnumber,string,orsinglecharacter.Inparticular,theraw-valuetypemustconformtotheEquatableprotocolandoneofthefollowingprotocols:ExpressibleByIntegerLiteralforintegerliterals,ExpressibleByFloatLiteralforfloating-pointliterals,ExpressibleByStringLiteralforstringliteralsthatcontainanynumberofcharacters,andExpressibleByUnicodeScalarLiteralorExpressibleByExtendedGraphemeClusterLiteralforstringliteralsthatcontainonlyasinglecharacter.Eachcasemusthaveauniquenameandbeassignedauniquerawvalue.

Iftheraw-valuetypeisspecifiedasIntandyoudon’tassignavaluetothecasesexplicitly,theyareimplicitlyassignedthevalues0,1,2,andsoon.EachunassignedcaseoftypeIntisimplicitlyassignedarawvaluethatisautomaticallyincrementedfromtherawvalueofthepreviouscase.

1 enumExampleEnum:Int{

2 casea,b,c=5,d

3 }

Intheaboveexample,therawvalueofExampleEnum.ais0andthevalueofExampleEnum.bis1.AndbecausethevalueofExampleEnum.cisexplicitlysetto5,thevalueofExampleEnum.disautomaticallyincrementedfrom5andistherefore6.

Iftheraw-valuetypeisspecifiedasStringandyoudon’tassignvaluestothecasesexplicitly,eachunassignedcaseisimplicitlyassignedastringwiththesametextasthenameofthatcase.

1 enumGamePlayMode:String{

2 casecooperative,individual,competitive

3 }

Intheaboveexample,therawvalueofGamePlayMode.cooperativeis"cooperative",therawvalueofGamePlayMode.individualis"individual",andtherawvalueofGamePlayMode.competitiveis"competitive".

Enumerationsthathavecasesofaraw-valuetypeimplicitlyconformtotheRawRepresentableprotocol,definedintheSwiftstandardlibrary.Asaresult,theyhavearawValuepropertyandafailableinitializerwiththesignatureinit?(rawValue:RawValue).YoucanusetherawValuepropertytoaccesstherawvalueofanenumerationcase,asinExampleEnum.b.rawValue.Youcanalsousearawvaluetofindacorrespondingcase,ifthereisone,bycallingtheenumeration’sfailableinitializer,asinExampleEnum(rawValue:5),whichreturnsanoptionalcase.Formoreinformationandtoseeexamplesofcaseswithraw-valuetypes,seeRawValues.

AccessingEnumerationCasesToreferencethecaseofanenumerationtype,usedot(.)syntax,asinEnumerationType.enumerationCase.Whentheenumerationtypecanbeinferredfromcontext,youcanomitit(thedotisstillrequired),asdescribedinEnumerationSyntaxandImplicitMemberExpression.

Tocheckthevaluesofenumerationcases,useaswitchstatement,asshowninMatchingEnumerationValueswithaSwitchStatement.Theenumerationtypeispattern-matchedagainsttheenumerationcasepatternsinthecaseblocksoftheswitchstatement,asdescribedinEnumerationCasePattern.

GRAMMAR OF AN ENUMERAT ION DECLARAT ION

enum-declaration → attributes opt access-level-modifier opt union-style-enumenum-declaration → attributes opt access-level-modifier opt raw-value-style-enumunion-style-enum → indirectopt enum enum-name generic-parameter-clause opt

type-inheritance-clause opt generic-where-clause opt { union-style-enum-members

opt }union-style-enum-members → union-style-enum-member union-style-enum-members optunion-style-enum-member → declaration | union-style-enum-case-clause | compiler-

control-statementunion-style-enum-case-clause → attributes opt indirectopt case union-style-enum-

case-listunion-style-enum-case-list → union-style-enum-case | union-style-enum-case , union-

style-enum-case-listunion-style-enum-case → enum-case-name tuple-type optenum-name → identifierenum-case-name → identifierraw-value-style-enum → enum enum-name generic-parameter-clause opt type-

inheritance-clause generic-where-clause opt { raw-value-style-enum-members }raw-value-style-enum-members → raw-value-style-enum-member raw-value-style-enum-

members optraw-value-style-enum-member → declaration | raw-value-style-enum-case-clause |

compiler-control-statementraw-value-style-enum-case-clause → attributes opt case raw-value-style-enum-case-

listraw-value-style-enum-case-list → raw-value-style-enum-case | raw-value-style-enum-

case , raw-value-style-enum-case-listraw-value-style-enum-case → enum-case-name raw-value-assignment optraw-value-assignment → = raw-value-literalraw-value-literal → numeric-literal | static-string-literal | boolean-literal

StructureDeclaration

Astructuredeclarationintroducesanamedstructuretypeintoyourprogram.Structuredeclarationsaredeclaredusingthestructkeywordandhavethefollowingform:

struct structurename : adoptedprotocols {

declarations

}

Thebodyofastructurecontainszeroormoredeclarations.Thesedeclarationscanincludebothstoredandcomputedproperties,typeproperties,instancemethods,typemethods,initializers,subscripts,typealiases,andevenotherstructure,class,andenumerationdeclarations.Structuredeclarationscan’tcontaindeinitializerorprotocoldeclarations.Foradiscussionandseveralexamplesofstructuresthatincludevariouskindsofdeclarations,seeStructures

andClasses.

Structuretypescanadoptanynumberofprotocols,butcan’tinheritfromclasses,enumerations,orotherstructures.

Therearethreewaystocreateaninstanceofapreviouslydeclaredstructure:

Calloneoftheinitializersdeclaredwithinthestructure,asdescribedinInitializers.

Ifnoinitializersaredeclared,callthestructure’smemberwiseinitializer,asdescribedinMemberwiseInitializersforStructureTypes.

Ifnoinitializersaredeclared,andallpropertiesofthestructuredeclarationweregiveninitialvalues,callthestructure’sdefaultinitializer,asdescribedinDefaultInitializers.

Theprocessofinitializingastructure’sdeclaredpropertiesisdescribedinInitialization.

Propertiesofastructureinstancecanbeaccessedusingdot(.)syntax,asdescribedinAccessingProperties.

Structuresarevaluetypes;instancesofastructurearecopiedwhenassignedtovariablesorconstants,orwhenpassedasargumentstoafunctioncall.Forinformationaboutvaluetypes,seeStructuresandEnumerationsAreValueTypes.

Youcanextendthebehaviorofastructuretypewithanextensiondeclaration,asdiscussedinExtensionDeclaration.

GRAMMAR OF A STRUCTURE DECLARAT ION

struct-declaration → attributes opt access-level-modifier opt struct struct-namegeneric-parameter-clause opt type-inheritance-clause opt generic-where-clause optstruct-body

struct-name → identifierstruct-body → { struct-members opt }struct-members → struct-member struct-members optstruct-member → declaration | compiler-control-statement

ClassDeclaration

Aclassdeclarationintroducesanamedclasstypeintoyourprogram.Classdeclarationsaredeclaredusingtheclasskeywordandhavethefollowingform:

class classname : superclass , adoptedprotocols {

declarations

}

Thebodyofaclasscontainszeroormoredeclarations.Thesedeclarationscanincludebothstoredandcomputedproperties,instancemethods,typemethods,initializers,asingledeinitializer,subscripts,typealiases,andevenotherclass,structure,andenumerationdeclarations.Classdeclarationscan’tcontainprotocoldeclarations.Foradiscussionandseveralexamplesofclassesthatincludevariouskindsofdeclarations,seeStructuresandClasses.

Aclasstypecaninheritfromonlyoneparentclass,itssuperclass,butcanadoptanynumberofprotocols.Thesuperclassappearsfirstaftertheclassnameandcolon,followedbyanyadoptedprotocols.Genericclassescaninheritfromothergenericandnongenericclasses,butanongenericclasscaninheritonlyfromothernongenericclasses.Whenyouwritethenameofagenericsuperclassclassafterthecolon,youmustincludethefullnameofthatgenericclass,includingitsgenericparameterclause.

AsdiscussedinInitializerDeclaration,classescanhavedesignatedandconvenienceinitializers.Thedesignatedinitializerofaclassmustinitializealloftheclass’sdeclaredpropertiesanditmustdosobeforecallinganyofitssuperclass’sdesignatedinitializers.

Aclasscanoverrideproperties,methods,subscripts,andinitializersofitssuperclass.Overriddenproperties,methods,subscripts,anddesignatedinitializersmustbemarkedwiththeoverridedeclarationmodifier.

Torequirethatsubclassesimplementasuperclass’sinitializer,markthesuperclass’sinitializerwiththerequireddeclarationmodifier.Thesubclass’simplementationofthatinitializermustalsobemarkedwiththerequireddeclarationmodifier.

Althoughpropertiesandmethodsdeclaredinthesuperclassareinheritedbythecurrentclass,designatedinitializersdeclaredinthesuperclassareonlyinheritedwhenthesubclassmeetstheconditionsdescribedinAutomaticInitializerInheritance.Swiftclassesdonotinheritfromauniversalbaseclass.

Therearetwowaystocreateaninstanceofapreviouslydeclaredclass:

Calloneoftheinitializersdeclaredwithintheclass,asdescribedinInitializers.

Ifnoinitializersaredeclared,andallpropertiesoftheclassdeclarationweregiveninitialvalues,calltheclass’sdefaultinitializer,asdescribedinDefaultInitializers.

Accesspropertiesofaclassinstancewithdot(.)syntax,asdescribedinAccessingProperties.

Classesarereferencetypes;instancesofaclassarereferredto,ratherthancopied,whenassignedtovariablesorconstants,orwhenpassedasargumentstoafunctioncall.Forinformationaboutreferencetypes,seeStructuresandEnumerationsAreValueTypes.

Youcanextendthebehaviorofaclasstypewithanextensiondeclaration,asdiscussedinExtensionDeclaration.

GRAMMAR OF ACLASS DECLARAT ION

class-declaration → attributes opt access-level-modifier opt finalopt class class-name generic-parameter-clause opt type-inheritance-clause opt generic-where-clause

opt class-bodyclass-declaration → attributes opt final access-level-modifier opt class class-

name generic-parameter-clause opt type-inheritance-clause opt generic-where-clause

opt class-bodyclass-name → identifierclass-body → { class-members opt }class-members → class-member class-members optclass-member → declaration | compiler-control-statement

ProtocolDeclaration

Aprotocoldeclarationintroducesanamedprotocoltypeintoyourprogram.Protocoldeclarationsaredeclaredatglobalscopeusingtheprotocolkeywordandhavethefollowingform:

protocol protocolname : inheritedprotocols {

protocolmemberdeclarations

}

Thebodyofaprotocolcontainszeroormoreprotocolmemberdeclarations,whichdescribetheconformancerequirementsthatanytypeadoptingtheprotocolmustfulfill.Inparticular,aprotocolcandeclarethatconformingtypesmustimplementcertainproperties,methods,initializers,andsubscripts.Protocolscanalsodeclarespecialkindsoftypealiases,calledassociatedtypes,thatcanspecifyrelationshipsamongthevariousdeclarationsoftheprotocol.Protocoldeclarationscan’tcontainclass,structure,enumeration,orotherprotocoldeclarations.Theprotocolmemberdeclarationsarediscussedindetailbelow.

Protocoltypescaninheritfromanynumberofotherprotocols.Whenaprotocoltypeinheritsfromotherprotocols,thesetofrequirementsfromthoseotherprotocolsareaggregated,andanytypethatinheritsfromthecurrentprotocolmustconformtoallthoserequirements.Foranexampleofhowtouseprotocolinheritance,seeProtocolInheritance.

NOTE

Youcanalsoaggregatetheconformancerequirementsofmultipleprotocolsusingprotocolcompositiontypes,asdescribedinProtocolCompositionTypeandProtocolComposition.

Youcanaddprotocolconformancetoapreviouslydeclaredtypebyadoptingtheprotocolinanextensiondeclarationofthattype.Intheextension,youmustimplementalloftheadoptedprotocol’srequirements.Ifthetypealreadyimplementsalloftherequirements,youcanleavethebodyoftheextensiondeclarationempty.

Bydefault,typesthatconformtoaprotocolmustimplementallproperties,

methods,andsubscriptsdeclaredintheprotocol.Thatsaid,youcanmarktheseprotocolmemberdeclarationswiththeoptionaldeclarationmodifiertospecifythattheirimplementationbyaconformingtypeisoptional.Theoptionalmodifiercanbeappliedonlytomembersthataremarkedwiththeobjcattribute,andonlytomembersofprotocolsthataremarkedwiththeobjcattribute.Asaresult,onlyclasstypescanadoptandconformtoaprotocolthatcontainsoptionalmemberrequirements.Formoreinformationabouthowtousetheoptionaldeclarationmodifierandforguidanceabouthowtoaccessoptionalprotocolmembers—forexample,whenyou’renotsurewhetheraconformingtypeimplementsthem—seeOptionalProtocolRequirements.

Torestricttheadoptionofaprotocoltoclasstypesonly,includetheAnyObjectprotocolintheinheritedprotocolslistafterthecolon.Forexample,thefollowingprotocolcanbeadoptedonlybyclasstypes:

1 protocolSomeProtocol:AnyObject{

2 /*Protocolmembersgohere*/

3 }

Anyprotocolthatinheritsfromaprotocolthat’smarkedwiththeAnyObjectrequirementcanlikewisebeadoptedonlybyclasstypes.

NOTE

Ifaprotocolismarkedwiththeobjcattribute,theAnyObjectrequirementisimplicitlyappliedtothatprotocol;there’snoneedtomarktheprotocolwiththeAnyObjectrequirementexplicitly.

Protocolsarenamedtypes,andthustheycanappearinallthesameplacesinyourcodeasothernamedtypes,asdiscussedinProtocolsasTypes.However,youcan’tconstructaninstanceofaprotocol,becauseprotocolsdonotactuallyprovidetheimplementationsfortherequirementstheyspecify.

Youcanuseprotocolstodeclarewhichmethodsadelegateofaclassorstructureshouldimplement,asdescribedinDelegation.

GRAMMAR OF A PROTOCOLDECLARAT ION

protocol-declaration → attributes opt access-level-modifier opt protocol protocol-

name type-inheritance-clause opt generic-where-clause opt protocol-bodyprotocol-name → identifierprotocol-body → { protocol-members opt }protocol-members → protocol-member protocol-members optprotocol-member → protocol-member-declaration | compiler-control-statementprotocol-member-declaration → protocol-property-declarationprotocol-member-declaration → protocol-method-declarationprotocol-member-declaration → protocol-initializer-declarationprotocol-member-declaration → protocol-subscript-declarationprotocol-member-declaration → protocol-associated-type-declarationprotocol-member-declaration → typealias-declaration

ProtocolPropertyDeclarationProtocolsdeclarethatconformingtypesmustimplementapropertybyincludingaprotocolpropertydeclarationinthebodyoftheprotocoldeclaration.Protocolpropertydeclarationshaveaspecialformofavariabledeclaration:

var propertyname : type {getset}

Aswithotherprotocolmemberdeclarations,thesepropertydeclarationsdeclareonlythegetterandsetterrequirementsfortypesthatconformtotheprotocol.Asaresult,youdon’timplementthegetterorsetterdirectlyintheprotocolinwhichitisdeclared.

Thegetterandsetterrequirementscanbesatisfiedbyaconformingtypeinavarietyofways.Ifapropertydeclarationincludesboththegetandsetkeywords,aconformingtypecanimplementitwithastoredvariablepropertyoracomputedpropertythatisbothreadableandwriteable(thatis,onethatimplementsbothagetterandasetter).However,thatpropertydeclarationcan’tbeimplementedasaconstantpropertyoraread-onlycomputedproperty.Ifapropertydeclarationincludesonlythegetkeyword,itcanbeimplementedasanykindofproperty.Forexamplesofconformingtypesthatimplementthepropertyrequirementsofaprotocol,seePropertyRequirements.

Todeclareatypepropertyrequirementinaprotocoldeclaration,markthepropertydeclarationwiththestatickeyword.Structuresandenumerationsthatconformtotheprotocoldeclarethepropertywiththestatickeyword,and

classesthatconformtotheprotocoldeclarethepropertywitheitherthestaticorclasskeyword.Extensionsthataddprotocolconformancetoastructure,enumeration,orclassusethesamekeywordasthetypetheyextenduses.Extensionsthatprovideadefaultimplementationforatypepropertyrequirementusethestatickeyword.

SeealsoVariableDeclaration.

GRAMMAR OF A PROTOCOLPROPERTY DECLARAT ION

protocol-property-declaration → variable-declaration-head variable-name type-annotationgetter-setter-keyword-block

ProtocolMethodDeclarationProtocolsdeclarethatconformingtypesmustimplementamethodbyincludingaprotocolmethoddeclarationinthebodyoftheprotocoldeclaration.Protocolmethoddeclarationshavethesameformasfunctiondeclarations,withtwoexceptions:Theydon’tincludeafunctionbody,andyoucan’tprovideanydefaultparametervaluesaspartofthefunctiondeclaration.Forexamplesofconformingtypesthatimplementthemethodrequirementsofaprotocol,seeMethodRequirements.

Todeclareaclassorstaticmethodrequirementinaprotocoldeclaration,markthemethoddeclarationwiththestaticdeclarationmodifier.Structuresandenumerationsthatconformtotheprotocoldeclarethemethodwiththestatickeyword,andclassesthatconformtotheprotocoldeclarethemethodwitheitherthestaticorclasskeyword.Extensionsthataddprotocolconformancetoastructure,enumeration,orclassusethesamekeywordasthetypetheyextenduses.Extensionsthatprovideadefaultimplementationforatypemethodrequirementusethestatickeyword.

SeealsoFunctionDeclaration.

GRAMMAR OF A PROTOCOLMETHOD DECLARAT ION

protocol-method-declaration → function-head function-name generic-parameter-clauseopt function-signature generic-where-clause opt

ProtocolInitializerDeclarationProtocolsdeclarethatconformingtypesmustimplementaninitializerbyincludingaprotocolinitializerdeclarationinthebodyoftheprotocoldeclaration.Protocolinitializerdeclarationshavethesameformasinitializerdeclarations,excepttheydon’tincludetheinitializer’sbody.

Aconformingtypecansatisfyanonfailableprotocolinitializerrequirementbyimplementinganonfailableinitializeroraninit!failableinitializer.Aconformingtypecansatisfyafailableprotocolinitializerrequirementbyimplementinganykindofinitializer.

Whenaclassimplementsaninitializertosatisfyaprotocol’sinitializerrequirement,theinitializermustbemarkedwiththerequireddeclarationmodifieriftheclassisnotalreadymarkedwiththefinaldeclarationmodifier.

SeealsoInitializerDeclaration.

GRAMMAR OF A PROTOCOL I N I T I A L I ZER DECLARAT ION

protocol-initializer-declaration → initializer-head generic-parameter-clause optparameter-clause throwsopt generic-where-clause opt

protocol-initializer-declaration → initializer-head generic-parameter-clause optparameter-clause rethrows generic-where-clause opt

ProtocolSubscriptDeclarationProtocolsdeclarethatconformingtypesmustimplementasubscriptbyincludingaprotocolsubscriptdeclarationinthebodyoftheprotocoldeclaration.Protocolsubscriptdeclarationshaveaspecialformofasubscriptdeclaration:

subscript( parameters )-> returntype {getset}

Subscriptdeclarationsonlydeclaretheminimumgetterandsetterimplementationrequirementsfortypesthatconformtotheprotocol.Ifthesubscriptdeclarationincludesboththegetandsetkeywords,aconformingtypemustimplementbothagetterandasetterclause.Ifthesubscriptdeclarationincludesonlythegetkeyword,aconformingtypemustimplementatleasta

getterclauseandoptionallycanimplementasetterclause.

Todeclareastaticsubscriptrequirementinaprotocoldeclaration,markthesubscriptdeclarationwiththestaticdeclarationmodifier.Structuresandenumerationsthatconformtotheprotocoldeclarethesubscriptwiththestatickeyword,andclassesthatconformtotheprotocoldeclarethesubscriptwitheitherthestaticorclasskeyword.Extensionsthataddprotocolconformancetoastructure,enumeration,orclassusethesamekeywordasthetypetheyextenduses.Extensionsthatprovideadefaultimplementationforastaticsubscriptrequirementusethestatickeyword.

SeealsoSubscriptDeclaration.

GRAMMAR OF A PROTOCOLSUBSCR IPT DECLARAT ION

protocol-subscript-declaration → subscript-head subscript-result generic-where-clauseopt getter-setter-keyword-block

ProtocolAssociatedTypeDeclarationProtocolsdeclareassociatedtypesusingtheassociatedtypekeyword.Anassociatedtypeprovidesanaliasforatypethatisusedaspartofaprotocol’sdeclaration.Associatedtypesaresimilartotypeparametersingenericparameterclauses,butthey’reassociatedwithSelfintheprotocolinwhichthey’redeclared.Inthatcontext,Selfreferstotheeventualtypethatconformstotheprotocol.Formoreinformationandexamples,seeAssociatedTypes.

Youuseagenericwhereclauseinaprotocoldeclarationtoaddconstraintstoanassociatedtypesinheritedfromanotherprotocol,withoutredeclaringtheassociatedtypes.Forexample,thedeclarationsofSubProtocolbelowareequivalent:

1 protocolSomeProtocol{

2 associatedtypeSomeType

3 }

4

5 protocolSubProtocolA:SomeProtocol{

6 //Thissyntaxproducesawarning.

7 associatedtypeSomeType:Equatable

8 }

9

10 //Thissyntaxispreferred.

11 protocolSubProtocolB:SomeProtocolwhereSomeType:Equatable

{}

SeealsoTypeAliasDeclaration.

GRAMMAR OF A PROTOCOLASSOC IATED TYPE DECLARAT ION

protocol-associated-type-declaration → attributes opt access-level-modifier optassociatedtype typealias-name type-inheritance-clause opt typealias-assignment

opt generic-where-clause opt

InitializerDeclaration

Aninitializerdeclarationintroducesaninitializerforaclass,structure,orenumerationintoyourprogram.Initializerdeclarationsaredeclaredusingtheinitkeywordandhavetwobasicforms.

Structure,enumeration,andclasstypescanhaveanynumberofinitializers,buttherulesandassociatedbehaviorforclassinitializersaredifferent.Unlikestructuresandenumerations,classeshavetwokindsofinitializers:designatedinitializersandconvenienceinitializers,asdescribedinInitialization.

Thefollowingformdeclaresinitializersforstructures,enumerations,anddesignatedinitializersofclasses:

init( parameters ){

statements

}

Adesignatedinitializerofaclassinitializesalloftheclass’spropertiesdirectly.Itcan’tcallanyotherinitializersofthesameclass,andiftheclasshasasuperclass,itmustcalloneofthesuperclass’sdesignatedinitializers.Iftheclassinheritsanypropertiesfromitssuperclass,oneofthesuperclass’sdesignatedinitializersmustbecalledbeforeanyofthesepropertiescanbesetormodifiedinthecurrentclass.

Designatedinitializerscanbedeclaredinthecontextofaclassdeclarationonlyandthereforecan’tbeaddedtoaclassusinganextensiondeclaration.

Initializersinstructuresandenumerationscancallotherdeclaredinitializerstodelegatepartoralloftheinitializationprocess.

Todeclareconvenienceinitializersforaclass,marktheinitializerdeclarationwiththeconveniencedeclarationmodifier.

convenienceinit( parameters ){

statements

}

Convenienceinitializerscandelegatetheinitializationprocesstoanotherconvenienceinitializerortooneoftheclass’sdesignatedinitializers.Thatsaid,theinitializationprocessesmustendwithacalltoadesignatedinitializerthatultimatelyinitializestheclass’sproperties.Convenienceinitializerscan’tcallasuperclass’sinitializers.

Youcanmarkdesignatedandconvenienceinitializerswiththerequireddeclarationmodifiertorequirethateverysubclassimplementtheinitializer.Asubclass’simplementationofthatinitializermustalsobemarkedwiththerequireddeclarationmodifier.

Bydefault,initializersdeclaredinasuperclassarenotinheritedbysubclasses.Thatsaid,ifasubclassinitializesallofitsstoredpropertieswithdefaultvaluesanddoesn’tdefineanyinitializersofitsown,itinheritsallofthesuperclass’sinitializers.Ifthesubclassoverridesallofthesuperclass’sdesignatedinitializers,itinheritsthesuperclass’sconvenienceinitializers.

Aswithmethods,properties,andsubscripts,youneedtomarkoverridden

designatedinitializerswiththeoverridedeclarationmodifier.

NOTE

Ifyoumarkaninitializerwiththerequireddeclarationmodifier,youdon’talsomarktheinitializerwiththeoverridemodifierwhenyouoverridetherequiredinitializerinasubclass.

Justlikefunctionsandmethods,initializerscanthroworrethrowerrors.Andjustlikefunctionsandmethods,youusethethrowsorrethrowskeywordafteraninitializer’sparameterstoindicatetheappropriatebehavior.

Toseeexamplesofinitializersinvarioustypedeclarations,seeInitialization.

FailableInitializersAfailableinitializerisatypeofinitializerthatproducesanoptionalinstanceoranimplicitlyunwrappedoptionalinstanceofthetypetheinitializerisdeclaredon.Asaresult,afailableinitializercanreturnniltoindicatethatinitializationfailed.

Todeclareafailableinitializerthatproducesanoptionalinstance,appendaquestionmarktotheinitkeywordintheinitializerdeclaration(init?).Todeclareafailableinitializerthatproducesanimplicitlyunwrappedoptionalinstance,appendanexclamationmarkinstead(init!).Theexamplebelowshowsaninit?failableinitializerthatproducesanoptionalinstanceofastructure.

1 structSomeStruct{

2 letproperty:String

3 //producesanoptionalinstanceof'SomeStruct'

4 init?(input:String){

5 ifinput.isEmpty{

6 //discard'self'andreturn'nil'

7 returnnil

8 }

9 property=input

10 }

11 }

Youcallaninit?failableinitializerinthesamewaythatyoucallanonfailableinitializer,exceptthatyoumustdealwiththeoptionalityoftheresult.

1 ifletactualInstance=SomeStruct(input:"Hello"){

2 //dosomethingwiththeinstanceof'SomeStruct'

3 }else{

4 //initializationof'SomeStruct'failedandthe

initializerreturned'nil'

5 }

Afailableinitializercanreturnnilatanypointintheimplementationoftheinitializer’sbody.

Afailableinitializercandelegatetoanykindofinitializer.Anonfailableinitializercandelegatetoanothernonfailableinitializerortoaninit!failableinitializer.Anonfailableinitializercandelegatetoaninit?failableinitializerbyforce-unwrappingtheresultofthesuperclass’sinitializer—forexample,bywritingsuper.init()!.

Initializationfailurepropagatesthroughinitializerdelegation.Specifically,ifafailableinitializerdelegatestoaninitializerthatfailsandreturnsnil,thentheinitializerthatdelegatedalsofailsandimplicitlyreturnsnil.Ifanonfailableinitializerdelegatestoaninit!failableinitializerthatfailsandreturnsnil,thenaruntimeerrorisraised(asifyouusedthe!operatortounwrapanoptionalthathasanilvalue).

Afailabledesignatedinitializercanbeoverriddeninasubclassbyanykindofdesignatedinitializer.Anonfailabledesignatedinitializercanbeoverriddeninasubclassbyanonfailabledesignatedinitializeronly.

Formoreinformationandtoseeexamplesoffailableinitializers,seeFailable

Initializers.

GRAMMAR OF AN I N I T I A L I ZER DECLARAT ION

initializer-declaration → initializer-head generic-parameter-clause opt parameter-clausethrowsopt generic-where-clause opt initializer-body

initializer-declaration → initializer-head generic-parameter-clause opt parameter-clauserethrows generic-where-clause opt initializer-body

initializer-head → attributes opt declaration-modifiers opt initinitializer-head → attributes opt declaration-modifiers opt init ?initializer-head → attributes opt declaration-modifiers opt init !initializer-body → code-block

DeinitializerDeclaration

Adeinitializerdeclarationdeclaresadeinitializerforaclasstype.Deinitializerstakenoparametersandhavethefollowingform:

deinit{

statements

}

Adeinitializeriscalledautomaticallywhentherearenolongeranyreferencestoaclassobject,justbeforetheclassobjectisdeallocated.Adeinitializercanbedeclaredonlyinthebodyofaclassdeclaration—butnotinanextensionofaclass—andeachclasscanhaveatmostone.

Asubclassinheritsitssuperclass’sdeinitializer,whichisimplicitlycalledjustbeforethesubclassobjectisdeallocated.Thesubclassobjectisnotdeallocateduntilalldeinitializersinitsinheritancechainhavefinishedexecuting.

Deinitializersarenotcalleddirectly.

Foranexampleofhowtouseadeinitializerinaclassdeclaration,seeDeinitialization.

GRAMMAR OF ADE IN I T I A L I ZER DECLARAT ION

deinitializer-declaration → attributes opt deinit code-block

ExtensionDeclaration

Anextensiondeclarationallowsyoutoextendthebehaviorofexistingtypes.Extensiondeclarationsaredeclaredusingtheextensionkeywordandhavethefollowingform:

extension typename where requirements {

declarations

}

Thebodyofanextensiondeclarationcontainszeroormoredeclarations.Thesedeclarationscanincludecomputedproperties,computedtypeproperties,instancemethods,typemethods,initializers,subscriptdeclarations,andevenclass,structure,andenumerationdeclarations.Extensiondeclarationscan’tcontaindeinitializerorprotocoldeclarations,storedproperties,propertyobservers,orotherextensiondeclarations.Declarationsinaprotocolextensioncan’tbemarkedfinal.Foradiscussionandseveralexamplesofextensionsthatincludevariouskindsofdeclarations,seeExtensions.

Ifthetypenameisaclass,structure,orenumerationtype,theextensionextendsthattype.Ifthetypenameisaprotocoltype,theextensionextendsalltypesthatconformtothatprotocol.

Extensiondeclarationsthatextendagenerictypeoraprotocolwithassociatedtypescanincluderequirements.Ifaninstanceoftheextendedtypeorofatypethatconformstotheextendedprotocolsatisfiestherequirements,theinstancegainsthebehaviorspecifiedinthedeclaration.

Extensiondeclarationscancontaininitializerdeclarations.Thatsaid,ifthetypeyou’reextendingisdefinedinanothermodule,aninitializerdeclarationmustdelegatetoaninitializeralreadydefinedinthatmoduletoensuremembersofthattypeareproperlyinitialized.

Properties,methods,andinitializersofanexistingtypecan’tbeoverriddeninanextensionofthattype.

Extensiondeclarationscanaddprotocolconformancetoanexistingclass,structure,orenumerationtypebyspecifyingadoptedprotocols:

extension typename : adoptedprotocols where requirements

{

declarations

}

Extensiondeclarationscan’taddclassinheritancetoanexistingclass,andthereforeyoucanspecifyonlyalistofprotocolsafterthetypenameandcolon.

ConditionalConformanceYoucanextendagenerictypetoconditionallyconformtoaprotocol,sothatinstancesofthetypeconformtotheprotocolonlywhencertainrequirementsaremet.Youaddconditionalconformancetoaprotocolbyincludingrequirementsinanextensiondeclaration.

OverriddenRequirementsAren’tUsedinSomeGenericContexts

Insomegenericcontexts,typesthatgetbehaviorfromconditionalconformancetoaprotocoldon’talwaysusethespecializedimplementationsofthatprotocol’srequirements.Toillustratethisbehavior,thefollowingexampledefinestwoprotocolsandagenerictypethatconditionallyconformstobothprotocols.

1 protocolLoggable{

2 funclog()

3 }

4 extensionLoggable{

5 funclog(){

6 print(self)

7 }

8 }

9

10 protocolTitledLoggable:Loggable{

11 staticvarlogTitle:String{get}

12 }

13 extensionTitledLoggable{

14 funclog(){

15 print("\(Self.logTitle):\(self)")

16 }

17 }

18

19 structPair<T>:CustomStringConvertible{

20 letfirst:T

21 letsecond:T

22 vardescription:String{

23 return"(\(first),\(second))"

24 }

25 }

26

27 extensionPair:LoggablewhereT:Loggable{}

28 extensionPair:TitledLoggablewhereT:TitledLoggable{

29 staticvarlogTitle:String{

30 return"Pairof'\(T.logTitle)'"

31 }

32 }

33

34 extensionString:TitledLoggable{

35 staticvarlogTitle:String{

36 return"String"

37 }

38 }

ThePairstructureconformstoLoggableandTitledLoggablewheneveritsgenerictypeconformstoLoggableorTitledLoggable,respectively.Intheexamplebelow,oneAndTwoisaninstanceofPair<String>,whichconformstoTitledLoggablebecauseStringconformstoTitledLoggable.Whenthelog()methodiscalledononeAndTwodirectly,thespecializedversioncontainingthetitlestringisused.

1 letoneAndTwo=Pair(first:"one",second:"two")

2 oneAndTwo.log()

3 //Prints"Pairof'String':(one,two)"

However,whenoneAndTwoisusedinagenericcontextorasaninstanceoftheLoggableprotocol,thespecializedversionisn’tused.Swiftpickswhichimplementationoflog()tocallbyconsultingonlytheminimumrequirementsthatPairneedstoconformtoLoggable.Forthisreason,thedefaultimplementationprovidedbytheLoggableprotocolisusedinstead.

1 funcdoSomething<T:Loggable>(withx:T){

2 x.log()

3 }

4 doSomething(with:oneAndTwo)

5 //Prints"(one,two)"

Whenlog()iscalledontheinstancethat’spassedtodoSomething(_:),thecustomizedtitleisomittedfromtheloggedstring.

ProtocolConformanceMustNotBeRedundantAconcretetypecanconformtoaparticularprotocolonlyonce.Swiftmarksredundantprotocolconformancesasanerror.You’relikelytoencounterthiskindoferrorintwokindsofsituations.Thefirstsituationiswhenyouexplicitly

conformtothesameprotocolmultipletimes,butwithdifferentrequirements.Thesecondsituationiswhenyouimplicitlyinheritfromthesameprotocolmultipletimes.Thesesituationsarediscussedinthesectionsbelow.

ResolvingExplicitRedundancy

Multipleextensionsonaconcretetypecan’taddconformancetothesameprotocol,eveniftheextensions’requirementsaremutuallyexclusive.Thisrestrictionisdemonstratedintheexamplebelow.TwoextensiondeclarationsattempttoaddconditionalconformancetotheSerializableprotocol,oneforforarrayswithIntelements,andoneforarrayswithStringelements.

1 protocolSerializable{

2 funcserialize()->Any

3 }

4

5 extensionArray:SerializablewhereElement==Int{

6 funcserialize()->Any{

7 //implementation

8 }

9 }

10 extensionArray:SerializablewhereElement==String{

11 funcserialize()->Any{

12 //implementation

13 }

14 }

15 //Error:redundantconformanceof'Array<Element>'to

protocol'Serializable'

Ifyouneedtoaddconditionalconformancebasedonmultipleconcretetypes,createanewprotocolthateachtypecanconformtoandusethatprotocolastherequirementwhendeclaringconditionalconformance.

1 protocolSerializableInArray{}

2 extensionInt:SerializableInArray{}

3 extensionString:SerializableInArray{}

4

5 extensionArray:SerializablewhereElement:

SerializableInArray{

6 funcserialize()->Any{

7 //implementation

8 }

9 }

ResolvingImplicitRedundancy

Whenaconcretetypeconditionallyconformstoaprotocol,thattypeimplicitlyconformstoanyparentprotocolswiththesamerequirements.

Ifyouneedatypetoconditionallyconformtotwoprotocolsthatinheritfromasingleparent,explicitlydeclareconformancetotheparentprotocol.Thisavoidsimplicitlyconformingtotheparentprotocoltwicewithdifferentrequirements.

ThefollowingexampleexplicitlydeclarestheconditionalconformanceofArraytoLoggabletoavoidaconflictwhendeclaringitsconditionalconformancetobothTitledLoggableandthenewMarkedLoggableprotocol.

1 protocolMarkedLoggable:Loggable{

2 funcmarkAndLog()

3 }

4

5 extensionMarkedLoggable{

6 funcmarkAndLog(){

7 print("----------")

8 log()

9 }

10 }

11

12 extensionArray:LoggablewhereElement:Loggable{}

13 extensionArray:TitledLoggablewhereElement:TitledLoggable

{

14 staticvarlogTitle:String{

15 return"Arrayof'\(Element.logTitle)'"

16 }

17 }

18 extensionArray:MarkedLoggablewhereElement:MarkedLoggable

{}

WithouttheextensiontoexplicitlydeclareconditionalconformancetoLoggable,theotherArrayextensionswouldimplicitlycreatethesedeclarations,resultinginanerror:

1 extensionArray:LoggablewhereElement:TitledLoggable{}

2 extensionArray:LoggablewhereElement:MarkedLoggable{}

3 //Error:redundantconformanceof'Array<Element>'toprotocol

'Loggable'

GRAMMAR OF AN EXTENS ION DECLARAT ION

extension-declaration → attributes opt access-level-modifier opt extension type-identifier type-inheritance-clause opt generic-where-clause opt extension-body

extension-body → { extension-members opt }extension-members → extension-member extension-members optextension-member → declaration | compiler-control-statement

SubscriptDeclaration

Asubscriptdeclarationallowsyoutoaddsubscriptingsupportforobjectsofaparticulartypeandaretypicallyusedtoprovideaconvenientsyntaxforaccessingtheelementsinacollection,list,orsequence.Subscriptdeclarationsaredeclaredusingthesubscriptkeywordandhavethefollowingform:

subscript( parameters )-> returntype {

get{

statements

}

set( settername ){

statements

}

}

Subscriptdeclarationscanappearonlyinthecontextofaclass,structure,enumeration,extension,orprotocoldeclaration.

Theparametersspecifyoneormoreindexesusedtoaccesselementsofthecorrespondingtypeinasubscriptexpression(forexample,theiintheexpressionobject[i]).Althoughtheindexesusedtoaccesstheelementscanbeofanytype,eachparametermustincludeatypeannotationtospecifythetypeofeachindex.Thereturntypespecifiesthetypeoftheelementbeingaccessed.

Aswithcomputedproperties,subscriptdeclarationssupportreadingandwritingthevalueoftheaccessedelements.Thegetterisusedtoreadthevalue,andthesetterisusedtowritethevalue.Thesetterclauseisoptional,andwhenonlyagetterisneeded,youcanomitbothclausesandsimplyreturntherequestedvaluedirectly.Thatsaid,ifyouprovideasetterclause,youmustalsoprovideagetterclause.

Thesetternameandenclosingparenthesesareoptional.Ifyouprovideasettername,itisusedasthenameoftheparametertothesetter.Ifyoudonotprovideasettername,thedefaultparameternametothesetterisvalue.Thetypeoftheparametertothesetteristhesameasthereturntype.

Youcanoverloadasubscriptdeclarationinthetypeinwhichitisdeclared,as

longastheparametersorthereturntypedifferfromtheoneyou’reoverloading.Youcanalsooverrideasubscriptdeclarationinheritedfromasuperclass.Whenyoudoso,youmustmarktheoverriddensubscriptdeclarationwiththeoverridedeclarationmodifier.

Subscriptparametersfollowthesamerulesasfunctionparameters,withtwoexceptions.Bydefault,theparametersusedinsubscriptingdon’thaveargumentlabels,unlikefunctions,methods,andinitializers.However,youcanprovideexplicitargumentlabelsusingthesamesyntaxthatfunctions,methods,andinitializersuse.Inaddition,subscriptscan’thavein-outparameters.

Youcanalsodeclaresubscriptsinthecontextofaprotocoldeclaration,asdescribedinProtocolSubscriptDeclaration.

Formoreinformationaboutsubscriptingandtoseeexamplesofsubscriptdeclarations,seeSubscripts.

TypeSubscriptDeclarationsTodeclareasubscriptthat’sexposedbythetype,ratherthanbyinstancesofthetype,markthesubscriptdeclarationwiththestaticdeclarationmodifier.Classescanmarktypecomputedpropertieswiththeclassdeclarationmodifierinsteadtoallowsubclassestooverridethesuperclass’simplementation.Inaclassdeclaration,thestatickeywordhasthesameeffectasmarkingthedeclarationwithboththeclassandfinaldeclarationmodifiers.

GRAMMAR OF A SUBSCR IPT DECLARAT ION

subscript-declaration → subscript-head subscript-result generic-where-clause opt code-block

subscript-declaration → subscript-head subscript-result generic-where-clause opt getter-setter-block

subscript-declaration → subscript-head subscript-result generic-where-clause opt getter-setter-keyword-block

subscript-head → attributes opt declaration-modifiers opt subscript generic-parameter-clause opt parameter-clause

subscript-result → -> attributes opt type

OperatorDeclaration

Anoperatordeclarationintroducesanewinfix,prefix,orpostfixoperatorintoyourprogramandisdeclaredusingtheoperatorkeyword.

Youcandeclareoperatorsofthreedifferentfixities:infix,prefix,andpostfix.Thefixityofanoperatorspecifiestherelativepositionofanoperatortoitsoperands.

Therearethreebasicformsofanoperatordeclaration,oneforeachfixity.Thefixityoftheoperatorisspecifiedbymarkingtheoperatordeclarationwiththeinfix,prefix,orpostfixdeclarationmodifierbeforetheoperatorkeyword.Ineachform,thenameoftheoperatorcancontainonlytheoperatorcharactersdefinedinOperators.

Thefollowingformdeclaresanewinfixoperator:

infixoperator operatorname : precedencegroup

Aninfixoperatorisabinaryoperatorthatiswrittenbetweenitstwooperands,suchasthefamiliaradditionoperator(+)intheexpression1+2.

Infixoperatorscanoptionallyspecifyaprecedencegroup.Ifyouomittheprecedencegroupforanoperator,Swiftusesthedefaultprecedencegroup,DefaultPrecedence,whichspecifiesaprecedencejusthigherthanTernaryPrecedence.Formoreinformation,seePrecedenceGroupDeclaration.

Thefollowingformdeclaresanewprefixoperator:

prefixoperator operatorname

Aprefixoperatorisaunaryoperatorthatiswrittenimmediatelybeforeitsoperand,suchastheprefixlogicalNOToperator(!)intheexpression!a.

Prefixoperatorsdeclarationsdon’tspecifyaprecedencelevel.Prefixoperatorsarenonassociative.

Thefollowingformdeclaresanewpostfixoperator:

postfixoperator operatorname

Apostfixoperatorisaunaryoperatorthatiswrittenimmediatelyafteritsoperand,suchasthepostfixforced-unwrapoperator(!)intheexpressiona!.

Aswithprefixoperators,postfixoperatordeclarationsdon’tspecifyaprecedencelevel.Postfixoperatorsarenonassociative.

Afterdeclaringanewoperator,youimplementitbydeclaringastaticmethodthathasthesamenameastheoperator.Thestaticmethodisamemberofoneofthetypeswhosevaluestheoperatortakesasanargument—forexample,anoperatorthatmultipliesaDoublebyanIntisimplementedasastaticmethodoneithertheDoubleorIntstructure.Ifyou’reimplementingaprefixorpostfixoperator,youmustalsomarkthatmethoddeclarationwiththecorrespondingprefixorpostfixdeclarationmodifier.Toseeanexampleofhowtocreateandimplementanewoperator,seeCustomOperators.

GRAMMAR OF AN OPERATOR DECLARAT ION

operator-declaration → prefix-operator-declaration | postfix-operator-declaration | infix-operator-declaration

prefix-operator-declaration → prefix operator operatorpostfix-operator-declaration → postfix operator operatorinfix-operator-declaration → infix operator operator infix-operator-group optinfix-operator-group → : precedence-group-name

PrecedenceGroupDeclaration

Aprecedencegroupdeclarationintroducesanewgroupingforinfixoperatorprecedenceintoyourprogram.Theprecedenceofanoperatorspecifieshowtightlytheoperatorbindstoitsoperands,intheabsenceofgroupingparentheses.

Aprecedencegroupdeclarationhasthefollowingform:

precedencegroup precedencegroupname {

higherThan: lowergroupnames

lowerThan: highergroupnames

associativity: associativity

assignment: assignment

}

Thelowergroupnamesandhighergroupnameslistsspecifythenewprecedencegroup’srelationtoexistingprecedencegroups.ThelowerThanprecedencegroupattributemayonlybeusedtorefertoprecedencegroupsdeclaredoutsideofthecurrentmodule.Whentwooperatorscompetewitheachotherfortheiroperands,suchasintheexpression2+3*5,theoperatorwiththehigherrelativeprecedencebindsmoretightlytoitsoperands.

NOTE

Precedencegroupsrelatedtoeachotherusinglowergroupnamesandhighergroupnamesmustfitintoasinglerelationalhierarchy,buttheydon’thavetoformalinearhierarchy.Thismeansitispossibletohaveprecedencegroupswithundefinedrelativeprecedence.Operatorsfromthoseprecedencegroupscan’tbeusednexttoeachotherwithoutgroupingparentheses.

Swiftdefinesnumerousprecedencegroupstogoalongwiththeoperatorsprovidedbythestandardlibrary.Forexample,theaddition(+)andsubtraction(-)operatorsbelongtotheAdditionPrecedencegroup,andthemultiplication(*)anddivision(/)operatorsbelongtotheMultiplicationPrecedencegroup.ForacompletelistofprecedencegroupsprovidedbytheSwiftstandardlibrary,seeOperatorDeclarations.

Theassociativityofanoperatorspecifieshowasequenceofoperatorswiththesameprecedencelevelaregroupedtogetherintheabsenceofgroupingparentheses.Youspecifytheassociativityofanoperatorbywritingoneofthecontext-sensitivekeywordsleft,right,ornone—ifyouromittheassociativity,thedefaultisnone.Operatorsthatareleft-associativegroupleft-to-right.Forexample,thesubtractionoperator(-)isleft-associative,sotheexpression4-5-6isgroupedas(4-5)-6andevaluatesto-7.Operatorsthatareright-associativegroupright-to-left,andoperatorsthatarespecifiedwithanassociativityofnonedon’tassociateatall.Nonassociativeoperatorsofthesameprecedencelevelcan’tappearadjacenttoeachtoother.Forexample,the<operatorhasanassociativityofnone,whichmeans1<2<3isnotavalidexpression.

Theassignmentofaprecedencegroupspecifiestheprecedenceofanoperatorwhenusedinanoperationthatincludesoptionalchaining.Whensettotrue,anoperatorinthecorrespondingprecedencegroupusesthesamegroupingrulesduringoptionalchainingastheassignmentoperatorsfromthestandardlibrary.Otherwise,whensettofalseoromitted,operatorsintheprecedencegroupfollowsthesameoptionalchainingrulesasoperatorsthatdon’tperformassignment.

GRAMMAR OF A PRECEDENCE GROUP DECLARAT ION

precedence-group-declaration → precedencegroup precedence-group-name {precedence-group-attributes opt }

precedence-group-attributes → precedence-group-attribute precedence-group-attributesopt

precedence-group-attribute → precedence-group-relationprecedence-group-attribute → precedence-group-assignmentprecedence-group-attribute → precedence-group-associativityprecedence-group-relation → higherThan : precedence-group-namesprecedence-group-relation → lowerThan : precedence-group-namesprecedence-group-assignment → assignment : boolean-literalprecedence-group-associativity → associativity : leftprecedence-group-associativity → associativity : rightprecedence-group-associativity → associativity : noneprecedence-group-names → precedence-group-name | precedence-group-name ,

precedence-group-namesprecedence-group-name → identifier

DeclarationModifiers

Declarationmodifiersarekeywordsorcontext-sensitivekeywordsthatmodifythebehaviorormeaningofadeclaration.Youspecifyadeclarationmodifierbywritingtheappropriatekeywordorcontext-sensitivekeywordbetweenadeclaration’sattributes(ifany)andthekeywordthatintroducesthedeclaration.

class

Applythismodifiertoamemberofaclasstoindicatethatthememberisamemberoftheclassitself,ratherthanamemberofinstancesoftheclass.Membersofasuperclassthathavethismodifieranddon’thavethefinalmodifiercanbeoverriddenbysubclasses.

dynamic

ApplythismodifiertoanymemberofaclassthatcanberepresentedbyObjective-C.Whenyoumarkamemberdeclarationwiththedynamicmodifier,accesstothatmemberisalwaysdynamicallydispatchedusingtheObjective-Cruntime.Accesstothatmemberisneverinlinedordevirtualizedbythecompiler.

BecausedeclarationsmarkedwiththedynamicmodifieraredispatchedusingtheObjective-Cruntime,theymustbemarkedwiththeobjcattribute.

final

Applythismodifiertoaclassortoaproperty,method,orsubscriptmemberofaclass.It’sappliedtoaclasstoindicatethattheclasscan’tbesubclassed.It’sappliedtoaproperty,method,orsubscriptofaclasstoindicatethataclassmembercan’tbeoverriddeninanysubclass.Foranexampleofhowtousethefinalattribute,seePreventingOverrides.

lazy

Applythismodifiertoastoredvariablepropertyofaclassorstructuretoindicatethattheproperty’sinitialvalueiscalculatedandstoredatmostonce,whenthepropertyisfirstaccessed.Foranexampleofhowtousethelazymodifier,seeLazyStoredProperties.

optional

Applythismodifiertoaprotocol’sproperty,method,orsubscriptmemberstoindicatethataconformingtypeisn’trequiredtoimplementthosemembers.

Youcanapplytheoptionalmodifieronlytoprotocolsthataremarkedwiththeobjcattribute.Asaresult,onlyclasstypescanadoptandconformtoaprotocolthatcontainsoptionalmemberrequirements.Formoreinformationabouthowtousetheoptionalmodifierandforguidanceabouthowtoaccessoptionalprotocolmembers—forexample,whenyou’renotsurewhetheraconformingtypeimplementsthem—seeOptionalProtocolRequirements.

required

Applythismodifiertoadesignatedorconvenienceinitializerofaclasstoindicatethateverysubclassmustimplementthatinitializer.Thesubclass’simplementationofthatinitializermustalsobemarkedwiththerequiredmodifier.

static

Applythismodifiertoamemberofastructure,class,enumeration,orprotocoltoindicatethatthememberisamemberofthetype,ratherthanamemberofinstancesofthattype.Inthescopeofaclassdeclaration,writingthestaticmodifieronamemberdeclarationhasthesameeffectaswritingtheclassandfinalmodifiersonthatmemberdeclaration.However,constanttypepropertiesofaclassareanexception:statichasitsnormal,nonclassmeaningtherebecauseyoucan’twriteclassorfinalonthosedeclarations.

unowned

Applythismodifiertoastoredvariable,constant,orstoredpropertytoindicatethatthevariableorpropertyhasanunownedreferencetotheobjectstoredasitsvalue.Ifyoutrytoaccessthevariableorpropertyaftertheobjecthasbeendeallocated,aruntimeerrorisraised.Likeaweakreference,thetypeofthepropertyorvaluemustbeaclasstype;unlikeaweakreference,thetypeisnon-optional.Foranexampleandmoreinformationabouttheunownedmodifier,seeUnownedReferences.

unowned(safe)

Anexplicitspellingofunowned.

unowned(unsafe)

Applythismodifiertoastoredvariable,constant,orstoredpropertytoindicatethatthevariableorpropertyhasanunownedreferencetotheobjectstoredasitsvalue.Ifyoutrytoaccessthevariableorpropertyaftertheobjecthasbeendeallocated,you’llaccessthememoryatthelocationwheretheobjectusedtobe,whichisamemory-unsafeoperation.Likeaweakreference,thetypeofthepropertyorvaluemustbeaclasstype;unlikea

weakreference,thetypeisnon-optional.Foranexampleandmoreinformationabouttheunownedmodifier,seeUnownedReferences.

weak

Applythismodifiertoastoredvariableorstoredvariablepropertytoindicatethatthevariableorpropertyhasaweakreferencetotheobjectstoredasitsvalue.Thetypeofthevariableorpropertymustbeanoptionalclasstype.Ifyouaccessthevariableorpropertyaftertheobjecthasbeendeallocated,itsvalueisnil.Foranexampleandmoreinformationabouttheweakmodifier,seeWeakReferences.

AccessControlLevelsSwiftprovidesfivelevelsofaccesscontrol:open,public,internal,fileprivate,andprivate.Youcanmarkadeclarationwithoneoftheaccess-levelmodifiersbelowtospecifythedeclaration’saccesslevel.AccesscontrolisdiscussedindetailinAccessControl.

open

Applythismodifiertoadeclarationtoindicatethedeclarationcanbeaccessedandsubclassedbycodeinthesamemoduleasthedeclaration.Declarationsmarkedwiththeopenaccess-levelmodifiercanalsobeaccessedandsubclassedbycodeinamodulethatimportsthemodulethatcontainsthatdeclaration.

public

Applythismodifiertoadeclarationtoindicatethedeclarationcanbeaccessedandsubclassedbycodeinthesamemoduleasthedeclaration.Declarationsmarkedwiththepublicaccess-levelmodifiercanalsobeaccessed(butnotsubclassed)bycodeinamodulethatimportsthemodulethatcontainsthatdeclaration.

internal

Applythismodifiertoadeclarationtoindicatethedeclarationcanbe

accessedonlybycodeinthesamemoduleasthedeclaration.Bydefault,mostdeclarationsareimplicitlymarkedwiththeinternalaccess-levelmodifier.

fileprivate

Applythismodifiertoadeclarationtoindicatethedeclarationcanbeaccessedonlybycodeinthesamesourcefileasthedeclaration.

private

Applythismodifiertoadeclarationtoindicatethedeclarationcanbeaccessedonlybycodewithinthedeclaration’simmediateenclosingscope.

Forthepurposeofaccesscontrol,extensionstothesametypethatareinthesamefileshareanaccess-controlscope.Ifthetypetheyextendisalsointhesamefile,theysharethetype’saccess-controlscope.Privatemembersdeclaredinthetype’sdeclarationcanbeaccessedfromextensions,andprivatemembersdeclaredinoneextensioncanbeaccessedfromotherextensionsandfromthetype’sdeclaration.

Eachaccess-levelmodifieraboveoptionallyacceptsasingleargument,whichconsistsofthesetkeywordenclosedinparentheses(forexample,private(set)).Usethisformofanaccess-levelmodifierwhenyouwanttospecifyanaccesslevelforthesetterofavariableorsubscriptthat’slessthanorequaltotheaccesslevelofthevariableorsubscriptitself,asdiscussedinGettersandSetters.

GRAMMAR OF ADECLARAT ION MOD IF I ER

declaration-modifier → class | convenience | dynamic | final | infix | lazy |optional | override | postfix | prefix | required | static | unowned |unowned ( safe ) | unowned ( unsafe ) | weak

declaration-modifier → access-level-modifierdeclaration-modifier → mutation-modifierdeclaration-modifiers → declaration-modifier declaration-modifiers optaccess-level-modifier → private | private ( set )access-level-modifier → fileprivate | fileprivate ( set )access-level-modifier → internal | internal ( set )access-level-modifier → public | public ( set )access-level-modifier → open | open ( set )mutation-modifier → mutating | nonmutating

Attributes

TherearetwokindsofattributesinSwift—thosethatapplytodeclarationsandthosethatapplytotypes.Anattributeprovidesadditionalinformationaboutthedeclarationortype.Forexample,thediscardableResultattributeonafunctiondeclarationindicatesthat,althoughthefunctionreturnsavalue,thecompilershouldn’tgenerateawarningifthereturnvalueisunused.

Youspecifyanattributebywritingthe@symbolfollowedbytheattribute’snameandanyargumentsthattheattributeaccepts:

@ attributename

@ attributename ( attributearguments )

Somedeclarationattributesacceptargumentsthatspecifymoreinformationabouttheattributeandhowitappliestoaparticulardeclaration.Theseattributeargumentsareenclosedinparentheses,andtheirformatisdefinedbytheattributetheybelongto.

DeclarationAttributes

Youcanapplyadeclarationattributetodeclarationsonly.

availableApplythisattributetoindicateadeclaration’slifecyclerelativetocertainSwiftlanguageversionsorcertainplatformsandoperatingsystemversions.

Theavailableattributealwaysappearswithalistoftwoormorecomma-separatedattributearguments.Theseargumentsbeginwithoneofthefollowingplatformorlanguagenames:

iOS

iOSApplicationExtension

macOS

macOSApplicationExtension

watchOS

watchOSApplicationExtension

tvOS

tvOSApplicationExtension

swift

Youcanalsouseanasterisk(*)toindicatetheavailabilityofthedeclarationonalloftheplatformnameslistedabove.AnavailableattributethatspecifiesavailabilityusingaSwiftversionnumbercan’tusetheasterisk.

Theremainingargumentscanappearinanyorderandspecifyadditionalinformationaboutthedeclaration’slifecycle,includingimportantmilestones.

Theunavailableargumentindicatesthatthedeclarationisn’tavailableonthespecifiedplatform.Thisargumentcan’tbeusedwhenspecifyingSwiftversionavailability.

Theintroducedargumentindicatesthefirstversionofthespecifiedplatformorlanguageinwhichthedeclarationwasintroduced.Ithasthefollowingform:

introduced: versionnumber

Theversionnumberconsistsofonetothreepositiveintegers,separatedbyperiods.

Thedeprecatedargumentindicatesthefirstversionofthespecified

platformorlanguageinwhichthedeclarationwasdeprecated.Ithasthefollowingform:

deprecated: versionnumber

Theoptionalversionnumberconsistsofonetothreepositiveintegers,separatedbyperiods.Omittingtheversionnumberindicatesthatthedeclarationiscurrentlydeprecated,withoutgivinganyinformationaboutwhenthedeprecationoccurred.Ifyouomittheversionnumber,omitthecolon(:)aswell.

Theobsoletedargumentindicatesthefirstversionofthespecifiedplatformorlanguageinwhichthedeclarationwasobsoleted.Whenadeclarationisobsoleted,it’sremovedfromthespecifiedplatformorlanguageandcannolongerbeused.Ithasthefollowingform:

obsoleted: versionnumber

Theversionnumberconsistsofonetothreepositiveintegers,separatedbyperiods.

Themessageargumentprovidesatextualmessagethatthecompilerdisplayswhenemittingawarningorerrorabouttheuseofadeprecatedorobsoleteddeclaration.Ithasthefollowingform:

message: message

Themessageconsistsofastringliteral.

Therenamedargumentprovidesatextualmessagethatindicatesthenewnameforadeclarationthat’sbeenrenamed.Thecompilerdisplaysthenewnamewhenemittinganerrorabouttheuseofarenameddeclaration.Ithasthefollowingform:

renamed: newname

Thenewnameconsistsofastringliteral.

Youcanapplytheavailableattributewiththerenamedandunavailableargumentstoatypealiasdeclaration,asshownbelow,toindicatethatthenameofadeclarationchangedbetweenreleasesofaframeworkorlibrary.Thiscombinationresultsinacompile-timeerrorthatthedeclarationhasbeenrenamed.

1 //Firstrelease

2 protocolMyProtocol{

3 //protocoldefinition

4 }

1 //SubsequentreleaserenamesMyProtocol

2 protocolMyRenamedProtocol{

3 //protocoldefinition

4 }

5

6 @available(*,unavailable,renamed:"MyRenamedProtocol")

7 typealiasMyProtocol=MyRenamedProtocol

Youcanapplymultipleavailableattributesonasingledeclarationtospecifythedeclaration’savailabilityondifferentplatformsanddifferentversionsofSwift.Thedeclarationthattheavailableattributeappliestoisignorediftheattributespecifiesaplatformorlanguageversionthatdoesn’tmatchthecurrenttarget.Ifyouusemultipleavailableattributes,theeffectiveavailabilityisthecombinationoftheplatformandSwiftavailabilities.

Ifanavailableattributeonlyspecifiesanintroducedargumentinadditiontoaplatformorlanguagenameargument,youcanusethefollowingshorthandsyntaxinstead:

@available( platformname versionnumber ,*)

@available(swift versionnumber )

Theshorthandsyntaxforavailableattributesconciselyexpressesavailability

formultipleplatforms.Althoughthetwoformsarefunctionallyequivalent,theshorthandformispreferredwheneverpossible.

1 @available(iOS10.0,macOS10.12,*)

2 classMyClass{

3 //classdefinition

4 }

AnavailableattributethatspecifiesavailabilityusingaSwiftversionnumbercan’tadditionallyspecifyadeclaration’splatformavailability.Instead,useseparateavailableattributestospecifyaSwiftversionavailabilityandoneormoreplatformavailabilities.

1 @available(swift3.0.2)

2 @available(macOS10.12,*)

3 structMyStruct{

4 //structdefinition

5 }

discardableResultApplythisattributetoafunctionormethoddeclarationtosuppressthecompilerwarningwhenthefunctionormethodthatreturnsavalueiscalledwithoutusingitsresult.

dynamicCallableApplythisattributetoaclass,structure,enumeration,orprotocoltotreatinstancesofthetypeascallablefunctions.ThetypemustimplementeitheradynamicallyCall(withArguments:)method,adynamicallyCall(withKeywordArguments:)method,orboth.

Youcancallaninstanceofadynamicallycallabletypeasifit’safunctionthat

takesanynumberofarguments.

1 @dynamicCallable

2 structTelephoneExchange{

3 funcdynamicallyCall(withArgumentsphoneNumber:[Int]){

4 ifphoneNumber==[4,1,1]{

5 print("GetSwifthelponforums.swift.org")

6 }else{

7 print("Unrecognizednumber")

8 }

9 }

10 }

11

12 letdial=TelephoneExchange()

13

14 //Useadynamicmethodcall.

15 dial(4,1,1)

16 //Prints"GetSwifthelponforums.swift.org"

17

18 dial(8,6,7,5,3,0,9)

19 //Prints"Unrecognizednumber"

20

21 //Calltheunderlyingmethoddirectly.

22 dial.dynamicallyCall(withArguments:[4,1,1])

ThedeclarationofthedynamicallyCall(withArguments:)methodmusthaveasingleparameterthatconformstotheExpressibleByArrayLiteralprotocol—like[Int]intheexampleabove.Thereturntypecanbeanytype.

YoucanincludelabelsinadynamicmethodcallifyouimplementthedynamicallyCall(withKeywordArguments:)method.

1 @dynamicCallable

2 structRepeater{

3 funcdynamicallyCall(withKeywordArgumentspairs:

KeyValuePairs<String,Int>)->String{

4 returnpairs

5 .map{label,countin

6 repeatElement(label,count:

count).joined(separator:"")

7 }

8 .joined(separator:"\n")

9 }

10 }

11

12 letrepeatLabels=Repeater()

13 print(repeatLabels(a:1,b:2,c:3,b:2,a:1))

14 //a

15 //bb

16 //ccc

17 //bb

18 //a

ThedeclarationofthedynamicallyCall(withKeywordArguments:)methodmusthaveasingleparameterthatconformstotheExpressibleByDictionaryLiteralprotocol,andthereturntypecanbeanytype.Theparameter’sKeymustbeExpressibleByStringLiteral.ThepreviousexampleusesKeyValuePairsastheparametertypesothatcallerscanincludeduplicateparameterlabels—aandbappearmultipletimesinthecalltorepeat.

IfyouimplementbothdynamicallyCallmethods,dynamicallyCall(withKeywordArguments:)iscalledwhenthemethodcallincludeskeywordarguments.Inallothercases,dynamicallyCall(withArguments:)iscalled.

YoucanonlycalladynamicallycallableinstancewithargumentsandareturnvaluethatmatchthetypesyouspecifyinoneofyourdynamicallyCallmethodimplementations.Thecallinthefollowingexampledoesn’tcompilebecausethereisn’tanimplementationofdynamicallyCall(withArguments:)thattakesKeyValuePairs<String,String>.

repeatLabels(a:"four")//Error

dynamicMemberLookupApplythisattributetoaclass,structure,enumeration,orprotocoltoenablememberstobelookedupbynameatruntime.Thetypemustimplementasubscript(dynamicMemberLookup:)subscript.

Inanexplicitmemberexpression,ifthereisn’tacorrespondingdeclarationforthenamedmember,theexpressionisunderstoodasacalltothetype’ssubscript(dynamicMemberLookup:)subscript,passinginformationaboutthememberastheargument.Thesubscriptcanacceptaparameterthat’seitherakeypathoramembername;ifyouimplementbothsubscripts,thesubscriptthattakeskeypathargumentisused.

Animplementationofsubscript(dynamicMemberLookup:)canacceptkeypathsusinganargumentoftypeKeyPath,WritableKeyPath,orReferenceWritableKeyPath.ItcanacceptmembernamesusinganargumentofatypethatconformstotheExpressibleByStringLiteralprotocol—inmostcases,String.Thesubscript’sreturntypecanbeanytype.

Dynamicmemberlookupbymembernamecanbeusedtocreateawrappertypearounddatathatcan’tbetypecheckedatcompiletime,suchaswhenbridgingdatafromotherlanguagesintoSwift.Forexample:

1 @dynamicMemberLookup

2 structDynamicStruct{

3 letdictionary=["someDynamicMember":325,

4 "someOtherMember":787]

5 subscript(dynamicMembermember:String)->Int{

6 returndictionary[member]??1054

7 }

8 }

9 lets=DynamicStruct()

10

11 //Usedynamicmemberlookup.

12 letdynamic=s.someDynamicMember

13 print(dynamic)

14 //Prints"325"

15

16 //Calltheunderlyingsubscriptdirectly.

17 letequivalent=s[dynamicMember:"someDynamicMember"]

18 print(dynamic==equivalent)

19 //Prints"true"

Dynamicmemberlookupbykeypathcanbeusedtoimplementawrappertypeinawaythatsupportscompile-timetypechecking.Forexample:

1 structPoint{varx,y:Int}

2

3 @dynamicMemberLookup

4 structPassthroughWrapper<Value>{

5 varvalue:Value

6 subscript<T>(dynamicMembermember:KeyPath<Value,T>)->T

{

7 get{returnvalue[keyPath:member]}

8 }

9 }

10

11 letpoint=Point(x:381,y:431)

12 letwrapper=PassthroughWrapper(value:point)

13 print(wrapper.x)

frozenApplythisattributetoastructureorenumerationdeclarationtorestrictthekindsofchangesyoucanmaketothetype.Thisattributeisallowedonlywhencompilinginlibraryevolutionmode.Futureversionsofthelibrarycan’tchangethedeclarationbyadding,removing,orreorderinganenumeration’scasesorastructure’sstoredinstanceproperties.Thesechangesareallowedonnonfrozentypes,buttheybreakABIcompatibilityforfrozentypes.

NOTE

Whenthecompilerisn’tinlibraryevolutionmode,allstructuresandenumerationsareimplicitlyfrozen,andyoucan’tusethisattribute.

Inlibraryevolutionmode,codethatinteractswithmembersofnonfrozenstructuresandenumerationsiscompiledinawaythatallowsittocontinueworkingwithoutrecompilingevenifafutureversionofthelibraryadds,removes,orreorderssomeofthattype’smembers.Thecompilermakesthispossibleusingtechniqueslikelookingupinformationatruntimeandaddingalayerofindirection.Markingastructureorenumerationasfrozengivesupthisflexibilitytogainperformance:Futureversionsofthelibrarycanmakeonlylimitedchangestothetype,butthecompilercanmakeadditionaloptimizationsincodethatinteractswiththetype’smembers.

Frozentypes,thetypesofthestoredpropertiesoffrozenstructures,andtheassociatedvaluesoffrozenenumerationcasesmustbepublicormarkedwiththeusableFromInlineattribute.Thepropertiesofafrozenstructurecan’thavepropertyobservers,andexpressionsthatprovidetheinitialvalueforstoredinstancepropertiesmustfollowthesamerestrictionsasinlinablefunctions,asdiscussedininlinable.

Toenablelibraryevolutionmodeonthecommandline,passthe-enable-

library-evolutionoptiontotheSwiftcompiler.ToenableitinXcode,setthe“BuildLibrariesforDistribution”buildsetting(BUILD_LIBRARY_FOR_DISTRIBUTION)toYes,asdescribedinXcodeHelp.

Aswitchstatementoverafrozenenumerationdoesn’trequireadefaultcase,asdiscussedinSwitchingOverFutureEnumerationCases.Includingadefaultor@unknowndefaultcasewhenswitchingoverafrozenenumerationproducesawarningbecausethatcodeisneverexecuted.

GKInspectableApplythisattributetoexposeacustomGameplayKitcomponentpropertytotheSpriteKiteditorUI.Applyingthisattributealsoimpliestheobjcattribute.

inlinableApplythisattributetoafunction,method,computedproperty,subscript,convenienceinitializer,ordeinitializerdeclarationtoexposethatdeclaration’simplementationaspartofthemodule’spublicinterface.Thecompilerisallowedtoreplacecallstoaninlinablesymbolwithacopyofthesymbol’simplementationatthecallsite.

Inlinablecodecaninteractwithpublicsymbolsdeclaredinanymodule,anditcaninteractwithinternalsymbolsdeclaredinthesamemodulethataremarkedwiththeusableFromInlineattribute.Inlinablecodecan’tinteractwithprivateorfileprivatesymbols.

Thisattributecan’tbeappliedtodeclarationsthatarenestedinsidefunctionsortofileprivateorprivatedeclarations.Functionsandclosuresthataredefinedinsideaninlinablefunctionareimplicitlyinlinable,eventhoughtheycan’tbemarkedwiththisattribute.

nonobjcApplythisattributetoamethod,property,subscript,orinitializerdeclarationto

suppressanimplicitobjcattribute.ThenonobjcattributetellsthecompilertomakethedeclarationunavailableinObjective-Ccode,eventhoughit’spossibletorepresentitinObjective-C.

Applyingthisattributetoanextensionhasthesameeffectasapplyingittoeverymemberofthatextensionthatisn’texplicitlymarkedwiththeobjcattribute.

Youusethenonobjcattributetoresolvecircularityforbridgingmethodsinaclassmarkedwiththeobjcattribute,andtoallowoverloadingofmethodsandinitializersinaclassmarkedwiththeobjcattribute.

Amethodmarkedwiththenonobjcattributecan’toverrideamethodmarkedwiththeobjcattribute.However,amethodmarkedwiththeobjcattributecanoverrideamethodmarkedwiththenonobjcattribute.Similarly,amethodmarkedwiththenonobjcattributecan’tsatisfyaprotocolrequirementforamethodmarkedwiththeobjcattribute.

NSApplicationMainApplythisattributetoaclasstoindicatethatit’stheapplicationdelegate.UsingthisattributeisequivalenttocallingtheNSApplicationMain(_:_:)function.

Ifyoudon’tusethisattribute,supplyamain.swiftfilewithcodeatthetoplevelthatcallstheNSApplicationMain(_:_:)functionasfollows:

1 importAppKit

2 NSApplicationMain(CommandLine.argc,CommandLine.unsafeArgv)

NSCopyingApplythisattributetoastoredvariablepropertyofaclass.Thisattributecausestheproperty’ssettertobesynthesizedwithacopyoftheproperty’svalue—returnedbythecopyWithZone(_:)method—insteadofthevalueofthepropertyitself.ThetypeofthepropertymustconformtotheNSCopyingprotocol.

TheNSCopyingattributebehavesinawaysimilartotheObjective-Ccopypropertyattribute.

NSManagedApplythisattributetoaninstancemethodorstoredvariablepropertyofaclassthatinheritsfromNSManagedObjecttoindicatethatCoreDatadynamicallyprovidesitsimplementationatruntime,basedontheassociatedentitydescription.ForapropertymarkedwiththeNSManagedattribute,CoreDataalsoprovidesthestorageatruntime.Applyingthisattributealsoimpliestheobjcattribute.

objcApplythisattributetoanydeclarationthatcanberepresentedinObjective-C—forexample,nonnestedclasses,protocols,nongenericenumerations(constrainedtointegerraw-valuetypes),propertiesandmethods(includinggettersandsetters)ofclasses,protocolsandoptionalmembersofaprotocol,initializers,andsubscripts.TheobjcattributetellsthecompilerthatadeclarationisavailabletouseinObjective-Ccode.

Applyingthisattributetoanextensionhasthesameeffectasapplyingittoeverymemberofthatextensionthatisn’texplicitlymarkedwiththenonobjcattribute.

ThecompilerimplicitlyaddstheobjcattributetosubclassesofanyclassdefinedinObjective-C.However,thesubclassmustnotbegeneric,andmustnotinheritfromanygenericclasses.Youcanexplicitlyaddtheobjcattributetoasubclassthatmeetsthesecriteria,tospecifyitsObjective-Cnameasdiscussedbelow.Protocolsthataremarkedwiththeobjcattributecan’tinheritfromprotocolsthataren’tmarkedwiththisattribute.

Theobjcattributeisalsoimplicitlyaddedinthefollowingcases:

Thedeclarationisanoverrideinasubclass,andthesuperclass’sdeclarationhastheobjcattribute.

Thedeclarationsatisfiesarequirementfromaprotocolthathastheobjcattribute.

ThedeclarationhastheIBAction,IBSegueAction,IBOutlet,IBDesignable,IBInspectable,NSManaged,orGKInspectableattribute.

Ifyouapplytheobjcattributetoanenumeration,eachenumerationcaseisexposedtoObjective-Ccodeastheconcatenationoftheenumerationnameandthecasename.Thefirstletterofthecasenameiscapitalized.Forexample,acasenamedvenusinaSwiftPlanetenumerationisexposedtoObjective-CcodeasacasenamedPlanetVenus.

Theobjcattributeoptionallyacceptsasingleattributeargument,whichconsistsofanidentifier.TheidentifierspecifiesthenametobeexposedtoObjective-Cfortheentitythattheobjcattributeappliesto.Youcanusethisargumenttonameclasses,enumerations,enumerationcases,protocols,methods,getters,setters,andinitializers.IfyouspecifytheObjective-Cnameforaclass,protocol,orenumeration,includeathree-letterprefixonthename,asdescribedinConventionsinProgrammingwithObjective-C.TheexamplebelowexposesthegetterfortheenabledpropertyoftheExampleClasstoObjective-CcodeasisEnabledratherthanjustasthenameofthepropertyitself.

1 classExampleClass:NSObject{

2 @objcvarenabled:Bool{

3 @objc(isEnabled)get{

4 //Returntheappropriatevalue

5 }

6 }

7 }

objcMembersApplythisattributetoaclassdeclaration,toimplicitlyapplytheobjcattributetoallObjective-Ccompatiblemembersoftheclass,itsextensions,itssubclasses,andalloftheextensionsofitssubclasses.

Mostcodeshouldusetheobjcattributeinstead,toexposeonlythedeclarationsthatareneeded.Ifyouneedtoexposemanydeclarations,youcangrouptheminanextensionthathastheobjcattribute.TheobjcMembersattributeisaconvenienceforlibrariesthatmakeheavyuseoftheintrospectionfacilitiesoftheObjective-Cruntime.Applyingtheobjcattributewhenitisn’tneededcanincreaseyourbinarysizeandadverselyaffectperformance.

propertyWrapperApplythisattributetoaclass,structure,orenumerationdeclarationtousethattypeasapropertywrapper.Whenyouapplythisattributetoatype,youcreateacustomattributewiththesamenameasthetype.Applythatnewattributetoapropertyofaclass,structure,orenumerationtowrapaccesstothepropertythroughaninstanceofthewrappertype.Localandglobalvariablescan’tusepropertywrappers.

ThewrappermustdefineawrappedValueinstanceproperty.Thewrappedvalueofthepropertyisthevaluethatthegetterandsetterforthispropertyexpose.Inmostcases,wrappedValueisacomputedvalue,butitcanbeastoredvalueinstead.Thewrapperisresponsiblefordefiningandmanaginganyunderlyingstorageneededbyitswrappedvalue.Thecompilersynthesizesstoragefortheinstanceofthewrappertypebyprefixingthenameofthewrappedpropertywithanunderscore(_)—forexample,thewrapperforsomePropertyisstoredas_someProperty.Thesynthesizedstorageforthewrapperhasanaccesscontrollevelofprivate.

ApropertythathasapropertywrappercanincludewillSetanddidSetblocks,butitcan’toverridethecompiler-synthesizedgetorsetblocks.

Swiftprovidestwoformsofsyntacticsugarforinitializationofapropertywrapper.Youcanuseassignmentsyntaxinthedefinitionofawrappedvaluetopasstheexpressionontheright-handsideoftheassignmentastheargumenttothewrappedValueparameterofthepropertywrapper’sinitializer.Youcanalsoprovideargumentstotheattributewhenyouapplyittoaproperty,andthoseargumentsarepassedtothepropertywrapper’sinitializer.Forexample,inthecodebelow,SomeStructcallseachoftheinitializersthatSomeWrapperdefines.

1 @propertyWrapper

2 structSomeWrapper{

3 varwrappedValue:Int

4 varsomeValue:Double

5 init(){

6 self.wrappedValue=100

7 self.someValue=12.3

8 }

9 init(wrappedValue:Int){

10 self.wrappedValue=wrappedValue

11 self.someValue=45.6

12 }

13 init(wrappedValuevalue:Int,custom:Double){

14 self.wrappedValue=value

15 self.someValue=custom

16 }

17 }

18

19 structSomeStruct{

20 //Usesinit()

21 @SomeWrappervara:Int

22

23 //Usesinit(wrappedValue:)

24 @SomeWrappervarb=10

25

26 //Bothuseinit(wrappedValue:custom:)

27 @SomeWrapper(custom:98.7)varc=30

28 @SomeWrapper(wrappedValue:30,custom:98.7)vard

29 }

Theprojectedvalueforawrappedpropertyisasecondvaluethatapropertywrappercanusetoexposeadditionalfunctionality.Theauthorofapropertywrappertypeisresponsiblefordeterminingthemeaningofitsprojectedvalueanddefiningtheinterfacethattheprojectedvalueexposes.Toprojectavaluefromapropertywrapper,defineaprojectedValueinstancepropertyonthewrappertype.Thecompilersynthesizesanidentifierfortheprojectedvaluebyprefixingthenameofthewrappedpropertywithadollarsign($)—forexample,theprojectedvalueforsomePropertyis$someProperty.Theprojectedvaluehasthesameaccesscontrollevelastheoriginalwrappedproperty.

1 @propertyWrapper

2 structWrapperWithProjection{

3 varwrappedValue:Int

4 varprojectedValue:SomeProjection{

5 returnSomeProjection(wrapper:self)

6 }

7 }

8 structSomeProjection{

9 varwrapper:WrapperWithProjection

10 }

11

12 structSomeStruct{

13 @WrapperWithProjectionvarx=123

14 }

15 lets=SomeStruct()

16 s.x//Intvalue

17 s.$x//SomeProjectionvalue

18 s.$x.wrapper//WrapperWithProjectionvalue

requires_stored_property_initsApplythisattributetoaclassdeclarationtorequireallstoredpropertieswithin

theclasstoprovidedefaultvaluesaspartoftheirdefinitions.ThisattributeisinferredforanyclassthatinheritsfromNSManagedObject.

testableApplythisattributetoanimportdeclarationtoimportthatmodulewithchangestoitsaccesscontrolthatsimplifytestingthemodule’scode.Entitiesintheimportedmodulethataremarkedwiththeinternalaccess-levelmodifierareimportedasiftheyweredeclaredwiththepublicaccess-levelmodifier.Classesandclassmembersthataremarkedwiththeinternalorpublicaccess-levelmodifierareimportedasiftheyweredeclaredwiththeopenaccess-levelmodifier.Theimportedmodulemustbecompiledwithtestingenabled.

UIApplicationMainApplythisattributetoaclasstoindicatethatit’stheapplicationdelegate.UsingthisattributeisequivalenttocallingtheUIApplicationMainfunctionandpassingthisclass’snameasthenameofthedelegateclass.

Ifyoudon’tusethisattribute,supplyamain.swiftfilewithcodeatthetoplevelthatcallstheUIApplicationMain(_:_:_:_:)function.Forexample,ifyourappusesacustomsubclassofUIApplicationasitsprincipalclass,calltheUIApplicationMain(_:_:_:_:)functioninsteadofusingthisattribute.

usableFromInlineApplythisattributetoafunction,method,computedproperty,subscript,initializer,ordeinitializerdeclarationtoallowthatsymboltobeusedininlinablecodethat’sdefinedinthesamemoduleasthedeclaration.Thedeclarationmusthavetheinternalaccesslevelmodifier.AstructureorclassmarkedusableFromInlinecanuseonlytypesthatarepublicorusableFromInlineforitsproperties.AnenumerationmarkedusableFromInlinecanuseonlytypesthatarepublicorusableFromInlinefortherawvaluesandassociatedvaluesofitscases.

Likethepublicaccesslevelmodifier,thisattributeexposesthedeclarationas

partofthemodule’spublicinterface.Unlikepublic,thecompilerdoesn’tallowdeclarationsmarkedwithusableFromInlinetobereferencedbynameincodeoutsidethemodule,eventhoughthedeclaration’ssymbolisexported.However,codeoutsidethemodulemightstillbeabletointeractwiththedeclaration’ssymbolbyusingruntimebehavior.

Declarationsmarkedwiththeinlinableattributeareimplicitlyusablefrominlinablecode.AlthougheitherinlinableorusableFromInlinecanbeappliedtointernaldeclarations,applyingbothattributesisanerror.

warn_unqualified_accessApplythisattributetoatop-levelfunction,instancemethod,orclassorstaticmethodtotriggerwarningswhenthatfunctionormethodisusedwithoutaprecedingqualifier,suchasamodulename,typename,orinstancevariableorconstant.Usethisattributetohelpdiscourageambiguitybetweenfunctionswiththesamenamethatareaccessiblefromthesamescope.

Forexample,theSwiftstandardlibraryincludesbothatop-levelmin(_:_:)functionandamin()methodforsequenceswithcomparableelements.Thesequencemethodisdeclaredwiththewarn_unqualified_accessattributetohelpreduceconfusionwhenattemptingtouseoneortheotherfromwithinaSequenceextension.

DeclarationAttributesUsedbyInterfaceBuilderInterfaceBuilderattributesaredeclarationattributesusedbyInterfaceBuildertosynchronizewithXcode.SwiftprovidesthefollowingInterfaceBuilderattributes:IBAction,IBSegueAction,IBOutlet,IBDesignable,andIBInspectable.TheseattributesareconceptuallythesameastheirObjective-Ccounterparts.

YouapplytheIBOutletandIBInspectableattributestopropertydeclarationsofaclass.YouapplytheIBActionandIBSegueActionattributetomethoddeclarationsofaclassandtheIBDesignableattributetoclassdeclarations.

ApplyingtheIBAction,IBSegueAction,IBOutlet,IBDesignable,or

IBInspectableattributealsoimpliestheobjcattribute.

TypeAttributes

Youcanapplytypeattributestotypesonly.

autoclosureApplythisattributetodelaytheevaluationofanexpressionbyautomaticallywrappingthatexpressioninaclosurewithnoarguments.Youapplyittoaparameter’stypeinamethodorfunctiondeclaration,foraparameterwhosetypeisafunctiontypethattakesnoargumentsandthatreturnsavalueofthetypeoftheexpression.Foranexampleofhowtousetheautoclosureattribute,seeAutoclosuresandFunctionType.

conventionApplythisattributetothetypeofafunctiontoindicateitscallingconventions.

Theconventionattributealwaysappearswithoneofthefollowingarguments:

TheswiftargumentindicatesaSwiftfunctionreference.ThisisthestandardcallingconventionforfunctionvaluesinSwift.

TheblockargumentindicatesanObjective-Ccompatibleblockreference.Thefunctionvalueisrepresentedasareferencetotheblockobject,whichisanid-compatibleObjective-Cobjectthatembedsitsinvocationfunctionwithintheobject.TheinvocationfunctionusestheCcallingconvention.

ThecargumentindicatesaCfunctionreference.ThefunctionvaluecarriesnocontextandusestheCcallingconvention.

Withafewexceptions,afunctionofanycallingconventioncanbeusedwhenafunctionanyothercallingconventionisneeded.Anongenericglobalfunction,a

localfunctionthatdoesn’tcaptureanylocalvariablesoraclosurethatdoesn’tcaptureanylocalvariablescanbeconvertedtotheCcallingconvention.OtherSwiftfunctionscan’tbeconvertedtotheCcallingconvention.AfunctionwiththeObjective-Cblockcallingconventioncan’tbeconvertedtotheCcallingconvention.

escapingApplythisattributetoaparameter’stypeinamethodorfunctiondeclarationtoindicatethattheparameter’svaluecanbestoredforlaterexecution.Thismeansthatthevalueisallowedtooutlivethelifetimeofthecall.Functiontypeparameterswiththeescapingtypeattributerequireexplicituseofself.forpropertiesormethods.Foranexampleofhowtousetheescapingattribute,seeEscapingClosures.

SwitchCaseAttributes

Youcanapplyswitchcaseattributestoswitchcasesonly.

unknownApplythisattributetoaswitchcasetoindicatethatitisn’texpectedtobematchedbyanycaseoftheenumerationthat’sknownatthetimethecodeiscompiled.Foranexampleofhowtousetheunknownattribute,seeSwitchingOverFutureEnumerationCases.

GRAMMAR OF AN ATTR IBUTE

attribute → @ attribute-name attribute-argument-clause optattribute-name → identifierattribute-argument-clause → ( balanced-tokens opt )attributes → attribute attributes optbalanced-tokens → balanced-token balanced-tokens optbalanced-token → ( balanced-tokens opt )balanced-token → [ balanced-tokens opt ]

balanced-token → { balanced-tokens opt }balanced-token → Anyidentifier,keyword,literal,oroperatorbalanced-token → Anypunctuationexcept ( , ) , [ , ] , { ,or }

Patterns

Apatternrepresentsthestructureofasinglevalueoracompositevalue.Forexample,thestructureofatuple(1,2)isacomma-separatedlistoftwoelements.Becausepatternsrepresentthestructureofavalueratherthananyoneparticularvalue,youcanmatchthemwithavarietyofvalues.Forinstance,thepattern(x,y)matchesthetuple(1,2)andanyothertwo-elementtuple.Inadditiontomatchingapatternwithavalue,youcanextractpartorallofacompositevalueandbindeachparttoaconstantorvariablename.

InSwift,therearetwobasickindsofpatterns:thosethatsuccessfullymatchanykindofvalue,andthosethatmayfailtomatchaspecifiedvalueatruntime.

Thefirstkindofpatternisusedfordestructuringvaluesinsimplevariable,constant,andoptionalbindings.Theseincludewildcardpatterns,identifierpatterns,andanyvaluebindingortuplepatternscontainingthem.Youcanspecifyatypeannotationforthesepatternstoconstrainthemtomatchonlyvaluesofacertaintype.

Thesecondkindofpatternisusedforfullpatternmatching,wherethevaluesyou’retryingtomatchagainstmaynotbethereatruntime.Theseincludeenumerationcasepatterns,optionalpatterns,expressionpatterns,andtype-castingpatterns.Youusethesepatternsinacaselabelofaswitchstatement,acatchclauseofadostatement,orinthecaseconditionofanif,while,guard,orfor-instatement.

GRAMMAR OF A PATTERN

pattern → wildcard-pattern type-annotation optpattern → identifier-pattern type-annotation optpattern → value-binding-patternpattern → tuple-pattern type-annotation optpattern → enum-case-patternpattern → optional-patternpattern → type-casting-patternpattern → expression-pattern

WildcardPattern

Awildcardpatternmatchesandignoresanyvalueandconsistsofanunderscore(_).Useawildcardpatternwhenyoudon’tcareaboutthevaluesbeingmatchedagainst.Forexample,thefollowingcodeiteratesthroughtheclosedrange1...3,ignoringthecurrentvalueoftherangeoneachiterationoftheloop:

1 for_in1...3{

2 //Dosomethingthreetimes.

3 }

GRAMMAR OF AW I LDCARD PATTERN

wildcard-pattern → _

IdentifierPattern

Anidentifierpatternmatchesanyvalueandbindsthematchedvaluetoavariableorconstantname.Forexample,inthefollowingconstantdeclaration,someValueisanidentifierpatternthatmatchesthevalue42oftypeInt:

letsomeValue=42

Whenthematchsucceeds,thevalue42isbound(assigned)totheconstantnamesomeValue.

Whenthepatternontheleft-handsideofavariableorconstantdeclarationisanidentifierpattern,theidentifierpatternisimplicitlyasubpatternofavalue-bindingpattern.

GRAMMAR OF AN I DENT I F I ER PATTERN

identifier-pattern → identifier

Value-BindingPattern

Avalue-bindingpatternbindsmatchedvaluestovariableorconstantnames.Value-bindingpatternsthatbindamatchedvaluetothenameofaconstantbeginwiththeletkeyword;thosethatbindtothenameofvariablebeginwiththevarkeyword.

Identifierspatternswithinavalue-bindingpatternbindnewnamedvariablesorconstantstotheirmatchingvalues.Forexample,youcandecomposetheelementsofatupleandbindthevalueofeachelementtoacorrespondingidentifierpattern.

1 letpoint=(3,2)

2 switchpoint{

3 //Bindxandytotheelementsofpoint.

4 caselet(x,y):

5 print("Thepointisat(\(x),\(y)).")

6 }

7 //Prints"Thepointisat(3,2)."

Intheexampleabove,letdistributestoeachidentifierpatterninthetuplepattern(x,y).Becauseofthisbehavior,theswitchcasescaselet(x,y):andcase(letx,lety):matchthesamevalues.

GRAMMAR OF A VALUE -B IND ING PATTERN

value-binding-pattern → var pattern | let pattern

TuplePattern

Atuplepatternisacomma-separatedlistofzeroormorepatterns,enclosedinparentheses.Tuplepatternsmatchvaluesofcorrespondingtupletypes.

Youcanconstrainatuplepatterntomatchcertainkindsoftupletypesbyusing

typeannotations.Forexample,thetuplepattern(x,y):(Int,Int)intheconstantdeclarationlet(x,y):(Int,Int)=(1,2)matchesonlytupletypesinwhichbothelementsareoftypeInt.

Whenatuplepatternisusedasthepatterninafor-instatementorinavariableorconstantdeclaration,itcancontainonlywildcardpatterns,identifierpatterns,optionalpatterns,orothertuplepatternsthatcontainthose.Forexample,thefollowingcodeisn’tvalidbecausetheelement0inthetuplepattern(x,0)isanexpressionpattern:

1 letpoints=[(0,0),(1,0),(1,1),(2,0),(2,1)]

2 //Thiscodeisn'tvalid.

3 for(x,0)inpoints{

4 /*...*/

5 }

Theparenthesesaroundatuplepatternthatcontainsasingleelementhavenoeffect.Thepatternmatchesvaluesofthatsingleelement’stype.Forexample,thefollowingareequivalent:

1 leta=2//a:Int=2

2 let(a)=2//a:Int=2

3 let(a):Int=2//a:Int=2

GRAMMAR OF ATUPLE PATTERN

tuple-pattern → ( tuple-pattern-element-list opt )tuple-pattern-element-list → tuple-pattern-element | tuple-pattern-element , tuple-

pattern-element-listtuple-pattern-element → pattern | identifier : pattern

EnumerationCasePattern

Anenumerationcasepatternmatchesacaseofanexistingenumerationtype.

Enumerationcasepatternsappearinswitchstatementcaselabelsandinthecaseconditionsofif,while,guard,andfor-instatements.

Iftheenumerationcaseyou’retryingtomatchhasanyassociatedvalues,thecorrespondingenumerationcasepatternmustspecifyatuplepatternthatcontainsoneelementforeachassociatedvalue.Foranexamplethatusesaswitchstatementtomatchenumerationcasescontainingassociatedvalues,seeAssociatedValues.

Anenumerationcasepatternalsomatchesvaluesofthatcasewrappedinanoptional.Thissimplifiedsyntaxletsyouomitanoptionalpattern.Notethat,becauseOptionalisimplementedasanenumeration,.noneand.somecanappearinthesameswitchasthecasesoftheenumerationtype.

1 enumSomeEnum{caseleft,right}

2 letx:SomeEnum?=.left

3 switchx{

4 case.left:

5 print("Turnleft")

6 case.right:

7 print("Turnright")

8 casenil:

9 print("Keepgoingstraight")

10 }

11 //Prints"Turnleft"

GRAMMAR OF AN ENUMERAT ION CASE PATTERN

enum-case-pattern → type-identifier opt . enum-case-name tuple-pattern opt

OptionalPattern

Anoptionalpatternmatchesvalueswrappedinasome(Wrapped)caseofan

Optional<Wrapped>enumeration.Optionalpatternsconsistofanidentifierpatternfollowedimmediatelybyaquestionmarkandappearinthesameplacesasenumerationcasepatterns.

BecauseoptionalpatternsaresyntacticsugarforOptionalenumerationcasepatterns,thefollowingareequivalent:

1 letsomeOptional:Int?=42

2 //Matchusinganenumerationcasepattern.

3 ifcase.some(letx)=someOptional{

4 print(x)

5 }

6

7 //Matchusinganoptionalpattern.

8 ifcaseletx?=someOptional{

9 print(x)

10 }

Theoptionalpatternprovidesaconvenientwaytoiterateoveranarrayofoptionalvaluesinafor-instatement,executingthebodyofthelooponlyfornon-nilelements.

1 letarrayOfOptionalInts:[Int?]=[nil,2,3,nil,5]

2 //Matchonlynon-nilvalues.

3 forcaseletnumber?inarrayOfOptionalInts{

4 print("Founda\(number)")

5 }

6 //Founda2

7 //Founda3

8 //Founda5

GRAMMAR OF AN OPT IONALPATTERN

optional-pattern → identifier-pattern ?

Type-CastingPatterns

Therearetwotype-castingpatterns,theispatternandtheaspattern.Theispatternappearsonlyinswitchstatementcaselabels.Theisandaspatternshavethefollowingform:

is type

pattern as type

Theispatternmatchesavalueifthetypeofthatvalueatruntimeisthesameasthetypespecifiedintheright-handsideoftheispattern—orasubclassofthattype.Theispatternbehavesliketheisoperatorinthattheybothperformatypecastbutdiscardthereturnedtype.

Theaspatternmatchesavalueifthetypeofthatvalueatruntimeisthesameasthetypespecifiedintheright-handsideoftheaspattern—orasubclassofthattype.Ifthematchsucceeds,thetypeofthematchedvalueiscasttothepatternspecifiedintheright-handsideoftheaspattern.

Foranexamplethatusesaswitchstatementtomatchvalueswithisandaspatterns,seeTypeCastingforAnyandAnyObject.

GRAMMAR OF ATYPE CAST ING PATTERN

type-casting-pattern → is-pattern | as-patternis-pattern → is typeas-pattern → pattern as type

ExpressionPattern

Anexpressionpatternrepresentsthevalueofanexpression.Expressionpatternsappearonlyinswitchstatementcaselabels.

TheexpressionrepresentedbytheexpressionpatterniscomparedwiththevalueofaninputexpressionusingtheSwiftstandardlibrary~=operator.Thematchessucceedsifthe~=operatorreturnstrue.Bydefault,the~=operatorcomparestwovaluesofthesametypeusingthe==operator.Itcanalsomatchavaluewitharangeofvalues,bycheckingwhetherthevalueiscontainedwithintherange,asthefollowingexampleshows.

1 letpoint=(1,2)

2 switchpoint{

3 case(0,0):

4 print("(0,0)isattheorigin.")

5 case(-2...2,-2...2):

6 print("(\(point.0),\(point.1))isneartheorigin.")

7 default:

8 print("Thepointisat(\(point.0),\(point.1)).")

9 }

10 //Prints"(1,2)isneartheorigin."

Youcanoverloadthe~=operatortoprovidecustomexpressionmatchingbehavior.Forexample,youcanrewritetheaboveexampletocomparethepointexpressionwithastringrepresentationsofpoints.

1 //Overloadthe~=operatortomatchastringwithaninteger.

2 func~=(pattern:String,value:Int)->Bool{

3 returnpattern=="\(value)"

4 }

5 switchpoint{

6 case("0","0"):

7 print("(0,0)isattheorigin.")

8 default:

9 print("Thepointisat(\(point.0),\(point.1)).")

10 }

11 //Prints"Thepointisat(1,2)."

GRAMMAR OF AN EXPRESS ION PATTERN

expression-pattern → expression

GenericParametersandArguments

Thischapterdescribesparametersandargumentsforgenerictypes,functions,andinitializers.Whenyoudeclareagenerictype,function,subscript,orinitializer,youspecifythetypeparametersthatthegenerictype,function,orinitializercanworkwith.Thesetypeparametersactasplaceholdersthatarereplacedbyactualconcretetypeargumentswhenaninstanceofagenerictypeiscreatedoragenericfunctionorinitializeriscalled.

ForanoverviewofgenericsinSwift,seeGenerics.

GenericParameterClause

Agenericparameterclausespecifiesthetypeparametersofagenerictypeorfunction,alongwithanyassociatedconstraintsandrequirementsonthoseparameters.Agenericparameterclauseisenclosedinanglebrackets(<>)andhasthefollowingform:

< genericparameterlist >

Thegenericparameterlistisacomma-separatedlistofgenericparameters,eachofwhichhasthefollowingform:

typeparameter : constraint

Agenericparameterconsistsofatypeparameterfollowedbyanoptionalconstraint.Atypeparameterissimplythenameofaplaceholdertype(forexample,T,U,V,Key,Value,andsoon).Youhaveaccesstothetypeparameters(andanyoftheirassociatedtypes)intherestofthetype,function,orinitializerdeclaration,includinginthesignatureofthefunctionorinitializer.

Theconstraintspecifiesthatatypeparameterinheritsfromaspecificclassorconformstoaprotocolorprotocolcomposition.Forexample,inthegeneric

functionbelow,thegenericparameterT:ComparableindicatesthatanytypeargumentsubstitutedforthetypeparameterTmustconformtotheComparableprotocol.

1 funcsimpleMax<T:Comparable>(_x:T,_y:T)->T{

2 ifx<y{

3 returny

4 }

5 returnx

6 }

BecauseIntandDouble,forexample,bothconformtotheComparableprotocol,thisfunctionacceptsargumentsofeithertype.Incontrastwithgenerictypes,youdon’tspecifyagenericargumentclausewhenyouuseagenericfunctionorinitializer.Thetypeargumentsareinsteadinferredfromthetypeoftheargumentspassedtothefunctionorinitializer.

1 simpleMax(17,42)//TisinferredtobeInt

2 simpleMax(3.14159,2.71828)//TisinferredtobeDouble

GenericWhereClausesYoucanspecifyadditionalrequirementsontypeparametersandtheirassociatedtypesbyincludingagenericwhereclauserightbeforetheopeningcurlybraceofatypeorfunction’sbody.Agenericwhereclauseconsistsofthewherekeyword,followedbyacomma-separatedlistofoneormorerequirements.

where requirements

Therequirementsinagenericwhereclausespecifythatatypeparameterinheritsfromaclassorconformstoaprotocolorprotocolcomposition.Althoughthegenericwhereclauseprovidessyntacticsugarforexpressingsimpleconstraintsontypeparameters(forexample,<T:Comparable>isequivalentto<T>whereT:Comparableandsoon),youcanuseittoprovidemorecomplexconstraintson

typeparametersandtheirassociatedtypes.Forexample,youcanconstraintheassociatedtypesoftypeparameterstoconformtoprotocols.Forexample,<S:Sequence>whereS.Iterator.Element:EquatablespecifiesthatSconformstotheSequenceprotocolandthattheassociatedtypeS.Iterator.ElementconformstotheEquatableprotocol.Thisconstraintensuresthateachelementofthesequenceisequatable.

Youcanalsospecifytherequirementthattwotypesbeidentical,usingthe==operator.Forexample,<S1:Sequence,S2:Sequence>whereS1.Iterator.Element==S2.Iterator.ElementexpressestheconstraintsthatS1andS2conformtotheSequenceprotocolandthattheelementsofbothsequencesmustbeofthesametype.

Anytypeargumentsubstitutedforatypeparametermustmeetalltheconstraintsandrequirementsplacedonthetypeparameter.

Youcanoverloadagenericfunctionorinitializerbyprovidingdifferentconstraints,requirements,orbothonthetypeparameters.Whenyoucallanoverloadedgenericfunctionorinitializer,thecompilerusestheseconstraintstoresolvewhichoverloadedfunctionorinitializertoinvoke.

Formoreinformationaboutgenericwhereclausesandtoseeanexampleofoneinagenericfunctiondeclaration,seeGenericWhereClauses.

GRAMMAR OF AGENER IC PARAMETER CLAUSE

generic-parameter-clause → < generic-parameter-list >generic-parameter-list → generic-parameter | generic-parameter , generic-parameter-

listgeneric-parameter → type-namegeneric-parameter → type-name : type-identifiergeneric-parameter → type-name : protocol-composition-typegeneric-where-clause → where requirement-listrequirement-list → requirement | requirement , requirement-listrequirement → conformance-requirement | same-type-requirementconformance-requirement → type-identifier : type-identifierconformance-requirement → type-identifier : protocol-composition-typesame-type-requirement → type-identifier == type

GenericArgumentClause

Agenericargumentclausespecifiesthetypeargumentsofagenerictype.Agenericargumentclauseisenclosedinanglebrackets(<>)andhasthefollowingform:

< genericargumentlist >

Thegenericargumentlistisacomma-separatedlistoftypearguments.Atypeargumentisthenameofanactualconcretetypethatreplacesacorrespondingtypeparameterinthegenericparameterclauseofagenerictype.Theresultisaspecializedversionofthatgenerictype.TheexamplebelowshowsasimplifiedversionoftheSwiftstandardlibrary’sgenericdictionarytype.

1 structDictionary<Key:Hashable,Value>:Collection,

ExpressibleByDictionaryLiteral{

2 /*...*/

3 }

ThespecializedversionofthegenericDictionarytype,Dictionary<String,Int>isformedbyreplacingthegenericparametersKey:HashableandValuewiththeconcretetypeargumentsStringandInt.Eachtypeargumentmustsatisfyalltheconstraintsofthegenericparameteritreplaces,includinganyadditionalrequirementsspecifiedinagenericwhereclause.Intheexampleabove,theKeytypeparameterisconstrainedtoconformtotheHashableprotocolandthereforeStringmustalsoconformtotheHashableprotocol.

Youcanalsoreplaceatypeparameterwithatypeargumentthatisitselfaspecializedversionofagenerictype(provideditsatisfiestheappropriateconstraintsandrequirements).Forexample,youcanreplacethetypeparameterElementinArray<Element>withaspecializedversionofanarray,Array<Int>,toformanarraywhoseelementsarethemselvesarraysofintegers.

letarrayOfArrays:Array<Array<Int>>=[[1,2,3],[4,5,6],

[7,8,9]]

AsmentionedinGenericParameterClause,youdon’tuseagenericargumentclausetospecifythetypeargumentsofagenericfunctionorinitializer.

GRAMMAR OF AGENER IC ARGUMENT CLAUSE

generic-argument-clause → < generic-argument-list >generic-argument-list → generic-argument | generic-argument , generic-argument-listgeneric-argument → type

SummaryoftheGrammar

LexicalStructure

GRAMMAR OF WH I TESPACE

whitespace → whitespace-item whitespace optwhitespace-item → line-breakwhitespace-item → commentwhitespace-item → multiline-commentwhitespace-item → U+0000,U+0009,U+000B,U+000C,orU+0020line-break → U+000Aline-break → U+000Dline-break → U+000DfollowedbyU+000Acomment → // comment-text line-breakmultiline-comment → /* multiline-comment-text */comment-text → comment-text-item comment-text optcomment-text-item → AnyUnicodescalarvalueexceptU+000AorU+000Dmultiline-comment-text → multiline-comment-text-item multiline-comment-text optmultiline-comment-text-item → multiline-commentmultiline-comment-text-item → comment-text-itemmultiline-comment-text-item → AnyUnicodescalarvalueexcept /* or */

GRAMMAR OF AN I DENT I F I ER

identifier → identifier-head identifier-characters optidentifier → ` identifier-head identifier-characters opt `identifier → implicit-parameter-nameidentifier → property-wrapper-projectionidentifier-list → identifier | identifier , identifier-listidentifier-head → Upper-orlowercaseletterAthroughZidentifier-head → _identifier-head → U+00A8,U+00AA,U+00AD,U+00AF,U+00B2–U+00B5,orU+00B7–

U+00BAidentifier-head → U+00BC–U+00BE,U+00C0–U+00D6,U+00D8–U+00F6,orU+00F8–

U+00FFidentifier-head → U+0100–U+02FF,U+0370–U+167F,U+1681–U+180D,orU+180F–

U+1DBFidentifier-head → U+1E00–U+1FFFidentifier-head → U+200B–U+200D,U+202A–U+202E,U+203F–U+2040,U+2054,or

U+2060–U+206Fidentifier-head → U+2070–U+20CF,U+2100–U+218F,U+2460–U+24FF,orU+2776–

U+2793identifier-head → U+2C00–U+2DFForU+2E80–U+2FFFidentifier-head → U+3004–U+3007,U+3021–U+302F,U+3031–U+303F,orU+3040–

U+D7FFidentifier-head → U+F900–U+FD3D,U+FD40–U+FDCF,U+FDF0–U+FE1F,orU+FE30–

U+FE44identifier-head → U+FE47–U+FFFDidentifier-head → U+10000–U+1FFFD,U+20000–U+2FFFD,U+30000–U+3FFFD,or

U+40000–U+4FFFDidentifier-head → U+50000–U+5FFFD,U+60000–U+6FFFD,U+70000–U+7FFFD,or

U+80000–U+8FFFDidentifier-head → U+90000–U+9FFFD,U+A0000–U+AFFFD,U+B0000–U+BFFFD,or

U+C0000–U+CFFFDidentifier-head → U+D0000–U+DFFFDorU+E0000–U+EFFFDidentifier-character → Digit0through9identifier-character → U+0300–U+036F,U+1DC0–U+1DFF,U+20D0–U+20FF,or

U+FE20–U+FE2Fidentifier-character → identifier-headidentifier-characters → identifier-character identifier-characters optimplicit-parameter-name → $ decimal-digitsproperty-wrapper-projection → $ identifier-characters

GRAMMAR OF A L I TERAL

literal → numeric-literal | string-literal | boolean-literal | nil-literalnumeric-literal → -opt integer-literal | -opt floating-point-literalboolean-literal → true | falsenil-literal → nil

GRAMMAR OF AN I NTEGER L I TERAL

integer-literal → binary-literalinteger-literal → octal-literalinteger-literal → decimal-literalinteger-literal → hexadecimal-literalbinary-literal → 0b binary-digit binary-literal-characters optbinary-digit → Digit0or1binary-literal-character → binary-digit | _binary-literal-characters → binary-literal-character binary-literal-characters optoctal-literal → 0o octal-digit octal-literal-characters optoctal-digit → Digit0through7octal-literal-character → octal-digit | _octal-literal-characters → octal-literal-character octal-literal-characters optdecimal-literal → decimal-digit decimal-literal-characters optdecimal-digit → Digit0through9decimal-digits → decimal-digit decimal-digits optdecimal-literal-character → decimal-digit | _

decimal-literal-characters → decimal-literal-character decimal-literal-characters opthexadecimal-literal → 0x hexadecimal-digit hexadecimal-literal-characters opthexadecimal-digit → Digit0through9,athroughf,orAthroughFhexadecimal-literal-character → hexadecimal-digit | _hexadecimal-literal-characters → hexadecimal-literal-character hexadecimal-literal-

characters opt

GRAMMAR OF A F LOAT ING -PO INT L I TERAL

floating-point-literal → decimal-literal decimal-fraction opt decimal-exponent optfloating-point-literal → hexadecimal-literal hexadecimal-fraction opt hexadecimal-

exponentdecimal-fraction → . decimal-literaldecimal-exponent → floating-point-e sign opt decimal-literalhexadecimal-fraction → . hexadecimal-digit hexadecimal-literal-characters opthexadecimal-exponent → floating-point-p sign opt decimal-literalfloating-point-e → e | Efloating-point-p → p | Psign → + | -

GRAMMAR OF A STR ING L I TERAL

string-literal → static-string-literal | interpolated-string-literalstring-literal-opening-delimiter → extended-string-literal-delimiter opt "string-literal-closing-delimiter → " extended-string-literal-delimiter optstatic-string-literal → string-literal-opening-delimiter quoted-text opt string-literal-closing-

delimiterstatic-string-literal → multiline-string-literal-opening-delimiter multiline-quoted-text opt

multiline-string-literal-closing-delimitermultiline-string-literal-opening-delimiter → extended-string-literal-delimiter """multiline-string-literal-closing-delimiter → """ extended-string-literal-delimiterextended-string-literal-delimiter → # extended-string-literal-delimiter optquoted-text → quoted-text-item quoted-text optquoted-text-item → escaped-characterquoted-text-item → AnyUnicodescalarvalueexcept " , \ ,U+000A,orU+000Dmultiline-quoted-text → multiline-quoted-text-item multiline-quoted-text optmultiline-quoted-text-item → escaped-charactermultiline-quoted-text-item → AnyUnicodescalarvalueexcept \multiline-quoted-text-item → escaped-newlineinterpolated-string-literal → string-literal-opening-delimiter interpolated-text opt string-

literal-closing-delimiterinterpolated-string-literal → multiline-string-literal-opening-delimiter interpolated-text opt

multiline-string-literal-closing-delimiterinterpolated-text → interpolated-text-item interpolated-text optinterpolated-text-item → \( expression ) | quoted-text-item

multiline-interpolated-text → multiline-interpolated-text-item multiline-interpolated-text optmultiline-interpolated-text-item → \( expression ) |multiline-quoted-text-itemescape-sequence → \ extended-string-literal-delimiterescaped-character → escape-sequence 0 | escape-sequence \ | escape-sequence t |

escape-sequence n | escape-sequence r | escape-sequence " | escape-sequence'

escaped-character → escape-sequence u { unicode-scalar-digits }unicode-scalar-digits → Betweenoneandeighthexadecimaldigitsescaped-newline → escape-sequence whitespace opt line-break

GRAMMAR OF OPERATORS

operator → operator-head operator-characters optoperator → dot-operator-head dot-operator-charactersoperator-head → / | = | - | + | ! | * | % | < | > | & | | | ^ | ~ | ?operator-head → U+00A1–U+00A7operator-head → U+00A9orU+00ABoperator-head → U+00ACorU+00AEoperator-head → U+00B0–U+00B1operator-head → U+00B6,U+00BB,U+00BF,U+00D7,orU+00F7operator-head → U+2016–U+2017operator-head → U+2020–U+2027operator-head → U+2030–U+203Eoperator-head → U+2041–U+2053operator-head → U+2055–U+205Eoperator-head → U+2190–U+23FFoperator-head → U+2500–U+2775operator-head → U+2794–U+2BFFoperator-head → U+2E00–U+2E7Foperator-head → U+3001–U+3003operator-head → U+3008–U+3020operator-head → U+3030operator-character → operator-headoperator-character → U+0300–U+036Foperator-character → U+1DC0–U+1DFFoperator-character → U+20D0–U+20FFoperator-character → U+FE00–U+FE0Foperator-character → U+FE20–U+FE2Foperator-character → U+E0100–U+E01EFoperator-characters → operator-character operator-characters optdot-operator-head → .dot-operator-character → . | operator-characterdot-operator-characters → dot-operator-character dot-operator-characters optbinary-operator → operatorprefix-operator → operatorpostfix-operator → operator

Types

GRAMMAR OF ATYPE

type → function-typetype → array-typetype → dictionary-typetype → type-identifiertype → tuple-typetype → optional-typetype → implicitly-unwrapped-optional-typetype → protocol-composition-typetype → opaque-typetype → metatype-typetype → self-typetype → Anytype → ( type )

GRAMMAR OF ATYPE ANNOTAT ION

type-annotation → : attributes opt inoutopt type

GRAMMAR OF ATYPE I DENT I F I ER

type-identifier → type-name generic-argument-clause opt | type-name generic-argument-clause opt . type-identifier

type-name → identifier

GRAMMAR OF ATUPLE TYPE

tuple-type → ( ) | ( tuple-type-element , tuple-type-element-list )tuple-type-element-list → tuple-type-element | tuple-type-element , tuple-type-element-

listtuple-type-element → element-name type-annotation | typeelement-name → identifier

GRAMMAR OF A FUNCT ION TYPE

function-type → attributes opt function-type-argument-clause throwsopt -> typefunction-type-argument-clause → ( )function-type-argument-clause → ( function-type-argument-list ...opt )function-type-argument-list → function-type-argument | function-type-argument ,

function-type-argument-listfunction-type-argument → attributes opt inoutopt type | argument-label type-

annotationargument-label → identifier

GRAMMAR OF AN ARRAY TYPE

array-type → [ type ]

GRAMMAR OF AD ICT IONARY TYPE

dictionary-type → [ type : type ]

GRAMMAR OF AN OPT IONALTYPE

optional-type → type ?

GRAMMAR OF AN IMPL IC I T LY UNWRAPPED OPT IONALTYPE

implicitly-unwrapped-optional-type → type !

GRAMMAR OF A PROTOCOLCOMPOS I T ION TYPE

protocol-composition-type → type-identifier & protocol-composition-continuationprotocol-composition-continuation → type-identifier | protocol-composition-type

GRAMMAR OF AN OPAQUE TYPE

opaque-type → some type

GRAMMAR OF AMETATYPE TYPE

metatype-type → type . Type | type . Protocol

GRAMMAR OF A SELF TYPE

self-type → Self

GRAMMAR OF ATYPE I NHER I TANCE CLAUSE

type-inheritance-clause → : type-inheritance-listtype-inheritance-list → type-identifier | type-identifier , type-inheritance-list

Expressions

GRAMMAR OF AN EXPRESS ION

expression → try-operator opt prefix-expression binary-expressions optexpression-list → expression | expression , expression-list

GRAMMAR OF A PREF IX EXPRESS ION

prefix-expression → prefix-operator opt postfix-expressionprefix-expression → in-out-expressionin-out-expression → & identifier

GRAMMAR OF ATRY EXPRESS ION

try-operator → try | try ? | try !

GRAMMAR OF A B INARY EXPRESS ION

binary-expression → binary-operator prefix-expressionbinary-expression → assignment-operator try-operator opt prefix-expressionbinary-expression → conditional-operator try-operator opt prefix-expressionbinary-expression → type-casting-operatorbinary-expressions → binary-expression binary-expressions opt

GRAMMAR OF AN ASS IGNMENT OPERATOR

assignment-operator → =

GRAMMAR OF ACOND I T IONALOPERATOR

conditional-operator → ? expression :

GRAMMAR OF ATYPE -CAST ING OPERATOR

type-casting-operator → is typetype-casting-operator → as typetype-casting-operator → as ? typetype-casting-operator → as ! type

GRAMMAR OF A PR IMARY EXPRESS ION

primary-expression → identifier generic-argument-clause optprimary-expression → literal-expressionprimary-expression → self-expressionprimary-expression → superclass-expressionprimary-expression → closure-expressionprimary-expression → parenthesized-expressionprimary-expression → tuple-expressionprimary-expression → implicit-member-expressionprimary-expression → wildcard-expressionprimary-expression → key-path-expressionprimary-expression → selector-expressionprimary-expression → key-path-string-expression

GRAMMAR OF A L I TERALEXPRESS ION

literal-expression → literalliteral-expression → array-literal | dictionary-literal | playground-literalliteral-expression → #file | #line | #column | #function | #dsohandlearray-literal → [ array-literal-items opt ]array-literal-items → array-literal-item ,opt | array-literal-item , array-literal-itemsarray-literal-item → expressiondictionary-literal → [ dictionary-literal-items ] | [ : ]dictionary-literal-items → dictionary-literal-item ,opt | dictionary-literal-item , dictionary-

literal-itemsdictionary-literal-item → expression : expressionplayground-literal → #colorLiteral ( red : expression , green : expression

, blue : expression , alpha : expression )playground-literal → #fileLiteral ( resourceName : expression )playground-literal → #imageLiteral ( resourceName : expression )

GRAMMAR OF A SELF EXPRESS ION

self-expression → self | self-method-expression | self-subscript-expression | self-initializer-expression

self-method-expression → self . identifierself-subscript-expression → self [ function-call-argument-list ]self-initializer-expression → self . init

GRAMMAR OF A SUPERCLASS EXPRESS ION

superclass-expression → superclass-method-expression | superclass-subscript-expression | superclass-initializer-expression

superclass-method-expression → super . identifiersuperclass-subscript-expression → super [ function-call-argument-list ]superclass-initializer-expression → super . init

GRAMMAR OF ACLOSURE EXPRESS ION

closure-expression → { closure-signature opt statements opt }closure-signature → capture-list opt closure-parameter-clause throwsopt function-

result opt inclosure-signature → capture-list inclosure-parameter-clause → ( ) | ( closure-parameter-list ) | identifier-listclosure-parameter-list → closure-parameter | closure-parameter , closure-parameter-

listclosure-parameter → closure-parameter-name type-annotation optclosure-parameter → closure-parameter-name type-annotation ...closure-parameter-name → identifiercapture-list → [ capture-list-items ]capture-list-items → capture-list-item | capture-list-item , capture-list-items

capture-list-item → capture-specifier opt expressioncapture-specifier → weak | unowned | unowned(safe) | unowned(unsafe)

GRAMMAR OF A IMPL IC I T MEMBER EXPRESS ION

implicit-member-expression → . identifier

GRAMMAR OF A PARENTHES I ZED EXPRESS ION

parenthesized-expression → ( expression )

GRAMMAR OF ATUPLE EXPRESS ION

tuple-expression → ( ) | ( tuple-element , tuple-element-list )tuple-element-list → tuple-element | tuple-element , tuple-element-listtuple-element → expression | identifier : expression

GRAMMAR OF AW I LDCARD EXPRESS ION

wildcard-expression → _

GRAMMAR OF A KEY-PATH EXPRESS ION

key-path-expression → \ type opt . key-path-componentskey-path-components → key-path-component | key-path-component . key-path-

componentskey-path-component → identifier key-path-postfixes opt | key-path-postfixeskey-path-postfixes → key-path-postfix key-path-postfixes optkey-path-postfix → ? | ! | self | [ function-call-argument-list ]

GRAMMAR OF A SELECTOR EXPRESS ION

selector-expression → #selector ( expression )selector-expression → #selector ( getter: expression )selector-expression → #selector ( setter: expression )

GRAMMAR OF A KEY-PATH STR ING EXPRESS ION

key-path-string-expression → #keyPath ( expression )

GRAMMAR OF A POSTF IX EXPRESS ION

postfix-expression → primary-expressionpostfix-expression → postfix-expression postfix-operatorpostfix-expression → function-call-expressionpostfix-expression → initializer-expressionpostfix-expression → explicit-member-expression

postfix-expression → postfix-self-expressionpostfix-expression → subscript-expressionpostfix-expression → forced-value-expressionpostfix-expression → optional-chaining-expression

GRAMMAR OF A FUNCT ION CALL EXPRESS ION

function-call-expression → postfix-expression function-call-argument-clausefunction-call-expression → postfix-expression function-call-argument-clause opt trailing-

closurefunction-call-argument-clause → ( ) | ( function-call-argument-list )function-call-argument-list → function-call-argument | function-call-argument , function-

call-argument-listfunction-call-argument → expression | identifier : expressionfunction-call-argument → operator | identifier : operatortrailing-closure → closure-expression

GRAMMAR OF AN I N I T I A L I ZER EXPRESS ION

initializer-expression → postfix-expression . initinitializer-expression → postfix-expression . init ( argument-names )

GRAMMAR OF AN EXPL IC I T MEMBER EXPRESS ION

explicit-member-expression → postfix-expression . decimal-digitsexplicit-member-expression → postfix-expression . identifier generic-argument-clause

optexplicit-member-expression → postfix-expression . identifier ( argument-names )argument-names → argument-name argument-names optargument-name → identifier :

GRAMMAR OF A POSTF IX SELF EXPRESS ION

postfix-self-expression → postfix-expression . self

GRAMMAR OF A SUBSCR IPT EXPRESS ION

subscript-expression → postfix-expression [ function-call-argument-list ]

GRAMMAR OF A FORCED -VALUE EXPRESS ION

forced-value-expression → postfix-expression !

GRAMMAR OF AN OPT IONAL -CHA IN ING EXPRESS ION

optional-chaining-expression → postfix-expression ?

Statements

GRAMMAR OF A STATEMENT

statement → expression ;optstatement → declaration ;optstatement → loop-statement ;optstatement → branch-statement ;optstatement → labeled-statement ;optstatement → control-transfer-statement ;optstatement → defer-statement ;optstatement → do-statement ;optstatement → compiler-control-statementstatements → statement statements opt

GRAMMAR OF A LOOP STATEMENT

loop-statement → for-in-statementloop-statement → while-statementloop-statement → repeat-while-statement

GRAMMAR OF A FOR - IN STATEMENT

for-in-statement → for caseopt pattern in expression where-clause opt code-block

GRAMMAR OF AWH I LE STATEMENT

while-statement → while condition-list code-blockcondition-list → condition | condition , condition-listcondition → expression | availability-condition | case-condition | optional-binding-

conditioncase-condition → case pattern initializeroptional-binding-condition → let pattern initializer | var pattern initializer

GRAMMAR OF AREPEAT-WH I LE STATEMENT

repeat-while-statement → repeat code-block while expression

GRAMMAR OF A BRANCH STATEMENT

branch-statement → if-statementbranch-statement → guard-statementbranch-statement → switch-statement

GRAMMAR OF AN I F STATEMENT

if-statement → if condition-list code-block else-clause optelse-clause → else code-block | else if-statement

GRAMMAR OF AGUARD STATEMENT

guard-statement → guard condition-list else code-block

GRAMMAR OF A SW ITCH STATEMENT

switch-statement → switch expression { switch-cases opt }switch-cases → switch-case switch-cases optswitch-case → case-label statementsswitch-case → default-label statementsswitch-case → conditional-switch-casecase-label → attributes opt case case-item-list :case-item-list → pattern where-clause opt | pattern where-clause opt , case-item-listdefault-label → attributes opt default :where-clause → where where-expressionwhere-expression → expressionconditional-switch-case → switch-if-directive-clause switch-elseif-directive-clauses opt

switch-else-directive-clause opt endif-directiveswitch-if-directive-clause → if-directive compilation-condition switch-cases optswitch-elseif-directive-clauses → elseif-directive-clause switch-elseif-directive-clauses optswitch-elseif-directive-clause → elseif-directive compilation-condition switch-cases optswitch-else-directive-clause → else-directive switch-cases opt

GRAMMAR OF A LABELED STATEMENT

labeled-statement → statement-label loop-statementlabeled-statement → statement-label if-statementlabeled-statement → statement-label switch-statementlabeled-statement → statement-label do-statementstatement-label → label-name :label-name → identifier

GRAMMAR OF ACONTROLTRANSFER STATEMENT

control-transfer-statement → break-statementcontrol-transfer-statement → continue-statementcontrol-transfer-statement → fallthrough-statementcontrol-transfer-statement → return-statementcontrol-transfer-statement → throw-statement

GRAMMAR OF A BREAK STATEMENT

break-statement → break label-name opt

GRAMMAR OF ACONT INUE STATEMENT

continue-statement → continue label-name opt

GRAMMAR OF A FALLTHROUGH STATEMENT

fallthrough-statement → fallthrough

GRAMMAR OF ARETURN STATEMENT

return-statement → return expression opt

GRAMMAR OF ATHROWSTATEMENT

throw-statement → throw expression

GRAMMAR OF ADEFER STATEMENT

defer-statement → defer code-block

GRAMMAR OF ADO STATEMENT

do-statement → do code-block catch-clauses optcatch-clauses → catch-clause catch-clauses optcatch-clause → catch pattern opt where-clause opt code-block

GRAMMAR OF ACOMP I LER CONTROLSTATEMENT

compiler-control-statement → conditional-compilation-blockcompiler-control-statement → line-control-statementcompiler-control-statement → diagnostic-statement

GRAMMAR OF ACOND I T IONALCOMP I LAT ION BLOCK

conditional-compilation-block → if-directive-clause elseif-directive-clauses opt else-directive-clause opt endif-directive

if-directive-clause → if-directive compilation-condition statements optelseif-directive-clauses → elseif-directive-clause elseif-directive-clauses optelseif-directive-clause → elseif-directive compilation-condition statements optelse-directive-clause → else-directive statements optif-directive → #ifelseif-directive → #elseifelse-directive → #elseendif-directive → #endifcompilation-condition → platform-condition

compilation-condition → identifiercompilation-condition → boolean-literalcompilation-condition → ( compilation-condition )compilation-condition → ! compilation-conditioncompilation-condition → compilation-condition && compilation-conditioncompilation-condition → compilation-condition || compilation-conditionplatform-condition → os ( operating-system )platform-condition → arch ( architecture )platform-condition → swift ( >= swift-version ) | swift ( < swift-version )platform-condition → compiler ( >= swift-version ) | compiler ( < swift-version

)

platform-condition → canImport ( module-name )platform-condition → targetEnvironment ( environment )operating-system → macOS | iOS | watchOS | tvOSarchitecture → i386 | x86_64 | arm | arm64swift-version → decimal-digits swift-version-continuation optswift-version-continuation → . decimal-digits swift-version-continuation optmodule-name → identifierenvironment → simulator

GRAMMAR OF A L I NE CONTROLSTATEMENT

line-control-statement → #sourceLocation ( file: file-name , line: line-number )

line-control-statement → #sourceLocation ( )line-number → Adecimalintegergreaterthanzerofile-name → static-string-literal

GRAMMAR OF ACOMP I LE - T IME D I AGNOST IC STATEMENT

diagnostic-statement → #error ( diagnostic-message )diagnostic-statement → #warning ( diagnostic-message )diagnostic-message → static-string-literal

GRAMMAR OF AN AVA I LAB I L I TY COND I T ION

availability-condition → #available ( availability-arguments )availability-arguments → availability-argument | availability-argument , availability-

argumentsavailability-argument → platform-name platform-versionavailability-argument → *platform-name → iOS | iOSApplicationExtensionplatform-name → macOS | macOSApplicationExtensionplatform-name → watchOSplatform-name → tvOSplatform-version → decimal-digitsplatform-version → decimal-digits . decimal-digits

platform-version → decimal-digits . decimal-digits . decimal-digits

Declarations

GRAMMAR OF ADECLARAT ION

declaration → import-declarationdeclaration → constant-declarationdeclaration → variable-declarationdeclaration → typealias-declarationdeclaration → function-declarationdeclaration → enum-declarationdeclaration → struct-declarationdeclaration → class-declarationdeclaration → protocol-declarationdeclaration → initializer-declarationdeclaration → deinitializer-declarationdeclaration → extension-declarationdeclaration → subscript-declarationdeclaration → operator-declarationdeclaration → precedence-group-declarationdeclarations → declaration declarations opt

GRAMMAR OF ATOP - LEVELDECLARAT ION

top-level-declaration → statements opt

GRAMMAR OF ACODE BLOCK

code-block → { statements opt }

GRAMMAR OF AN IMPORT DECLARAT ION

import-declaration → attributes opt import import-kind opt import-pathimport-kind → typealias | struct | class | enum | protocol | let | var |

func

import-path → import-path-identifier | import-path-identifier . import-pathimport-path-identifier → identifier | operator

GRAMMAR OF ACONSTANT DECLARAT ION

constant-declaration → attributes opt declaration-modifiers opt let pattern-initializer-list

pattern-initializer-list → pattern-initializer | pattern-initializer , pattern-initializer-list

pattern-initializer → pattern initializer optinitializer → = expression

GRAMMAR OF A VAR IABLE DECLARAT ION

variable-declaration → variable-declaration-head pattern-initializer-listvariable-declaration → variable-declaration-head variable-name type-annotation code-

blockvariable-declaration → variable-declaration-head variable-name type-annotation getter-

setter-blockvariable-declaration → variable-declaration-head variable-name type-annotation getter-

setter-keyword-blockvariable-declaration → variable-declaration-head variable-name initializer willSet-didSet-

blockvariable-declaration → variable-declaration-head variable-name type-annotation

initializer opt willSet-didSet-blockvariable-declaration-head → attributes opt declaration-modifiers opt varvariable-name → identifiergetter-setter-block → code-blockgetter-setter-block → { getter-clause setter-clause opt }getter-setter-block → { setter-clause getter-clause }getter-clause → attributes opt mutation-modifier opt get code-blocksetter-clause → attributes opt mutation-modifier opt set setter-name opt code-blocksetter-name → ( identifier )getter-setter-keyword-block → { getter-keyword-clause setter-keyword-clause opt }getter-setter-keyword-block → { setter-keyword-clause getter-keyword-clause }getter-keyword-clause → attributes opt mutation-modifier opt getsetter-keyword-clause → attributes opt mutation-modifier opt setwillSet-didSet-block → { willSet-clause didSet-clause opt }willSet-didSet-block → { didSet-clause willSet-clause opt }willSet-clause → attributes opt willSet setter-name opt code-blockdidSet-clause → attributes opt didSet setter-name opt code-block

GRAMMAR OF ATYPE AL I AS DECLARAT ION

typealias-declaration → attributes opt access-level-modifier opt typealias typealias-name generic-parameter-clause opt typealias-assignment

typealias-name → identifiertypealias-assignment → = type

GRAMMAR OF A FUNCT ION DECLARAT ION

function-declaration → function-head function-name generic-parameter-clause optfunction-signature generic-where-clause opt function-body opt

function-head → attributes opt declaration-modifiers opt func

function-name → identifier | operatorfunction-signature → parameter-clause throwsopt function-result optfunction-signature → parameter-clause rethrows function-result optfunction-result → -> attributes opt typefunction-body → code-blockparameter-clause → ( ) | ( parameter-list )parameter-list → parameter | parameter , parameter-listparameter → external-parameter-name opt local-parameter-name type-annotation

default-argument-clause optparameter → external-parameter-name opt local-parameter-name type-annotationparameter → external-parameter-name opt local-parameter-name type-annotation ...external-parameter-name → identifierlocal-parameter-name → identifierdefault-argument-clause → = expression

GRAMMAR OF AN ENUMERAT ION DECLARAT ION

enum-declaration → attributes opt access-level-modifier opt union-style-enumenum-declaration → attributes opt access-level-modifier opt raw-value-style-enumunion-style-enum → indirectopt enum enum-name generic-parameter-clause opt

type-inheritance-clause opt generic-where-clause opt { union-style-enum-members

opt }union-style-enum-members → union-style-enum-member union-style-enum-members optunion-style-enum-member → declaration | union-style-enum-case-clause | compiler-

control-statementunion-style-enum-case-clause → attributes opt indirectopt case union-style-enum-

case-listunion-style-enum-case-list → union-style-enum-case | union-style-enum-case , union-

style-enum-case-listunion-style-enum-case → enum-case-name tuple-type optenum-name → identifierenum-case-name → identifierraw-value-style-enum → enum enum-name generic-parameter-clause opt type-

inheritance-clause generic-where-clause opt { raw-value-style-enum-members }raw-value-style-enum-members → raw-value-style-enum-member raw-value-style-enum-

members optraw-value-style-enum-member → declaration | raw-value-style-enum-case-clause |

compiler-control-statementraw-value-style-enum-case-clause → attributes opt case raw-value-style-enum-case-

listraw-value-style-enum-case-list → raw-value-style-enum-case | raw-value-style-enum-

case , raw-value-style-enum-case-listraw-value-style-enum-case → enum-case-name raw-value-assignment optraw-value-assignment → = raw-value-literalraw-value-literal → numeric-literal | static-string-literal | boolean-literal

GRAMMAR OF A STRUCTURE DECLARAT ION

struct-declaration → attributes opt access-level-modifier opt struct struct-namegeneric-parameter-clause opt type-inheritance-clause opt generic-where-clause optstruct-body

struct-name → identifierstruct-body → { struct-members opt }struct-members → struct-member struct-members optstruct-member → declaration | compiler-control-statement

GRAMMAR OF ACLASS DECLARAT ION

class-declaration → attributes opt access-level-modifier opt finalopt class class-name generic-parameter-clause opt type-inheritance-clause opt generic-where-clause

opt class-bodyclass-declaration → attributes opt final access-level-modifier opt class class-

name generic-parameter-clause opt type-inheritance-clause opt generic-where-clause

opt class-bodyclass-name → identifierclass-body → { class-members opt }class-members → class-member class-members optclass-member → declaration | compiler-control-statement

GRAMMAR OF A PROTOCOLDECLARAT ION

protocol-declaration → attributes opt access-level-modifier opt protocol protocol-name type-inheritance-clause opt generic-where-clause opt protocol-body

protocol-name → identifierprotocol-body → { protocol-members opt }protocol-members → protocol-member protocol-members optprotocol-member → protocol-member-declaration | compiler-control-statementprotocol-member-declaration → protocol-property-declarationprotocol-member-declaration → protocol-method-declarationprotocol-member-declaration → protocol-initializer-declarationprotocol-member-declaration → protocol-subscript-declarationprotocol-member-declaration → protocol-associated-type-declarationprotocol-member-declaration → typealias-declaration

GRAMMAR OF A PROTOCOLPROPERTY DECLARAT ION

protocol-property-declaration → variable-declaration-head variable-name type-annotationgetter-setter-keyword-block

GRAMMAR OF A PROTOCOLMETHOD DECLARAT ION

protocol-method-declaration → function-head function-name generic-parameter-clauseopt function-signature generic-where-clause opt

GRAMMAR OF A PROTOCOL I N I T I A L I ZER DECLARAT ION

protocol-initializer-declaration → initializer-head generic-parameter-clause optparameter-clause throwsopt generic-where-clause opt

protocol-initializer-declaration → initializer-head generic-parameter-clause optparameter-clause rethrows generic-where-clause opt

GRAMMAR OF A PROTOCOLSUBSCR IPT DECLARAT ION

protocol-subscript-declaration → subscript-head subscript-result generic-where-clauseopt getter-setter-keyword-block

GRAMMAR OF A PROTOCOLASSOC IATED TYPE DECLARAT ION

protocol-associated-type-declaration → attributes opt access-level-modifier optassociatedtype typealias-name type-inheritance-clause opt typealias-assignment

opt generic-where-clause opt

GRAMMAR OF AN I N I T I A L I ZER DECLARAT ION

initializer-declaration → initializer-head generic-parameter-clause opt parameter-clausethrowsopt generic-where-clause opt initializer-body

initializer-declaration → initializer-head generic-parameter-clause opt parameter-clauserethrows generic-where-clause opt initializer-body

initializer-head → attributes opt declaration-modifiers opt initinitializer-head → attributes opt declaration-modifiers opt init ?initializer-head → attributes opt declaration-modifiers opt init !initializer-body → code-block

GRAMMAR OF ADE IN I T I A L I ZER DECLARAT ION

deinitializer-declaration → attributes opt deinit code-block

GRAMMAR OF AN EXTENS ION DECLARAT ION

extension-declaration → attributes opt access-level-modifier opt extension type-identifier type-inheritance-clause opt generic-where-clause opt extension-body

extension-body → { extension-members opt }extension-members → extension-member extension-members optextension-member → declaration | compiler-control-statement

GRAMMAR OF A SUBSCR IPT DECLARAT ION

subscript-declaration → subscript-head subscript-result generic-where-clause opt code-block

subscript-declaration → subscript-head subscript-result generic-where-clause opt getter-setter-block

subscript-declaration → subscript-head subscript-result generic-where-clause opt getter-setter-keyword-block

subscript-head → attributes opt declaration-modifiers opt subscript generic-parameter-clause opt parameter-clause

subscript-result → -> attributes opt type

GRAMMAR OF AN OPERATOR DECLARAT ION

operator-declaration → prefix-operator-declaration | postfix-operator-declaration | infix-operator-declaration

prefix-operator-declaration → prefix operator operatorpostfix-operator-declaration → postfix operator operatorinfix-operator-declaration → infix operator operator infix-operator-group optinfix-operator-group → : precedence-group-name

GRAMMAR OF A PRECEDENCE GROUP DECLARAT ION

precedence-group-declaration → precedencegroup precedence-group-name {precedence-group-attributes opt }

precedence-group-attributes → precedence-group-attribute precedence-group-attributesopt

precedence-group-attribute → precedence-group-relationprecedence-group-attribute → precedence-group-assignmentprecedence-group-attribute → precedence-group-associativityprecedence-group-relation → higherThan : precedence-group-namesprecedence-group-relation → lowerThan : precedence-group-namesprecedence-group-assignment → assignment : boolean-literalprecedence-group-associativity → associativity : leftprecedence-group-associativity → associativity : rightprecedence-group-associativity → associativity : noneprecedence-group-names → precedence-group-name | precedence-group-name ,

precedence-group-namesprecedence-group-name → identifier

GRAMMAR OF ADECLARAT ION MOD IF I ER

declaration-modifier → class | convenience | dynamic | final | infix | lazy |optional | override | postfix | prefix | required | static | unowned |unowned ( safe ) | unowned ( unsafe ) | weak

declaration-modifier → access-level-modifierdeclaration-modifier → mutation-modifier

declaration-modifiers → declaration-modifier declaration-modifiers optaccess-level-modifier → private | private ( set )access-level-modifier → fileprivate | fileprivate ( set )access-level-modifier → internal | internal ( set )access-level-modifier → public | public ( set )access-level-modifier → open | open ( set )mutation-modifier → mutating | nonmutating

Attributes

GRAMMAR OF AN ATTR IBUTE

attribute → @ attribute-name attribute-argument-clause optattribute-name → identifierattribute-argument-clause → ( balanced-tokens opt )attributes → attribute attributes optbalanced-tokens → balanced-token balanced-tokens optbalanced-token → ( balanced-tokens opt )balanced-token → [ balanced-tokens opt ]balanced-token → { balanced-tokens opt }balanced-token → Anyidentifier,keyword,literal,oroperatorbalanced-token → Anypunctuationexcept ( , ) , [ , ] , { ,or }

Patterns

GRAMMAR OF A PATTERN

pattern → wildcard-pattern type-annotation optpattern → identifier-pattern type-annotation optpattern → value-binding-patternpattern → tuple-pattern type-annotation optpattern → enum-case-patternpattern → optional-patternpattern → type-casting-patternpattern → expression-pattern

GRAMMAR OF AW I LDCARD PATTERN

wildcard-pattern → _

GRAMMAR OF AN I DENT I F I ER PATTERN

identifier-pattern → identifier

GRAMMAR OF A VALUE -B IND ING PATTERN

value-binding-pattern → var pattern | let pattern

GRAMMAR OF ATUPLE PATTERN

tuple-pattern → ( tuple-pattern-element-list opt )tuple-pattern-element-list → tuple-pattern-element | tuple-pattern-element , tuple-

pattern-element-listtuple-pattern-element → pattern | identifier : pattern

GRAMMAR OF AN ENUMERAT ION CASE PATTERN

enum-case-pattern → type-identifier opt . enum-case-name tuple-pattern opt

GRAMMAR OF AN OPT IONALPATTERN

optional-pattern → identifier-pattern ?

GRAMMAR OF ATYPE CAST ING PATTERN

type-casting-pattern → is-pattern | as-patternis-pattern → is typeas-pattern → pattern as type

GRAMMAR OF AN EXPRESS ION PATTERN

expression-pattern → expression

GenericParametersandArguments

GRAMMAR OF AGENER IC PARAMETER CLAUSE

generic-parameter-clause → < generic-parameter-list >generic-parameter-list → generic-parameter | generic-parameter , generic-parameter-

listgeneric-parameter → type-namegeneric-parameter → type-name : type-identifiergeneric-parameter → type-name : protocol-composition-type

generic-where-clause → where requirement-listrequirement-list → requirement | requirement , requirement-listrequirement → conformance-requirement | same-type-requirementconformance-requirement → type-identifier : type-identifierconformance-requirement → type-identifier : protocol-composition-typesame-type-requirement → type-identifier == type

GRAMMAR OF AGENER IC ARGUMENT CLAUSE

generic-argument-clause → < generic-argument-list >generic-argument-list → generic-argument | generic-argument , generic-argument-listgeneric-argument → type

RevisionHistory

DocumentRevisionHistory

2019-09-10

UpdatedforSwift5.1.

Addedinformationaboutfunctionsthatspecifyaprotocolthattheirreturnvalueconformsto,insteadofprovidingaspecificnamedreturntype,totheOpaqueTypeschapter.

AddedinformationaboutpropertywrapperstothePropertyWrapperssection.

Addedinformationenumerationsandstructuresthatarefrozenforlibraryevolutiontothefrozensection.

AddedtheFunctionsWithanImplicitReturnandShorthandGetterDeclarationsectionswithinformationaboutfunctionsthatomitreturn.

AddedinformationaboutusingsubscriptsontypestotheTypeSubscriptssection.

UpdatedtheEnumerationCasePatternsection,nowthatanenumerationcasepatterncanmatchanoptionalvalue.

UpdatedtheMemberwiseInitializersforStructureTypessection,nowthatmemberwiseinitializerssupportomittingparametersforpropertiesthathaveadefaultvalue.

AddedinformationaboutdynamicmembersthatarelookedupbykeypathatruntimetothedynamicMemberLookupsection.

AddedmacCatalysttothelistoftargetenvironmentsinConditionalCompilationBlock.

UpdatedtheSelfTypesection,nowthatSelfcanbeusedtorefertothetypeintroducedbythecurrentclass,structure,orenumerationdeclaration.

2019-03-25

UpdatedforSwift5.0.

AddedtheExtendedStringDelimiterssectionandupdatedtheStringLiteralssectionwithinformationaboutextendedstringdelimiters.

AddedthedynamicCallablesectionwithinformationaboutdynamicallycallinginstancesasfunctionsusingthedynamicCallableattribute.

AddedtheunknownandSwitchingOverFutureEnumerationCasessectionswithinformationabouthandlingfutureenumerationcasesinswitchstatementsusingtheunknownswitchcaseattribute.

Addedinformationabouttheidentitykeypath(\.self)totheKey-PathExpressionsection.

Addedinformationaboutusingthelessthan(<)operatorinplatformconditionstotheConditionalCompilationBlocksection.

2018-09-17

UpdatedforSwift4.2.

Addedinformationaboutaccessingallofanenumeration’scasestotheIteratingoverEnumerationCasessection.

Addedinformationabout#errorand#warningtotheCompile-TimeDiagnosticStatementsection.

AddedinformationaboutinliningtotheDeclarationAttributessectionundertheinlinableandusableFromInlineattributes.

AddedinformationaboutmembersthatarelookedupbynameatruntimetotheDeclarationAttributessectionunderthedynamicMemberLookupattribute.

Addedinformationabouttherequires_stored_property_initsandwarn_unqualified_accessattributestotheDeclarationAttributessection.

AddedinformationabouthowtoconditionallycompilecodedependingontheSwiftcompilerversionbeingusedtotheConditionalCompilationBlocksection.

Addedinformationabout#dsohandletotheLiteralExpressionsection.

2018-03-29

UpdatedforSwift4.1.

AddedinformationaboutsynthesizedimplementationsofequivalenceoperatorstotheEquivalenceOperatorssection.

AddedinformationaboutconditionalprotocolconformancetotheExtensionDeclarationsectionoftheDeclarationschapter,andtotheConditionallyConformingtoaProtocolsectionoftheProtocolschapter.

AddedinformationaboutrecursiveprotocolconstraintstotheUsingaProtocolinItsAssociatedType’sConstraintssection.

AddedinformationaboutthecanImport()andtargetEnvironment()platformconditionstoConditionalCompilationBlock.

2017-12-04

UpdatedforSwift4.0.3.

UpdatedtheKey-PathExpressionsection,nowthatkeypathssupportsubscriptcomponents.

2017-09-19

UpdatedforSwift4.0.

AddedinformationaboutexclusiveaccesstomemorytotheMemorySafetychapter.

AddedtheAssociatedTypeswithaGenericWhereClausesection,nowthatyoucanusegenericwhereclausestoconstrainassociatedtypes.

AddedinformationaboutmultilinestringliteralstotheStringLiteralssectionoftheStringsandCharacterschapter,andtotheStringLiteralssectionoftheLexicalStructurechapter.

UpdatedthediscussionoftheobjcattributeinDeclarationAttributes,nowthatthisattributeisinferredinfewerplaces.

AddedtheGenericSubscriptssection,nowthatsubscriptscanbegeneric.

UpdatedthediscussionintheProtocolCompositionsectionoftheProtocolschapter,andintheProtocolCompositionTypesectionoftheTypeschapter,nowthatprotocolcompositiontypescancontainasuperclassrequirement.

UpdatedthediscussionofprotocolextensionsinExtensionDeclarationnowthatfinalisn’tallowedinthem.

AddedinformationaboutpreconditionsandfatalerrorstotheAssertionsandPreconditionssection.

2017-03-27

UpdatedforSwift3.1.

AddedtheExtensionswithaGenericWhereClausesectionwithinformationaboutextensionsthatincluderequirements.

AddedexamplesofiteratingoverarangetotheFor-InLoopssection.

AddedanexampleoffailablenumericconversionstotheFailableInitializerssection.

AddedinformationtotheDeclarationAttributessectionaboutusingtheavailableattributewithaSwiftlanguageversion.

UpdatedthediscussionintheFunctionTypesectiontonotethatargumentlabelsarenotallowedwhenwritingafunctiontype.

UpdatedthediscussionofSwiftlanguageversionnumbersintheConditionalCompilationBlocksection,nowthatanoptionalpatchnumber

isallowed.

UpdatedthediscussionintheFunctionTypesection,nowthatSwiftdistinguishesbetweenfunctionsthattakemultipleparametersandfunctionsthattakeasingleparameterofatupletype.

RemovedtheDynamicTypeExpressionsectionfromtheExpressionschapter,nowthattype(of:)isaSwiftstandardlibraryfunction.

2016-10-27

UpdatedforSwift3.0.1.

UpdatedthediscussionofweakandunownedreferencesintheAutomaticReferenceCountingchapter.

Addedinformationabouttheunowned,unowned(safe),andunowned(unsafe)declarationmodifiersintheDeclarationModifierssection.

AddedanotetotheTypeCastingforAnyandAnyObjectsectionaboutusinganoptionalvaluewhenavalueoftypeAnyisexpected.

UpdatedtheExpressionschaptertoseparatethediscussionofparenthesizedexpressionsandtupleexpressions.

2016-09-13

UpdatedforSwift3.0.

UpdatedthediscussionoffunctionsintheFunctionschapterandtheFunctionDeclarationsectiontonotethatallparametersgetanargumentlabelbydefault.

UpdatedthediscussionofoperatorsintheAdvancedOperatorschapter,nowthatyouimplementthemastypemethodsinsteadofasglobalfunctions.

Addedinformationabouttheopenandfileprivateaccess-levelmodifierstotheAccessControlchapter.

UpdatedthediscussionofinoutintheFunctionDeclarationsectiontonotethatitappearsinfrontofaparameter’stypeinsteadofinfrontofaparameter’sname.

Updatedthediscussionofthe@noescapeand@autoclosureattributesintheEscapingClosuresandAutoclosuressectionsandtheAttributeschapternowthattheyaretypeattributes,ratherthandeclarationattributes.

AddedinformationaboutoperatorprecedencegroupstothePrecedenceforCustomInfixOperatorssectionoftheAdvancedOperatorschapter,andtothePrecedenceGroupDeclarationsectionoftheDeclarationschapter.

UpdateddiscussionthroughouttousemacOSinsteadofOSX,ErrorinsteadofErrorProtocol,andprotocolnamessuchasExpressibleByStringLiteralinsteadofStringLiteralConvertible.

UpdatedthediscussionintheGenericWhereClausessectionoftheGenericschapterandintheGenericParametersandArgumentschapter,nowthatgenericwhereclausesarewrittenattheendofadeclaration.

UpdatedthediscussionintheEscapingClosuressection,nowthatclosuresarenonescapingbydefault.

UpdatedthediscussionintheOptionalBindingsectionoftheTheBasicschapterandtheWhileStatementsectionoftheStatementschapter,nowthatif,while,andguardstatementsuseacomma-separatedlistofconditionswithoutwhereclauses.

AddedinformationaboutswitchcasesthathavemultiplepatternstotheSwitchsectionoftheControlFlowchapterandtheSwitchStatementsectionoftheStatementschapter.

UpdatedthediscussionoffunctiontypesintheFunctionTypesectionnowthatfunctionargumentlabelsarenolongerpartofafunction’stype.

UpdatedthediscussionofprotocolcompositiontypesintheProtocolCompositionsectionoftheProtocolschapterandintheProtocolCompositionTypesectionoftheTypeschaptertousethenewProtocol1&Protocol2syntax.

UpdatedthediscussionintheDynamicTypeExpressionsectiontousethenewtype(of:)syntaxfordynamictypeexpressions.

Updatedthediscussionoflinecontrolstatementstousethe#sourceLocation(file:line:)syntaxintheLineControlStatementsection.

UpdatedthediscussioninFunctionsthatNeverReturntousethenewNevertype.

AddedinformationaboutplaygroundliteralstotheLiteralExpressionsection.

UpdatedthediscussionintheIn-OutParameterssectiontonotethatonlynonescapingclosurescancapturein-outparameters.

UpdatedthediscussionaboutdefaultparametersintheDefaultParameterValuessection,nowthattheycan’tbereorderedinfunctioncalls.

UpdatedattributeargumentstouseacolonintheAttributeschapter.

AddedinformationaboutthrowinganerrorinsidethecatchblockofarethrowingfunctiontotheRethrowingFunctionsandMethodssection.

AddedinformationaboutaccessingtheselectorofanObjective-Cproperty’sgetterorsettertotheSelectorExpressionsection.

AddedinformationtotheTypeAliasDeclarationsectionaboutgenerictypealiasesandusingtypealiasesinsideofprotocols.

UpdatedthediscussionoffunctiontypesintheFunctionTypesectiontonotethatparenthesesaroundtheparametertypesarerequired.

UpdatedtheAttributeschaptertonotethatthe@IBAction,@IBOutlet,and@NSManagedattributesimplythe@objcattribute.

Addedinformationaboutthe@GKInspectableattributetotheDeclarationAttributessection.

UpdatedthediscussionofoptionalprotocolrequirementsintheOptionalProtocolRequirementssectiontoclarifythattheyareusedonlyincodethatinteroperateswithObjective-C.

RemovedthediscussionofexplicitlyusingletinfunctionparametersfromtheFunctionDeclarationsection.

RemovedthediscussionoftheBooleanprotocolfromtheStatementschapter,nowthattheprotocolhasbeenremovedfromtheSwiftstandardlibrary.

Correctedthediscussionofthe@NSApplicationMainattributeintheDeclarationAttributessection.

2016-03-21

UpdatedforSwift2.2.

AddedinformationabouthowtoconditionallycompilecodedependingontheversionofSwiftbeingusedtotheConditionalCompilationBlocksection.

AddedinformationabouthowtodistinguishbetweenmethodsorinitializerswhosenamesdifferonlybythenamesoftheirargumentstotheExplicitMemberExpressionsection.

Addedinformationaboutthe#selectorsyntaxforObjective-CselectorstotheSelectorExpressionsection.

UpdatedthediscussionofassociatedtypestousetheassociatedtypekeywordintheAssociatedTypesandProtocolAssociatedTypeDeclarationsections.

UpdatedinformationaboutinitializersthatreturnnilbeforetheinstanceisfullyinitializedintheFailableInitializerssection.

AddedinformationaboutcomparingtuplestotheComparisonOperatorssection.

AddedinformationaboutusingkeywordsasexternalparameternamestotheKeywordsandPunctuationsection.

Updatedthediscussionofthe@objcattributeintheDeclarationAttributessectiontonotethatenumerationsandenumerationcasescanusethisattribute.

UpdatedtheOperatorssectionwithdiscussionofcustomoperatorsthatcontainadot.

AddedanotetotheRethrowingFunctionsandMethodssectionthatrethrowingfunctionscan’tdirectlythrowerrors.

AddedanotetothePropertyObserverssectionaboutpropertyobserversbeingcalledwhenyoupassapropertyasanin-outparameter.

AddedasectionabouterrorhandlingtotheASwiftTourchapter.

UpdatedfiguresintheWeakReferencessectiontoshowthedeallocationprocessmoreclearly.

RemoveddiscussionofC-styleforloops,the++prefixandpostfixoperators,andthe--prefixandpostfixoperators.

Removeddiscussionofvariablefunctionargumentsandthespecialsyntaxforcurriedfunctions.

2015-10-20

UpdatedforSwift2.1.

UpdatedtheStringInterpolationandStringLiteralssectionsnowthatstringinterpolationscancontainstringliterals.

AddedtheEscapingClosuressectionwithinformationaboutthe@noescapeattribute.

UpdatedtheDeclarationAttributesandConditionalCompilationBlocksectionswithinformationabouttvOS.

Addedinformationaboutthebehaviorofin-outparameterstotheIn-OutParameterssection.

AddedinformationtotheCaptureListssectionabouthowvaluesspecifiedinclosurecapturelistsarecaptured.

UpdatedtheAccessingPropertiesThroughOptionalChainingsectiontoclarifyhowassignmentthroughoptionalchainingbehaves.

ImprovedthediscussionofautoclosuresintheAutoclosuressection.

Addedanexamplethatusesthe??operatortotheASwiftTourchapter.

2015-09-16

UpdatedforSwift2.0.

AddedinformationabouterrorhandlingtotheErrorHandlingchapter,theDoStatementsection,theThrowStatementsection,theDeferStatementsection,andtheTryOperatorsection.

UpdatedtheRepresentingandThrowingErrorssection,nowthatalltypescanconformtotheErrorTypeprotocol.

Addedinformationaboutthenewtry?keywordtotheConvertingErrorstoOptionalValuessection.

AddedinformationaboutrecursiveenumerationstotheRecursiveEnumerationssectionoftheEnumerationschapterandtheEnumerationswithCasesofAnyTypesectionoftheDeclarationschapter.

AddedinformationaboutAPIavailabilitycheckingtotheCheckingAPIAvailabilitysectionoftheControlFlowchapterandtheAvailabilityConditionsectionoftheStatementschapter.

AddedinformationaboutthenewguardstatementtotheEarlyExitsectionoftheControlFlowchapterandtheGuardStatementsectionoftheStatementschapter.

AddedinformationaboutprotocolextensionstotheProtocolExtensionssectionoftheProtocolschapter.

AddedinformationaboutaccesscontrolforunittestingtotheAccessLevelsforUnitTestTargetssectionoftheAccessControlchapter.

AddedinformationaboutthenewoptionalpatterntotheOptionalPatternsectionofthePatternschapter.

UpdatedtheRepeat-Whilesectionwithinformationabouttherepeat-whileloop.

UpdatedtheStringsandCharacterschapter,nowthatStringnolongerconformstotheCollectionTypeprotocolfromtheSwiftstandardlibrary.

AddedinformationaboutthenewSwiftstandardlibraryprint(_:separator:terminator)functiontothePrintingConstantsandVariablessection.

AddedinformationaboutthebehaviorofenumerationcaseswithStringrawvaluestotheImplicitlyAssignedRawValuessectionoftheEnumerationschapterandtheEnumerationswithCasesofaRaw-ValueTypesectionoftheDeclarationschapter.

Addedinformationaboutthe@autoclosureattribute—includingits@autoclosure(escaping)form—totheAutoclosuressection.

UpdatedtheDeclarationAttributessectionwithinformationaboutthe@availableand@warn_unused_resultattributes.

UpdatedtheTypeAttributessectionwithinformationaboutthe@conventionattribute.

AddedanexampleofusingmultipleoptionalbindingswithawhereclausetotheOptionalBindingsection.

AddedinformationtotheStringLiteralssectionabouthowconcatenatingstringliteralsusingthe+operatorhappensatcompiletime.

AddedinformationtotheMetatypeTypesectionaboutcomparingmetatypevaluesandusingthemtoconstructinstanceswithinitializerexpressions.

AddedanotetotheDebuggingwithAssertionssectionaboutwhenuser-definedassertionsaredisabled.

Updatedthediscussionofthe@NSManagedattributeintheDeclarationAttributessection,nowthattheattributecanbeappliedtocertaininstancemethods.

UpdatedtheVariadicParameterssection,nowthatvariadicparameterscanbedeclaredinanypositioninafunction’sparameterlist.

AddedinformationtotheOverridingaFailableInitializersectionabouthowanonfailableinitializercandelegateuptoafailableinitializerbyforce-unwrappingtheresultofthesuperclass’sinitializer.

AddedinformationaboutusingenumerationcasesasfunctionstotheEnumerationswithCasesofAnyTypesection.

AddedinformationaboutexplicitlyreferencinganinitializertotheInitializerExpressionsection.

AddedinformationaboutbuildconfigurationandlinecontrolstatementstotheCompilerControlStatementssection.

AddedanotetotheMetatypeTypesectionaboutconstructingclassinstancesfrommetatypevalues.

AddedanotetotheWeakReferencessectionaboutweakreferencesbeingunsuitableforcaching.

UpdatedanoteintheTypePropertiessectiontomentionthatstoredtypepropertiesarelazilyinitialized.

UpdatedtheCapturingValuessectiontoclarifyhowvariablesandconstantsarecapturedinclosures.

UpdatedtheDeclarationAttributessectiontodescribewhenyoucanapply

the@objcattributetoclasses.

AddedanotetotheHandlingErrorssectionabouttheperformanceofexecutingathrowstatement.AddedsimilarinformationaboutthedostatementintheDoStatementsection.

UpdatedtheTypePropertiessectionwithinformationaboutstoredandcomputedtypepropertiesforclasses,structures,andenumerations.

UpdatedtheBreakStatementsectionwithinformationaboutlabeledbreakstatements.

UpdatedanoteinthePropertyObserverssectiontoclarifythebehaviorofwillSetanddidSetobservers.

AddedanotetotheAccessLevelssectionwithinformationaboutthescopeofprivateaccess.

AddedanotetotheWeakReferencessectionaboutthedifferencesinweakreferencesbetweengarbagecollectedsystemsandARC.

UpdatedtheSpecialCharactersinStringLiteralssectionwithamoreprecisedefinitionofUnicodescalars.

2015-04-08

UpdatedforSwift1.2.

SwiftnowhasanativeSetcollectiontype.Formoreinformation,seeSets.

@autoclosureisnowanattributeoftheparameterdeclaration,notitstype.Thereisalsoanew@noescapeparameterdeclarationattribute.Formoreinformation,seeDeclarationAttributes.

Typemethodsandpropertiesnowusethestatickeywordasadeclarationmodifier.FormoreinformationseeTypeVariableProperties.

Swiftnowincludestheas?andas!failabledowncastoperators.Formoreinformation,seeCheckingforProtocolConformance.

AddedanewguidesectionaboutStringIndices.

Removedtheoverflowdivision(&/)andoverflowremainder(&%)operatorsfromOverflowOperators.

Updatedtherulesforconstantandconstantpropertydeclarationandinitialization.Formoreinformation,seeConstantDeclaration.

UpdatedthedefinitionofUnicodescalarsinstringliterals.SeeSpecialCharactersinStringLiterals.

UpdatedRangeOperatorstonotethatahalf-openrangewiththesamestartandendindexwillbeempty.

UpdatedClosuresAreReferenceTypestoclarifythecapturingrulesforvariables.

UpdatedValueOverflowtoclarifytheoverflowbehaviorofsignedandunsignedintegers

UpdatedProtocolDeclarationtoclarifyprotocoldeclarationscopeandmembers.

UpdatedDefiningaCaptureListtoclarifythesyntaxforweakandunownedreferencesinclosurecapturelists.

UpdatedOperatorstoexplicitlymentionexamplesofsupportedcharactersforcustomoperators,suchasthoseintheMathematicalOperators,MiscellaneousSymbols,andDingbatsUnicodeblocks.

Constantscannowbedeclaredwithoutbeinginitializedinlocalfunctionscope.Theymusthaveasetvaluebeforefirstuse.Formoreinformation,seeConstantDeclaration.

Inaninitializer,constantpropertiescannowonlyassignavalueonce.Formoreinformation,seeAssigningConstantPropertiesDuringInitialization.

Multipleoptionalbindingscannowappearinasingleifstatementasacomma-separatedlistofassignmentexpressions.Formoreinformation,see

OptionalBinding.

AnOptional-ChainingExpressionmustappearwithinapostfixexpression.

Protocolcastsarenolongerlimitedto@objcprotocols.

Typecaststhatcanfailatruntimenowusetheas?oras!operator,andtypecaststhatareguaranteednottofailusetheasoperator.Formoreinformation,seeType-CastingOperators.

2014-10-16

UpdatedforSwift1.1.

AddedafullguidetoFailableInitializers.

AddedadescriptionofFailableInitializerRequirementsforprotocols.

ConstantsandvariablesoftypeAnycannowcontainfunctioninstances.UpdatedtheexampleinTypeCastingforAnyandAnyObjecttoshowhowtocheckforandcasttoafunctiontypewithinaswitchstatement.

EnumerationswithrawvaluesnowhavearawValuepropertyratherthanatoRaw()methodandafailableinitializerwitharawValueparameterratherthanafromRaw()method.Formoreinformation,seeRawValuesandEnumerationswithCasesofaRaw-ValueType.

AddedanewreferencesectionaboutFailableInitializers,whichcantriggerinitializationfailure.

Customoperatorscannowcontainthe?character.UpdatedtheOperatorsreferencetodescribetherevisedrules.RemovedaduplicatedescriptionofthevalidsetofoperatorcharactersfromCustomOperators.

2014-08-18

NewdocumentthatdescribesSwift1.0,Apple’snewprogramminglanguageforbuildingiOSandOSXapps.

AddedanewsectionaboutInitializerRequirementsinprotocols.

AddedanewsectionaboutClass-OnlyProtocols.

AssertionsandPreconditionscannowusestringinterpolation.Removedanotetothecontrary.

UpdatedtheConcatenatingStringsandCharacterssectiontoreflectthefactthatStringandCharactervaluescannolongerbecombinedwiththeadditionoperator(+)oradditionassignmentoperator(+=).TheseoperatorsarenowusedonlywithStringvalues.UsetheStringtype’sappend(_:)methodtoappendasingleCharactervalueontotheendofastring.

AddedinformationabouttheavailabilityattributetotheDeclarationAttributessection.

Optionalsnolongerimplicitlyevaluatetotruewhentheyhaveavalueandfalsewhentheydonot,toavoidconfusionwhenworkingwithoptionalBoolvalues.Instead,makeanexplicitcheckagainstnilwiththe==or!=operatorstofindoutifanoptionalcontainsavalue.

SwiftnowhasaNil-CoalescingOperator(a??b),whichunwrapsanoptional’svalueifitexists,orreturnsadefaultvalueiftheoptionalisnil.

UpdatedandexpandedtheComparingStringssectiontoreflectanddemonstratethatstringandcharactercomparisonandprefix/suffixcomparisonarenowbasedonUnicodecanonicalequivalenceofextendedgraphemeclusters.

Youcannowtrytosetaproperty’svalue,assigntoasubscript,orcallamutatingmethodoroperatorthroughOptionalChaining.TheinformationaboutAccessingPropertiesThroughOptionalChaininghasbeenupdatedaccordingly,andtheexamplesofcheckingformethodcallsuccessinCallingMethodsThroughOptionalChaininghavebeenexpandedtoshowhowtocheckforpropertysettingsuccess.

AddedanewsectionaboutAccessingSubscriptsofOptionalTypethroughoptionalchaining.

UpdatedtheAccessingandModifyinganArraysectiontonotethatyoucannolongerappendasingleitemtoanarraywiththe+=operator.Instead,usetheappend(_:)method,orappendasingle-itemarraywiththe+=operator.

AddedanotethatthestartvalueafortheRangeOperatorsa...banda..<bmustnotbegreaterthantheendvalueb.

RewrotetheInheritancechaptertoremoveitsintroductorycoverageofinitializeroverrides.Thischapternowfocusesmoreontheadditionofnewfunctionalityinasubclass,andthemodificationofexistingfunctionalitywithoverrides.Thechapter’sexampleofOverridingPropertyGettersandSettershasbeenrewrittentoshowhowtooverrideadescriptionproperty.(Theexamplesofmodifyinganinheritedproperty’sdefaultvalueinasubclassinitializerhavebeenmovedtotheInitializationchapter.)

UpdatedtheInitializerInheritanceandOverridingsectiontonotethatoverridesofadesignatedinitializermustnowbemarkedwiththeoverridemodifier.

UpdatedtheRequiredInitializerssectiontonotethattherequiredmodifierisnowwrittenbeforeeverysubclassimplementationofarequiredinitializer,andthattherequirementsforrequiredinitializerscannowbesatisfiedbyautomaticallyinheritedinitializers.

InfixOperatorMethodsnolongerrequirethe@infixattribute.

The@prefixand@postfixattributesforPrefixandPostfixOperatorshavebeenreplacedbyprefixandpostfixdeclarationmodifiers.

AddedanoteabouttheorderinwhichPrefixandPostfixOperatorsareappliedwhenbothaprefixandapostfixoperatorareappliedtothesameoperand.

OperatorfunctionsforCompoundAssignmentOperatorsnolongerusethe@assignmentattributewhendefiningthefunction.

TheorderinwhichmodifiersarespecifiedwhendefiningCustomOperatorshaschanged.Younowwriteprefixoperatorratherthanoperatorprefix,forexample.

AddedinformationaboutthedynamicdeclarationmodifierinDeclarationModifiers.

AddedinformationabouthowtypeinferenceworkswithLiterals.

Addedmoreinformationaboutcurriedfunctions.

AddedanewchapteraboutAccessControl.

UpdatedtheStringsandCharacterschaptertoreflectthefactthatSwift’sCharactertypenowrepresentsasingleUnicodeextendedgraphemecluster.IncludesanewsectiononExtendedGraphemeClustersandmoreinformationaboutUnicodeScalarValuesandComparingStrings.

UpdatedtheStringLiteralssectiontonotethatUnicodescalarsinsidestringliteralsarenowwrittenas\u{n},wherenisahexadecimalnumberbetween0and10FFFF,therangeofUnicode’scodespace.

TheNSStringlengthpropertyisnowmappedontoSwift’snativeStringtypeasutf16Count,notutf16count.

Swift’snativeStringtypenolongerhasanuppercaseStringorlowercaseStringproperty.ThecorrespondingsectioninStringsandCharactershasbeenremoved,andvariouscodeexampleshavebeenupdated.

AddedanewsectionaboutInitializerParametersWithoutArgumentLabels.

AddedanewsectionaboutRequiredInitializers.

AddedanewsectionaboutOptionalTupleReturnTypes.

UpdatedtheTypeAnnotationssectiontonotethatmultiplerelatedvariablescanbedefinedonasinglelinewithonetypeannotation.

The@optional,@lazy,@final,and@requiredattributesarenowtheoptional,lazy,final,andrequiredDeclarationModifiers.

Updatedtheentirebooktoreferto..<astheHalf-OpenRangeOperator(ratherthanthe“half-closedrangeoperator”).

UpdatedtheAccessingandModifyingaDictionarysectiontonotethatDictionarynowhasaBooleanisEmptyproperty.

ClarifiedthefulllistofcharactersthatcanbeusedwhendefiningCustomOperators.

nilandtheBooleanstrueandfalsearenowLiterals.

Swift’sArraytypenowhasfullvaluesemantics.UpdatedtheinformationaboutMutabilityofCollectionsandArraystoreflectthenewapproach.Alsoclarifiedtheassignmentandcopybehaviorforstringsarraysanddictionaries.

ArrayTypeShorthandSyntaxisnowwrittenas[SomeType]ratherthanSomeType[].

AddedanewsectionaboutDictionaryTypeShorthandSyntax,whichiswrittenas[KeyType:ValueType].

AddedanewsectionaboutHashValuesforSetTypes.

ExamplesofClosureExpressionsnowusetheglobalsorted(_:_:)functionratherthantheglobalsort(_:_:)function,toreflectthenewarrayvaluesemantics.

UpdatedtheinformationaboutMemberwiseInitializersforStructureTypestoclarifythatthememberwisestructureinitializerismadeavailableevenifastructure’sstoredpropertiesdonothavedefaultvalues.

Updatedto..<ratherthan..fortheHalf-OpenRangeOperator.

AddedanexampleofExtendingaGenericType.

CopyrightandNotices

AppleInc.Copyright©2019AppleInc.

ThisdocumentismadeavailableunderaCreativeCommonsAttribution4.0International(CCBY4.0)License:https://creativecommons.org/licenses/by/4.0/

Nolicenses,expressorimplied,aregrantedwithrespecttoanyofthetechnologydescribedinthisdocument.Appleretainsallintellectualpropertyrightsassociatedwiththetechnologydescribedinthisdocument.

AppleInc.OneAppleParkWayCupertino,CA95014408-996-1010

Apple,theApplelogo,Bonjour,Cocoa,Logic,Mac,Numbers,Objective-C,OSX,Retina,Sand,Shake,andXcodearetrademarksofAppleInc.,registeredintheU.S.andothercountries.

SwiftandtvOSaretrademarksofAppleInc.

IOSisatrademarkorregisteredtrademarkofCiscointheU.S.andothercountriesandisusedunderlicense.

TimesisaregisteredtrademarkofHeidelbergerDruckmaschinenAG,availablefromLinotypeLibraryGmbH.