Lecture Handout

Welcome

Hello! Welcome to AppDev’s iOS Training Course. Throughout this course, you will learn all the fundamentals of iOS programming and learn how to build your own full-fledged iOS applications! Each lecture will be accompanied by a handout (like this) summarizing the main concepts from that lecture. This is the first handout and we will be covering Swift and Xcode.

Xcode

What is Xcode?

Xcode is the main integrated development environment (IDE) that iOS developers use to develop their applications. In simpler terms, Xcode is just the application that we use to write iOS code (similar to how you might use Atom, Sublime, vim, etc.). The reason all iOS developers use Xcode (instead of other IDE’s) is that other than being a place for us to write iOS code, Xcode allows us to run our code and build our application on a simulator. This is important because otherwise we would have no way to testing if our code actually does what it wants!

Creating a new Xcode project

Setting Up a New Xcode Project

Swift

What is Swift?

Swift is Apple’s newest programming language for developing iOS applications. One other popular programming language that you can use is Objective-C. However, in this course we will be using Swift. Thus, you will be writing Swift code inside your Xcode project and then running that on the simulator to see your application come to life.

Swift Basics

We will now cover some fundamentals of the Swift programming language. One thing to note is that while we will not be able to cover every single aspect of Swift in this handout, any aspect not covered can be found through a simple Google search.

Variables and Constants

var x = 5
var y: Int = 5
y = 6

Like other programming languages, we can declare a variable in Swift using the syntax var [variable name] = [value].

Unlike languages like Java, where value types have to be explicitly typed, Swift has type-inferencing so Swift is able to infer the type of your variable based on the value that you assign to it. For example, by assigning 5 to x, Xcode knows that x is of type Int because 5 is of type Int. Therefore, when declaring a new variable and assign a value to it, it is not required to also declare the type of the variable.

If you wanted to, however, you could do so using the syntax var [variable name]: [type] = [value] There are also some cases where the type is ambiguous and Swift cannot automatically infer the type so you'd have to explicitly declare the type for the compiler.

let z = 7
let y: Int = 5
z = 8 // Error!

In addition to variables, we can also declare a constant. One important distinction to make between variables and constants is that after you assign a value to a constant, you cannot change the constant’s value again in the future (you can change a variable’s value). The syntax to declare a constant is let [variable name] = [value]. Similar to variables, it is not required to declare the type of the constant, although you can.

Classes and Structs

// class name
class Song {

    // field declarations
    var name: String
    var author: String
    
    // methods
    init(name: String, author: String) {
        self.name = name
        self.author = author
    }

}

In Swift, we can also declare classes and structs. Every class contains several different parts: a class name, fields, and methods. Fields are just variables/constants that belong to that class. In our sample class above, for example, our Song class contains a name field of type Stringand an author field of type String. Similarly, methods are just functions that belong to that class. In our example above, we have an init method which is called when you create an instance of that class.

A struct has the same exact syntax as a class (except you replace class with struct). However, a core difference between structs and classes is that structs are value types whereas classes are reference types. What does this mean? A value type “is a type whose value is copied when it’s assigned to a variable or constant, or when it’s passed to a function.” On the other hand, reference types “are not copied when they are assigned to a variable or constant, or when they are passed to a function. Rather than a copy, a reference to the same existing instance is used.” To see what this means, let us look at the following example.

var songA = Song(name: "Lucky You", author: "Eminem")
var songB = songA
songB.name = "Walk on Water"
print(songA.name) // prints "Walk on Water"
print(songB.name) // prints "Walk on Water"

Using the Song class that we declared before, we create an object of type Song and assign variable songA to that Song object. Next, we create another variable songB and assign its value to be songA. Then, we set the name field of songB to be “Walk on Water” (previously was “Lucky You”). Lastly, we print the value of songA’s name field and the value of songB’s name field. In both cases, “Walk on Water” gets printed.

