Welcome to Swift - Marek Piaseckimarek.piasecki.staff.iiar.pwr.wroc.pl/dydaktyka/pam/iOS... ·...
Transcript of Welcome to Swift - Marek Piaseckimarek.piasecki.staff.iiar.pwr.wroc.pl/dydaktyka/pam/iOS... ·...
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
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.
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.
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:
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.
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)
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"
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=[
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{
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.
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,
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)
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.
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 }
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
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()
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 }
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 }
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
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@[email protected]@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(©OfStepSize)
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.