Skip to content

Instantly share code, notes, and snippets.

@loganwright
Last active August 29, 2015 14:06
Show Gist options
  • Save loganwright/a37ad0ee9411d84cd88c to your computer and use it in GitHub Desktop.
Save loganwright/a37ad0ee9411d84cd88c to your computer and use it in GitHub Desktop.
SwiftLessonOne_Revamp

#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.

  1. The inclusive range operator: ... "from left value, up to and including right value"
  2. 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")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment