#Introduction to Variables
To declare a variable in Swift, simply assign something to it, like below:
import Cocoa
var str = "Hello, playground"
As you can see, there is no explicit type declared. This is because type inference will notice that the variable str
is being assigned a string and the compiler will infer that str is of type String
You can declare a Type explicitly using a Type Annotation
like this:
var strVar: String = "Something"
The colon can be read as of type
so this code would read "variable strVar of type String equals "Something". This syntax is consistent throughout the language.
#Constants
In Swift, unlike Objective-C, there is another type of variable declaration with the keyword let
. At its most basic, let
indicates that something is constant and var
indicates that something is variable.
Let's look at a simple example by creating a new constant named someConstant
that will be an unchanging value and compare it with a similar variable:
let someConstant = "I am forever constant"
// Error
someConstant = "Let's modify it"
var someVariable = "A value"
// Ok
someVariable = "another value"
When dealing with constants, there is an important distinction between what this means for value types(structures and enums), and for class objects. When dealing with class objects, you cannot reassign the value, but you can modify the properties within that value (assuming these properties are variable). For example, if we were dealing with a constant view, we could still modify its background color:
let view = UIView()
view.backgroundColor = UIColor.yellowColor()
Whenever possible, one should take care to appropriately assign let
and var
values as this is part of Swift optimization. Apple notes:
If a stored value in your code is not going to change, always declare it as a constant with the let keyword. Use variables only for storing values that need to be able to change.
#Arrays & Dicts
When you see an array in swift you will notice it looks a little like javascript except that in Swift, arrays are type defined, meaning they need to know what type of object will be going inside. Let's look at an array of strings:
var stringArray: [String] = ["hello", "there", "sir"]
stringArray
When dealing with arrays in objective-c, there is a distinction between an NSArray
which is immutable, and an NSMutableArray
, which is of course, mutable. In Swift, this is handled by use of the let
and var
keywords. By declaring our stringArray
as a variable, we are saying that it is mutable. Let's take advantage of this and add some objects. You can use the function append
to add singular objects to the end of an array.
stringArray.append("how")
Or if we need to insert an object at a specific index, we can use the insert
function:
stringArray.insert("Well", atIndex: 0)
We can also concatenate two arrays using the +=
infix operator like so:
stringArray += ["are", "you"]
You can also use this syntax to add single objects by wrapping the single object as an array:
stringArray += ["today"]
We can remove elements by using the removeAtIndex
, removeLast
, and removeAll
functions:
stringArray.removeAtIndex(0)
stringArray.removeLast()
// Don't actually remove ... or delete this line before proceeding
// stringArray.removeAll(keepCapacity: false)
##Subarrays
To get a subarray, we can use a simple subscript syntax that contains a range, like so:
let subArray = stringArray[3...5]
The range operators in Swift are a handy little feature that comes in two styles.
- The inclusive range operator:
...
"from left value, up to and including right value" - The non-inclusive range operator:
..<
"from left value, up to but not including right value"
Let's look at an example of a non-inclusive range operator in getting a subarray:
let anotherSubArray = stringArray[0..<2]
For grabbing a specific element, we can use the familiar subscript syntax to get an object at a particular index:
stringArray[0]
By declaring values in our array during the declaration of stringArray
, it is already initialized. If we need to initialize an array before adding values, there are a variety of different syntax options, let's look at a couple:
// My personal preference (feel free to ignore)
var a: [Int] = []
// Others
var b = [Int]()
// Less good
var c: Array<Int> = []
var d = Array<Int>()
These will all initialize an empty array of type [Int]
.
Before we move on to dictionaries, let's look at one more cool function that allows us to initialize an array of a repeated value. For example, if we wanted an array of 3 UIView
objects, we could do this:
let viewArray = [UIView](count: 3, repeatedValue: UIView())
#Dictionaries
If you have never worked with dictionaries before, they are “A dictionary is a container that stores multiple values of the same type." “Unlike items in an array, items in a dictionary do not have a specified order. You use a dictionary when you need to look up values based on their identifier, in much the same way that a real-world dictionary is used to look up the definition for a particular word.
As with arrays, we use var
and let
to indicate the mutability of an array, and Dictionaries are also type defined through an explicit type annotation or through type inference.
Let's make a basic dictionary:
var airports: [String : String] = ["EWR": "Newark", "JFK": "John F Kennedy"]
We declared our airports as variable with the var
keyword so that we can modify its contents later. We also declared the type of dictionary [String : String]
explicitly, although in this case it's not necessary. Let's remove the type annotation.
Swift is fine with this because it can use type inference to analyze the contents of the dictionary we're initializing to get its type. For more ambiguous situations, or when initializing a dictionary without values, a type annotation is usually necessary.
###Adding Objects
Adding items to a dictionary follows the similar subscript syntax of ObjC NSMutableDictionaries
airports["PHL"] = "Philadelphia"
airports
You can also use this syntax to update values already present in the array:
airports["JFK"] = "JFK Airport"
There's another way to update dictionary values that implements the function updateValue
. This provides the added functionality of returning the previous value as an optional (more on optionals later). For example, if we did something like this:
let previousValue = airports.updateValue("Philly", forKey: "PHL")
previousValue
Then previousValue
would contain the value "Philadelphia"
. If however we updated a value that didn't previously exist, like so:
let oldValue = airports.updateValue("Kalamazoo", forKey: "AZO")
oldValue
Then oldValue
would be nil
because there was no previous value. This can be nested into if statements like so:
if let oldValue = airports.updateValue("Kalamazoo", forKey: "AZO") {
println(oldValue)
} else {
println("There was no old value")
}
###Removing Objects
We can also remove objects using the subscript syntax by assigning the values to nil
like so:
airports["JFK"] = nil
Or, we can use a function similar to updateValue
for removing, called removeValueForKey
. This function returns the previous value for the key specified. For example, since we just removed the value at key "JFK", it no longer exists. If we remove this with removeValueForKey
, we'll get nil:
let oldValue = airports.removeValueForKey("JFK")
As with updateValue
, we can nest this remove function in an if statement:
if let oldValue = airports.removeValueForKey("AZO") {
println("Removed \(oldValue)")
} else {
println("No value")
}
###Initializing
Like with arrays, there are a variety of ways to initialize dictionaries:
// Personal preference
var a: [String : String] = [:]
// Others
var b = [String : String]()
// Less good
var c: Dictionary<String, String> = [:]
var d = Dictionary<String, String>()
#Loops
Loops in swift accomodate the very traditional for-condition-increment
loop with the following syntax:
let names = ["Joe", "Jim", "Bob"]
for var i = 0; i < names.count; i++ {
println(names[i])
}
This is a very common for loop that is present in most languages, but in Swift, the for-in
loop is so powerful that there are very few situations in which you'll need to make use of this type of syntax. Let's look at some uses of the for-in
loop
###Arrays
To iterate through all objects of the array, we can use the following example:
let cities = ["New York", "Boston", "Paris"]
for city in cities {
println(city)
}
If we need access to the indexes of the objects as well, we can use the enumerate function like so:
for (idx, city) in enumerate(cities) {
println("Found \(city) at index \(idx)")
}
###Dictionaries
We can easily iterate through dictionaries with the following syntax:
let dictionary = ["Name" : "Jim", "Profession" : "Salesman", "HairColor" : "Brown"]
for (key, value) in dictionary {
println("Found value \(value) for key \(key)")
}
###Strings
Because strings in Swift are basically a collection of characters, the for-in
statement can even be used to concisely iterate through characters in a string:
for char in "hello world" {
println(char)
}
###Ranges
We mentioned inclusive and non-inclusive ranges a bit earlier, and these can also be iterated:
for i in 1...5 {
println(i)
}
If there were a situation where the current value didn't matter and we just needed to complete an operation a certain number of times, we could ignore the value by declaring it as an underscore:
for _ in 1...3 {
println("Ran")
}
#Control Flow
###If Statements
If statements are a staple of any programming language and Swift is no different. The syntax is relatively similar to Objective-C except there are no parenthesis.
let x = 1
if x == 1 {
println("x equals 1")
} else if x == 2 {
println("x equals 2")
} else {
println("x doesn't equal 1 or 2")
}
If statements become more powerful in Swift when they are coupled with optionals and downcasting, but we will talk more about that functionality when we get to those topics.
###Switch Statements
Switch statements behave a little bit differently than in Objective-C, but in ways that I think vastly improve their functionality. Like many features in comparison to ObjC, switch statements don't make use of parenthesis. Let's look at our if-else
workflow from above in a switch statement.
switch x {
case 1:
println("x equals 1")
case 2:
println("x equals 2")
default:
println("x doesn't equal 1 or 2")
}
This probably looks relatively familiar, except no break
statements. In Swift, breaking is default behavior and if you instead want fallthrough behavior, this must be declared explicitly like so:
switch x {
case 1:
fallthrough;
case 2:
println("x equals 1 or 2")
default:
println("x doesn't equal 1 or 2")
}
However, in Swift there's a better way we could write this by grouping our values:
switch x {
case 1, 2:
println("x equals 1 or 2")
default:
println("x doesn't equal 1 or 2")
}
You can still use the break
keyword to jump to the end of the switch statement and there are some situations where this may be useful.
####Advanced Switch Statements:
Through the use of tuples, we can create very complex switch statements that evaluate a wide array of possibilities. For example, let's look at an example that requires us to check a coordinate:
let somePoint = (1,1)
switch somePoint{
case(0,0):
println("at origin")
case (_,0):
println("(\(somePoint.0), 0) is on the x-axis")
case (-2...2, -2...2):
println("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
println("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}
We can use the underscore to stand for any value, and we can use range operators to look for values within those ranges. This is extremely different from ObjC functionality and substantially more powerful.
We can also assign values within our switch statement cases to be accessible within that case:
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
println("on the y-axis with an x value of \(x)")
case (0, let y):
println("on the x-axis with a y value of \(y)")
case let (x, y):
println("somewhere else at (\(x), \(y))")
}
What the heck are we doing here? Value Binding. We are "binding" values to temporary constants the temporary constants are going to take on the value of part or both of the values of the tuple.
These however are limited because they don't let us specify what both values should be. Enter the where clause
.
switch anotherPoint {
case (let x, 0) where x == 2:
println("on the y-axis with an x value of \(x)")
case (0, let y) where y == 0:
println("on the x-axis with a y value of \(y)")
case let (x, y) where x != y:
println("somewhere else at (\(x), \(y))")
default:
println("I dunno")
}
In addition to traditional integer functionality and tuples, we can utilize switch statements for a wide variety of matching, including strings:
let someString = "hello"
switch someString {
case "hello":
println("Our string said hello")
case "bye":
println("Our string said bye")
default:
println("Our string said something else")
}
Switch statements are also useful for enums
values, but we'll talk more about that when we get there. Overall, they are a vast improvement over their Objective-C predecessors with a great deal of functionality.
Let's take a look at one more example that combines some of what we learned today:
// Constant because unchanging
let sentence = "Thanks for watching the tutorial!"
// var because mutable
var vowels: [Character] = []
for char in sentence {
switch char {
case "a", "e", "i", "o", "u":
vowels += [char]
default:
println("\(char) is not a vowel")
}
}
println("Found \(vowels.count) vowels")