Swift Basics

Variables

Swift has two types of basic containers for data. Variables (denoted by the var keyword) can contain mutable data, meaning that it can change over the course of your program’s runtime. For example, we can have a variable var price = 50 denote the price on some item: if later on, we want to represent a discount on the item, we can change the value price = 25 for 50% off.

Constants (denoted by the let keyword) are set upon initialization and cannot be changed once they are set. For some example, if you want all your buttons to have the same width and height, it’s good practice to assign these values to constants and reuse them in creation i.e. let width = 150 and let height = 50. Attempting to change these values later on will throw a compilation error.

The main benefit using constants provides is protection against programming errors, so you don’t modify variables that other parts of your code might rely on to be a certain value. If you know that there is some variable you are never going to change, you should declare it as a constant — it not only makes the code cleaner and easier to read/debug, but also provides some performance optimization.

Variables and constants all have some sort of type they fall into which restricts the kind of data they can store. Common types include String (represents text), Int (represents integer numbers), Float (represents numbers with a decimal point), and Boolean (represents true or false). More complex types include Array (represents a list of items) and Dictionary (represents a map between two different sets) and are explained below.

Something you might’ve noticed above is that we aren’t explicitly writing our variables to be of a certain type: the variable price is automatically assumed to be an Int. This is because variables types in Swift are inferred. It is not required to assign a type, as you have to in languages like C or Java. For example if you declare a variable like this:

var aNumber = 100

aNumber will forever be of type Int. However, we can still explicit declare a type for variables. For example, we can write:

var aNumber: Int = 100

The reason we might want to do this is for both code readability and to ensure that Swift doesn’t infer the type incorrectly. Let’s say we want var aNumber to be a Float: as talked about above, var aNumber = 100 will make Swift recognize it as an Int, and we might run into issues if we want to change the value like var aNumber = 100.5. In this case, we can instead declare it with a Float type:

var aNumber: Float = 100

 

Optionals

Optionals (denoted with a ? or a ! after the type) are a special type of variable in Swift. Normally, variables cannot be assigned to nil, which means that they are empty with no values associated: if you try to do so, Swift will throw an error and crash. Optionals allow this to happen. To declare a variable to be an Optional, we need to explicitly declare its type.

We can create some variables like this:

var aFloat: Float?

var aDouble: Double!

Notice that we have two different types of Optionals: those created with a ? and those created with a !. While they look similar, these are actually quite different in usage.

Optionals created with a ? can be set to nil at any time: they continuously fluctuate between having and not having a value. Optionals created with a ! can be nil at first, but once they are initialized with an actual value, they can never be nil again: trying to do so will throw an error. These are what we call implicitly unwrapped Optionals and can be treated as normal variables. We use them when we have a variable we initialize later, but is essential to the functionality of our app. They must have a value for correct behavior of what we are building. Thus, if by the time we need them, they have not yet been initialized, our app shouldn’t be allowed to run and should crash.

Optionals can be “force unwrapped” (the value will be “force read”) by adding a ! to the end. For example, we can access the value of an Optional opt and assign it to another variable x with the following code:

let x = opt!

This assumes that opt‘s value is not nil, so if it is, our code will throw an error. An interesting thing to note is that we only need to unwrap normal Optionals created with a ?. Those created with a ! are treated and accessed as normal variables, and assumed to have a value, so they don’t need to be unwrapped: that’s why we call them implicitly unwrapped. With a normal Optional (those created with a ?), when unwrapping their value, we need to be conscious of the fact that it can always either be a value or nil, and thus we need to be able to handle both cases properly.

Note: Xcode will frequently propose unwrapping an optional to fix a variety of common issues. This is usually not the best way to fix a problem!! Make sure you consider what is causing an error before you use Xcode’s auto-solver.

We can avoid these force unwrap issues by using something like an if let statement. The structure is as follows:

var optionalName: String? = "John Appleseed"
if let name = optionalName {
    print("Hello, (name)")
}

In this example, “Hello, John Appleseed” will only be printed if optionalName is not nil. In this case, it is assigned to “John Appleseed,” so therefore it will be printed. If we were to assign optionalName to nil, nothing will be printed and the program continue on without crashing. These are a much more ideal way to deal with Optionals as it allows us to write code to run only when Optionals have real values. Another good way to unwrap them is with guard statements, which basically allow us to define default behavior for Optionals when their values are nil:

guard let name = optionalName else {
    print("I have no name")
}

 

Functions

Functions allow us to write blocks of organized, reusable code that perform a specific task and that can be called anywhere else in the code. Basically, functions allow us to easily reuse code and provide modularity within our app. Some possible functions you might build include reading in user input or sorting a list of items you have.

Functions in Swift are structured in a very clear format: they take in as many arguments as you want, and return either a value or nil. All functions have a basic signature that will follow the below structure:

func lookAtThis(number: Int) -> String

This is a function called “lookAtThis” with a single argument “number” of type Int. It will return a variable of type String. To call this function, we write it like this:

