varemployee=["name":"Taylor","job":"Singer"]// overwrite the valueemployee["name"]="Ed"// add a new dictionary itememployee["surname"]="Sherrin"print(employee)//["job": "Singer", "name": "Ed", "surname": "Sherrin"]
enumWeekday{casemonday,tuesday,wednesday,thursday,friday}varday=Weekday.mondayday=.friday// enum type annotationenumUIStyle{caselight,dark,system}varstyle:UIStyle=.light
letplayer:String="Roy"varluckyNumber:Int=13letpi:Double=3.141varisEnabled:Bool=truevaralbums:Array<String>=["Red","Fearless"]varuser:Dictionary<String,String>=["id":"@twostraws"]varbooks:Set<String>=Set(["The Bluest Eye","Foundation"])// Alternate syntax for arrays and dictsvaralbums:[String]=["Red","Fearless"]varuser:[String:String]=["id":"@twostraws"]// empty arrays can be written either of these waysvarteams:[String]=[String]()varclues=[String]()
enumWeather{casesun,rain,wind}letforecast=Weather.sunswitchforecast{case.sun:print("A nice day.")case.rain:print("Pack an umbrella.")default:print("Should be okay.")}
break to exit the loop and skip all remaining iterations
1
2
3
4
5
6
7
8
9
10
11
12
forosinplatforms{print("Swift works on \(os).")}foriin1...12{print("5 x \(i) is \(5*i)")}// if you don't need the iteratorfor_in1...5{}whilecount>0{print("\(count)…")count-=1}
funcgetUser()->(firstName:String,lastName:String){(firstName:"Taylor",lastName:"Swift")}letuser=getUser()print("Name: \(user.firstName)\(user.lastName)")//if you don't need all the values, you can use `_` to ignore somelet(firstName,_)=getUser()print("Name: \(firstName)")
Pass in a default value if there is none passed in.
1
2
3
4
5
6
7
8
9
10
funcgreet(_person:String,formal:Bool=false){ifformal{print("Welcome, \(person)!")}else{print("Hi, \(person)!")}}// now we can call `greet()` in two waysgreet("Tim",formal:true)greet("Taylor")
letstring="12345"do{letresult=trycheckPassword(string)print("Rating: \(result)")}catchPasswordError.obvious{print("I have the same combination on my luggage!")}catch{print("There was an error.")}
Builtin Closures are used extensively. For example, an array method called filter(),
if the closure returns true, get returned in a new array, we could filter
an array to include only names beginning with T:
filter() must be given a function that accepts one item from and array and returns
true. Because the function must behave like that, we can omit the type:
1
2
3
letonlyT=team.filter({nameinname.hasPrefix("T")})
Trailing Closure Syntax. If the closure expression is the method’s only argument
and you provide that expression as a trailing closure, you can omit the ()
1
2
3
letonlyT=team.filter{nameinname.hasPrefix("T")}
Finally, sift provides short parameter names for us (suggest only use for a single
parameter)
it also silently generates memberwise initializer based on the properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
structAlbum{lettitle:Stringletartist:StringvarisReleased=truefuncprintSummary(){print("\(title) by \(artist)")}// if you want to be able to change a property from a methodmutatingfuncremoveFromSale(){isReleased=false}}letred=Album(title:"Red",artist:"Taylor Swift")print(red.title)red.printSummary()
If we want to be able to right to vactionRemaining, we need to provide the
getter and settter
1
2
3
4
5
6
7
8
9
10
varvacationRemaining:Int{get{vacationAllocated-vacationTaken}set{// new value is provided by Swift and stores what the user passed invacationAllocated=vacationTaken+newValue}}
These are callbacks, didSet runs when the property just changed and willSet
runs before the property changed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
classStepCounter{vartotalSteps:Int=0{willSet(newTotalSteps){// if you don't set the parameter, swift provides this as newValueprint("About to set totalSteps to \(newTotalSteps)")}didSet{iftotalSteps>oldValue{print("Added \(totalSteps-oldValue) steps")}}}}letstepCounter=StepCounter()stepCounter.totalSteps=896
Four access controls are the most common but there are more.
Use private for “don’t let anything outside the struct use this.”
Use private(set) for “anything outside the struct can read this, but don’t let them change it.”
Use fileprivate for “don’t let anything outside the current file use this.”
Use public for “let anyone, anywhere use this.”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
structBankAccount{// reading is fine, but writing can't be done outside the structprivate(set)varfunds=0mutatingfuncdeposit(amount:Int){funds+=amount}mutatingfuncwithdraw(amount:Int)->Bool{iffunds>amount{funds-=amountreturntrue}else{returnfalse}}}
classEmployee{lethours:Intinit(hours:Int){self.hours=hours}funcprintSummary(){print("I work \(hours) hours a day.")}}classDeveloper:Employee{funcwork(){print("I'm coding for \(hours) hours.")}}letnovall=Developer(hours:8)novall.work()novall.printSummary()
in order to change a method’s functionality, you need to override it.
1
2
3
overridefuncprintSummary(){print("I spend \(hours) hours a day searching Stack Overflow.")}
All copies of a class instance share their data, changes to one will automatically
change the data in others. In comparison, struct copies don’t share their
data.
1
2
3
4
5
6
7
8
9
10
11
classSinger{varname="Adele"}varsinger1=Singer()varsinger2=singer1singer2.name="Justin"print(singer1.name)// Justin -> not Adeleprint(singer2.name)// Justin
The final difference is that classes allow us to change variable properties
when the class itself is constant, as a result classes don’t need the
mutating keyword with methods that change their data.
Protocols are interfaces. They define functionality and swift ensures conformance.
The protocol only defines the methods, not the implementation.
Once you have a protocol, you can make a data types conform to it by implementing
the required functionality (all protocols must be implemented)
This allows us to write a function that accepts any kind of type that conforms
to Vehicle because Swift knows it implemnents both estimateTime() and travel().
Protocols can also require properties. You can define as a get that might
be a constant or computed property and one marked with get set that might
be a variable or computed property with a getter and setter
1
2
3
4
5
6
7
8
9
10
protocolVehicle{varname:String{get}varcurrentPassengers:Int{getset}funcestimateTime(fordistance:Int)->Intfunctravel(distance:Int)}// all conforming types must implement the two properties,// like this for `Car`letname="Car"varcurrentPassengers=1
Tip
You can conform to as many protocols as you need, just by listing them separated
by commas
Extensions let us add functionality to any type, both user defined and built-in!
For example, swift strings have a method to trim whitespace and new lines, but
it’s quite long so you could turn it into an extension.
Use ed or ing to return a copy of the data
Use the verb to return a mutated version of the data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
varquote=" The truth is rarely pure and never simple "// return a copy of the dataextensionString{functrimmed()->String{self.trimmingCharacters(in:.whitespacesAndNewlines)}}lettrimmed=quote.trimmed()// return a mutate form of the dataextensionString{mutatingfunctrim(){self=self.trimmed()}}quote.trim()
Extensions can also add a computed properties to types like this:
1
2
3
4
5
6
7
8
9
10
11
12
extensionString{varlines:[String]{self.components(separatedBy:.newlines)}}letlyrics="""
But I keep cruising
Can't stop, won't stop moving
"""print(lyrics.lines.count)// 2
This means that we can list required methods in a protocol and then add default implementations inside a protocol extension. All conforming types then get to use those default implementations or provide their own.
peachOpposite doesn’t exist, so this can’t be a regular string. Swift’s solution is called optionals.
An optional string might have a string or a nil, any kind of data can be optional including Int, Double and Bool as well as enums, structs, and classes.
Swift won’t let us use the optional data directly as it might be empty, we have to unwrap the optional to use it.
There are multiple ways to do this, but the most common looks like this:
1
2
3
ifletmarioOpposite=opposites["Mario"]{print("Mario's opposite is \(marioOpposite)")}
if let runs the code inside the braces if the optional had a value;
guard let runs the code if the optional doesn’t have a value (ala unless)
1
2
3
4
5
6
7
8
funcprintSquare(ofnumber:Int?){guardletnumber=numberelse{print("Missing input")return}print("\(number) x \(number) is \(number*number)")}
If you use a guard to check a function’s inputs are valid, Swift requires you to use return if the check fails. If successful, you can use the let after the guard clause finishes.
Tip
You can use a guard with any condition, including ones that don’t unwrap optionals.
Swift has a third way of unwrapping optionals called the nil coalescing operator. It unwraps an optional and provides a default value if the optional is empty.
The nil coalescing operator is useful in many places optionals are created, for example, creating an Int from a string returns an optional Int? because the conversion might have failed so we can provide a default value:
When calling a function that might throw errors, we can use try? to convert its result into an optional containing a value on success or nil otherwise.
1
2
3
4
5
6
7
8
9
10
11
12
enumUserError:Error{casebadID,networkFailed}funcgetUser(id:Int)throws->String{throwUserError.networkFailed}ifletuser=try?getUser(id:23){// getUser always throws an error so we never get here.print("User: \(user)")}