SwiftUI Stanford Course Assigned Reading Notes (Part 1)

August 10, 2020

Snippets

SwiftUI

I’m starting to learn SwiftUI by following a course on Stanford’s Website called CS193p – Developing Apps for iOS. There are specific assigned readings and that is what I took notes on below. Part 2 is here, and the rest of the course notes can be found here.


The Basics

var x = 0.0, y = 0.0, z = 0.0
print("The value of friendlyWelcome is \(friendlyWelcome)")

Semicolons are only required if you want to write multiple separate statements on a single line such as let cat = “🐱”; print(cat)

let anotherPi = 3 + 0.14159

Tuples

Tuples enable you to create and pass around groupings of values. You can use a tuple to return multiple values from a function as a single compound value. So basically Tuples group multiple values into a single compound value. You can retrieve a tuple’s value with the below code 👇

let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// Prints "The status code is 404"

If you only need some of the tuple’s values, ignore parts of the tuple with an underscore _ An example is below 👇

let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"

Alternatively, access the individual element values in a tuple using index numbers starting at zero:

print("The status code is \(http404Error.0)")
// Prints "The status code is 404"
print("The status message is \(http404Error.1)")
// Prints "The status message is Not Found"

You can name the individual elements in a tuple when the tuple is defined, kind of like values in an array: let http200Status = (statusCode: 200, description: “OK”). If you name the elements in a tuple, you can use the element names to access the values of those elements:

print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"

Assertions & Preconditions

Assertions and preconditions are checks that happen at runtime. Assertions help you find mistakes and incorrect assumptions during development, and preconditions help you detect issues in production. The difference between assertions and preconditions is in when they’re checked: Assertions are checked only in debug builds, but preconditions are checked in both debug and production builds. In production builds, the condition inside an assertion isn’t evaluated. This means you can use as many assertions as you want during your development process, without impacting performance in production.

An example of an Assertion is below. Note that an assertion message is not required, but it is helpful.

let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 is not >= 0.

If the code already checks the condition (like in an if statement), you can use the assertionFailure() function to indicate that an assertion has failed. For example:

if age > 10 {
    print("You can ride the roller-coaster or the ferris wheel.")
} else if age >= 0 {
    print("You can ride the ferris wheel.")
} else {
    assertionFailure("A person's age can't be less than zero.")
}

Use a Precondition whenever a condition has the potential to be false, but must definitely be true for your code to continue execution. An example is below 👇

precondition(index > 0, "Index must be greater than zero.")

You can use the fatalError() function during prototyping and early development to create stubs for functionality that hasn’t been implemented yet, by writing fatalError(“Unimplemented”) as the stub implementation.

Basic Operators

The values that operators affect are operands. In the expression 1 + 2, the + symbol is a binary operator and its two operands are the values 1 and 2.

The remainder operator (%) is also known as a modulo operator in other languages. However, its behavior in Swift for negative numbers means that, strictly speaking, it’s a remainder rather than a modulo operation.
9 % 4 // equals 1

You can compare two tuples if they have the same type and the same number of values. Tuples are compared from left to right, one value at a time, until the comparison finds two values that aren’t equal. Those two values are compared, and the result of that comparison determines the overall result of the tuple comparison. When the tuples’ first elements are the same their second elements are compared.

A ternary conditional operator is a special operator with three parts, which takes the form question ? answer1 : answer2. If question is true, it evaluates answer1 and returns its value; otherwise, it evaluates answer2 and returns its value.

Strings & Characters

If you need a string that spans several lines, use a multiline string literal—a sequence of characters surrounded by three double quotation marks. If you want to use line breaks to make your source code easier to read, but you don’t want the line breaks to be part of the string’s value, write a backslash (\) at the end of those lines

let softWrappedQuotation = """
The White Rabbit put on his spectacles.  "Where shall I begin, \
please your Majesty?" he asked.

"Begin at the beginning," the King said gravely, "and go on \
till you come to the end; then stop."
"""
if emptyString.isEmpty {
    print("Nothing to see here")
}
for character in "Dog!🐶" {
    print(character)
}

You can create a standalone character with let exclamationMark: Character = “!” And you can also turn an array of characters into a string by using the below code:

let catString = String(catCharacters)

To retrieve a count of the Character values in a string, use the count property of the string. Example is stringName.count

You can use the use the insert(_:at:) and remove(at:) methods to insert or remove a certain character within a string.

Collection Types