let str = lookAtThis(number: 3)

Notice that we need to explicitly say which parameter (argument) we’re assigning a value to. Usually the argument label is required when you call the function unless you prefix it with an _ when it is declared. For example, we can write “lookAtThis” like:

func lookAtThis(_ number: Int) -> String

Then, accessing it would look like:

let str = lookAtThis(3)

 

Arrays & Dictionaries

Arrays are ordered lists of items, and allow us to have a single variable representing multiple, related values. For example, let’s say we have some tasks we need to do: eating, working, showering, and sleeping. Instead of instantiating separate Strings for each of these tasks, we can create a single array:

let tasks = ["eat", "work", "shower", "sleep"]

Something to note is that arrays have their own types as well: above, tasks is a String array, so it only takes in values that are Strings. An array of numbers (Ints) could be declared like this:

let oddNumbers = [1, 3, 5, 7, 9, 11, 13, 15]

The syntax of creating and accessing arrays is very similar to that of other programming languages. To access a specific element, we need to specify what index we want: an index is simply a defined place in the array, and starts at 0 for the first element and increments by 1 for each element to the right. For example, to get the 3rd element in our above array, we can write:

let num = oddNumbers[2]

While with arrays, our indices are very defined (always non-negative integers that increase in increments of 1), dictionaries let us store data and index them with different types: in essence, a dictionary is an unordered collection that stores multiple values of the same type. It maps keys to values and often allows us to more intuitively define certain situations. They are declared like this:

var interestingNumbers = ["primes": [2, 3, 5, 7, 11, 13, 17],
                          "triangular": [1, 3, 6, 10, 15, 21, 28],
                          "hexagonal": [1, 6, 15, 28, 45, 66, 91]]

If we wanted to access the array of prime numbers, we would use the statement interestingNumbers["primes"], because the “primes” array is stored under the key “primes”.

 

Control

In code, we have something called a conditional, a statement that will either evaluate to true or false. We can have code that only executes when a certain condition is true, such as:

if candace == "cool" {
    print("The world is right")
} else {
    print("Something's wrong")
}

If our String candace has the value “cool” (which should always be true for obvious reasons!!!!), then we print “The world is right”. However, if for some reason, this is not true, we can run some code within an else statement: we print “Something’s wrong.” These are if-else blocks.

While statements are the same as any other programming language and continuously run blocks of code until their condition becomes false. They are used as such:

while aBoolean{
  // do something
}

This while loop will continuously run until aBoolean becomes false. Be careful! If the condition never becomes false, our code will be stuck in an infinite loop and crash.

For loops allow us to repeatedly execute code a specific number of times. They’re safer than while loops since we specify how many times we repeat.

Let’s say we want to execute code for 5 iterations. The syntax to do so is:

for n in 1...5 {
    print(n)
}

This will print out 1 2 3 4 5. Note that this is inclusive of the number 5. If we want to iterate up to, but not including 5, we would write it as below:

for n in 1..<5 {
    print(n)
}

This will print out 1 2 3 4.

Another major use-case of for-loops is iterating through all the items in a collection (for example, an array) and performing an action for each element. These take on a slightly unique syntax in Swift. An example for loop is as follows:

for uni in universities{
  //do something with uni
}

This for loop will iterate over every element in the list universities and run the specified code.

 

Classes

Classes are a huge and extremely important part of programming that we don’t cover in detail here simply because of its complexity, but they’re extremely important in abstraction and code reusability, allowing us to create building blocks for complex systems. They are essentially blueprints containing a mixture of variables and functions that create what we call objects. For example, we can have a class Dog that has some variables like name and breed, and some functions eating, barking, and playing. We usually can’t use the class Dog to access its methods (if you’re curious about when we can, look into static methods!), so we need to create an instance of Dog using its constructor, a special method specifically used for this purpose. We do this like so:

let dog = Dog()

What’s the difference between an instance and a class? You could think of dog to be a specific dog, like my dog or your dog, and Dog to be a blueprint of how to create dogs, like the species itself: we know all dogs are of the species Dog, but each dog is unique and operates individually.

We can use instances to access variables (dog.species) and call functions (dog.bark()). Each instance usually keeps track of their own variables (for times when this is not true, look into class variables!), so one dog could have a different weight than another dog through modification. They might’ve been initialized to the same value, but each instance could update their own variables.

In short, classes allow us to further abstract our code out, and you should take the time to learn them from another resource. They are a cornerstone of a huge programming paradigm called object-oriented programming, which is foundational.

 

Extensions

Extensions are used to add new functionality to an existing class, structure, enumeration, or protocol type (elaborated upon later!). Basically, it allows you to “extend” the functionality of basic Swift types, or split up some class you’re writing across multiple files. This is useful to clean up your code and modularize your project into smaller files.

extension String {
    subscript (r: Range<Int> ) -> String {
        *** YOUR CODE HERE ***
    }
}

In doing so, we have extended the String class and added to its functionality some function subscript.