Why does this happen? Well, because Song is a class and we know that classes are reference types, when we created our new Song object and assigned that to songA, what really happened is that songA’s value was a pointer to that Song object. Then, when we assigned songA to songB, what we were doing was assigning songB that exact same pointer. Thus, when I changed the value of songB’s name field, we actually altered the object that both songA and songB were pointing to, and thus the value of songA’s name field changed as well.

If instead Song was a struct, we would see something else happen:

struct Song { ... }
var songA = Song(name: "Lucky You", author: "Eminem")
var songB = songA
songB.name = "Walk on Water"
print(songA.name) // prints “Lucky You”
print(songB.name) // prints "Walk on Water"

Looking at the example above, we see that printing out songA’s name prints out “Lucky You” and printing out songB’s name prints out “Walk on Water” (unlike the previous case where they both printed out “Walk on Water”). Why did this happen? Well, remember that structs are value types. Thus, when songA was assigned to songB, a copy of the Song object that songA was pointing to was passed to songB (unlike the previous case where the pointer to the exact same Song object was passed to songB). Thus, when we changed songB’s name field, songA’s name field was not changed because songA and songB are pointing to two different Song objects.

Optionals

In Swift, we also have a type that you may not often see in other programming languages, optionals. Optional types just mean that a variable may hold some value or it may not (i.e. it is nil). For example, let's consider a situation where we want to look at assignment grades for a student.

var grade1: Int
var grade2: Int
var grade3: Int

What if the TA hasn't graded assignment 3 for the student yet? How do we represent that grade3 might not actually have an integer value? Well, this is where optionals come in.

var grade1: Int?
var grade2: Int?
var grade3: Int?

grade1 = 10
grade3 = 9
// grade2 = nil

By making this change, we have redeclared grade1, grade2, and grade3 to be variables of an optional Int type so that it is possible for any of these variables to actually be nil, for the case when the TA has not yet provided a grade. In the example above, after the TA provides grades to assignment 1 and assignment 3, grade1 and grade2 will now have integer values. However, grade3 will still hold a nil value.

Now suppose we want to print out the grades for the student.

print(grade1) // 10
print(grade2) // Error: value of optional type ‘Int?
print(grade3) // 9

Trying to print grade2 will cause an error because grade2 could potentially be nil. In order to actually use this variable, we have to first unwrap the value inside the variable and check if it actualyl has a value. There are two main ways to unwrap a variable: using an if-let statement and using an guard-let statement. First, let's take a look at unwrapping using an if-let statement:

if let unwrappedGrade2 = grade2 {
    // this block gets run if variable a holds a value
    print(grade2)
} else {
    print("grade2 contains no value!")
}

The syntax for an if-let statement is if let [unwrapped constant name] = [optional variable] { … } else { … }. In our example above, we take optional variable grade2 and then we try to unwrap it. If grade2 actually does hold some value, then that value gets assigned to the constant unwrappedGrade2 and then we enter the first block of code. If, however, grade2 was nil, the second block would get run.

Another way that you can unwrap a value is using guard-let.

guard let unwrappedGrade2 = grade2 else {
    // this block gets run if variable a was nil
    // in here, you should return or throw an error
    print("grade2 contains no value!")
    return
}
// this gets run if variable a holds some value
print(unwrappedGrade2)

The syntax for a guard-let statement is guard let [unwrapped constant name] = [optional variable] else {…}. In our example above, we take optional variable grade2 and then we try to unwrap it. If grade2 actually does hold some value, then the rest of our code runs under the assumption that the new unwrappedGrade2 holds that value and grade2 is still an optional. If, however, grade2 was nil, the else block will get run. One thing to take note when using guard-let statements is that in the else-block, you must either return or throw some error at the end of it. This aspect is what distinguishes it from an if-let.

We often use guard-let statements more for cases where you want to assume an optional variable is non-nil for that portion of the code, and if not, then we want to exit or throw the proper error. if-let statements are better for cases where either nil or a real value is fine, we just need separate cases to handle each one.

Last updated