Swift provides three primary collection types, known as arrays, sets, and dictionaries, for storing collections of values. Arrays are ordered collections of values. Sets are unordered collections of unique values. Dictionaries are unordered collections of key-value associations.

Arrays

You can use the .count method to find out how many items are in an array.

Use the Boolean isEmpty property as a shortcut for checking whether the count property is equal to 0:

You can add a new item to the end of an array by calling the array’s append(_:) method. Alternatively, append an array of one or more compatible items with the addition assignment operator (+=): The two examples are shown below:

shoppingList.append("Flour")
shoppingList += ["Baking Powder"]

To insert a new value in a specific place use the insert(_:at:) method. Similarly, you remove an item from the array with the remove(at:) method:

shoppingList.insert("Maple Syrup", at: 0)
// retrieve value
var firstItem = shoppingList[0]
// change value
shoppingList[0] = "Six eggs"
for item in shoppingList {
    print(item)
}

Dictionaries

You can add a new item to a dictionary with subscript syntax. Use a new key of the appropriate type as the subscript index, and assign a new value of the appropriate type:

airports["LHR"] = "London"
// the airports dictionary now contains 3 items
airports["LHR"] = "London Heathrow"
// the value for "LHR" has been changed to "London Heathrow"

You can iterate over the key-value pairs in a dictionary with a forin loop. Each item in the dictionary is returned as a (key, value) tuple, and you can decompose the tuple’s members into temporary constants or variables as part of the iteration:

for (airportCode, airportName) in airports {
    print("\(airportCode): \(airportName)")
}
// LHR: London Heathrow
// YYZ: Toronto Pearson

Control Flow

For-In Loops

You use the forin loop to iterate over a sequence, such as items in an array, ranges of numbers, or characters in a string:

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
    print("Hello, \(name)!")
}
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}
for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}

In some situations, you might not want to use closed ranges, which include both endpoints. In this case we are iterating the number of times that the variables value is set to:

let minutes = 60
for tickMark in 0..<minutes {
    // render the tick mark each minute (60 times)
}

If you want to do the same as the above but only do it every 5 times, use the stride(from:to:by) function:

let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    // render the tick mark every 5 minutes (0, 5, 10, 15 ... 45, 50, 55)
}

While Loops

while loop performs a set of statements until a condition becomes false. These kinds of loops are best used when the number of iterations is not known before the first iteration begins. Swift provides two kinds of while loops:

While

while loop starts by evaluating a single condition. If the condition is true, a set of statements is repeated until the condition becomes false.

while condition {
    statements
}

Repeat-While

The repeatwhile loop, performs a single pass through the loop block first, before considering the loop’s condition. It then continues to repeat the loop until the condition is false.

repeat {
    statements
} while condition

Conditional Statements

Switch Statements

let someCharacter: Character = "z"
switch someCharacter {
   case "a":
      print("The first letter of the alphabet")
   case "z":
      print("The last letter of the alphabet")
   default:
      print("Some other character")
}

You can also create a compound switch case, in which you test two or more possible values. For readability, a compound case can also be written over multiple lines. An example is below.

let anotherCharacter: Character = "a"
switch anotherCharacter {
   case "a", "A":
      print("The letter A")
   default:
      print("Not the letter A")
}

When matching tuples in switch statements, and multiple matches are possible, the first matching case is always used.

let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
   case (let distance, 0), (0, let distance):
      print("On an axis, \(distance) from the origin")
   default:
      print("Not on an axis")
}
// Prints "On an axis, 9 from the origin"

Control Transfer Statements

Swift has five control transfer statements.

Break

The break statement can be used inside a switch or loop statement when you want to terminate the execution of the switch or loop statement earlier than would otherwise be the case.

Fallthrough

In Swift the switch statement completes its execution as soon as the first matching case is completed. This is in contrast to C, where the switch statement runs through each possible case that does not have a break statement. The fallthrough statement allows you to mimic the behavior in C and “fall through” to the next switch statement, even after you’ve found a matching statement.

Early Exit

A guard statement lets you write the code that’s typically executed without wrapping it in an else block, and it lets you keep the code that handles a violated requirement next to the requirement. An example is below:

func greet(person: [String: String]) {
    guard let name = person["name"] else {
        return
    }

    print("Hello \(name)!")
}

greet(person: ["name": "John"])
// Prints "Hello John!"

These notes are continued in Part 2 found here.

@joekotlan on Twitter