# Swift and iOS Programming: Part 1 ## Learning objectives * Xcode setup + Install Dev Tools * Constants, Variables, and DataTypes: * Declare variables and constants * Setting values to variables and constants * Highlight differences between the two * Basic DataTypes: Numerics (Int, Double), Chars and Strings, Booleans, and Collections (Arrays, Dictionaries) * Operators: basic computations, data type conversion, and value comparison for simple control flows * Introduction to Optionals and proper unwrapping * Standard debugging method: breakpoint and debug area ## Install Xcode Go to the official [Xcode](https://developer.apple.com/xcode/) webpage to install this IDE. This will prompt further installation (all the development tools necessary). You will need **a lot of space for this**, about 40GB or so depending on the emulators you installed. You don't need this to simply code in Swift, but you need this to build iOS apps (or any other Apple apps really). If you're good, you can use the [command line package](https://developer.apple.com/library/archive/technotes/tn2339/_index.html). However this is not recommended if you want a *fuss-free, beginner friendly experience*. ## Download materials and test Once you're done, please download the project **[here](https://www.dropbox.com/s/5cqt5mxdm4lm8vg/CandySearch-Week1.zip?dl=1)** and open it. Don't worry if you don't really understand how things work for now. You will have **several errors** and that is normal. Our goal is to create an app which list out several Candies, grouped by different `categories`. We will use today's lesson to "fix" these errors and learn a few things about Swift along the way. ## Constants, Variables, and Datatypes ### Constants and Variables Like any other programming language, variables and constants are used to hold pieces of *information*. The difference between the two: * Variables can **change** over time (mutable), * Constants must **stay** the **same** (immutable). Constants are declared with let: ```swift= let greeting = "Hello" let numberOfToys = 8 let isMorning = true ``` Variables are declared with `var`: ```swift= var category = 'Chocolate' var name = 'Chocolate Bar' ``` Variable declarations can include **type** annotations for clarity as below: ```swift= var category : String = 'Chocolate' var name : String = 'Chocolate Bar' ``` Variables that belong to a struct, class, or enum are called **properties**. It makes sense that we have many *properties* in an object. Take for example a `Candy` in real life, it has several properties such as: * Category of the candy: chocolate? hard or chewy? sour? luxurious? * Name of the candy * Price of the candy How do we **translate** this in terms of programming instructions? We can define a *structure* for an object with the `class` keyword. Create a new Swift file and name it `Candy.swift`: ![](https://i.imgur.com/ciuXI1i.png) Paste the following code in it: ``` swift= class Candy { var category : String var name : String // initialiser init(category: String, name: String) { self.category = category self.name = name } } ``` The above instruction declare a simple structure for the object `Candy`, having only two **properties** (variable) called `category` and `name`. Below it we have the **initialiser**. You need to **always** write an initializer **for each** property in the class. Save the project and attempt to run it with iPad simulator. Your project should compile and run now, but with **empty** list of candies: ![](https://i.imgur.com/Obc5FlD.png) ### Various Datatypes That is okay, we will add them up later. Now let's try to declare various other datatypes as `Candy` properties. There are **five** basic data types: * Numbers Int, Float * Booleans Bool * Strings * Enums We can add more properties to `Candy`: ```swift= var price : Int var inStock : Bool ``` Don't forget to edit the initialiser too: ```swift= // initialiser init(category: String, name: String, price: Int, inStock: Bool) { self.category = category self.name = name self.price = price self.inStock = inStock } ``` The keyword `self.` is just a way to refer to this object's property and not the **input** argument, since they're named the same way. As mentioned before, variables are **mutable**, and therefore we can **assign** values into them via this `init` function. ### Test To test easily, create a **new project** under macOS Command Line Tool. Then **copy** the code for `Candy.swift` there as a new file. After that, go to `main.swift` and paste the following: ```swift= import Foundation print("Hello, World!") var chocolateBar:Candy = Candy(category: "Chocolate", name: "Chocolate Bar", price: 1, inStock: true) print("The name of the candy is: " + chocolateBar.name + " which costs: " + chocolateBar.price.description + " dollars.") chocolateBar = Candy(category: "Chocolate", name: "Milk Chocolate Bar", price: 1, inStock: true) print("The name of the candy is: " + chocolateBar.name + " which costs: " + chocolateBar.price.description + " dollars.") ``` ![](https://i.imgur.com/AVD0aLf.png) Notice how the content of `chocolateBar` can be changed, as we change the `name` into `Milk Chocolate Bar` on line 7 above. If we were to declare `chocolateBar` as a `constant` instead with the `let` keyword, we will be met with such error: ![](https://i.imgur.com/3Wie0zc.png) ### Enums Enums (short for enumerations) define a type-safe **common** type for a group of **related** values. For instance, **candy category** can be created with an `enum` instead of writing them out as string one by one. Go to `Candy.swift` and add the following code: ```swift= enum candyCategory: String{ case Chocolate = "Chocolate" case Hard = "Hard" case Other = "Other" } ``` Here we created three types of candyCategory. We can use it as follows: ```swift= candyCategory.Chocolate.rawValue // returns "Chocolate" candyCategory.Other.rawValue // returns "Other" ... ``` **Test** its output: ```swift= var chocolateBar:Candy = Candy(category: candyCategory.Chocolate.rawValue, name: "Chocolate Bar", price: 1, inStock: true) print("The name of the candy is: " + chocolateBar.name + " which costs: " + chocolateBar.price.description + " dollars.") ``` ## Collection types There are **three** primary collection types in Swift. They're distinct from the **basic datatypes** mentioned above because a collection data type is a **complex** type that is made up of **one or more elements**, all of the **same** data type. * Arrays, e.g: [1, 2, 3] * Sets, e.g: Set<Int> * Dictionaries, e.g: ["Ava": 150, "Eve": 275] ### Arrays Arrays store values of a certain type in an **ordered** list. Suppose we want to create a few Candies. We are effectively creating a *collection* of candies. The most basic way is to create an **Array** of Candies: ```swift= var candyArray:[Candy] = [Candy(category: candyCategory.Chocolate.rawValue, name: "Hershey", price: 1, inStock: true), Candy(category: candyCategory.Chocolate.rawValue, name: "Lindt", price: 3, inStock: true), Candy(category: candyCategory.Other.rawValue, name: "KitKat", price: 2, inStock: false) ``` You can **access** each **element** in the array by indexing themm e.g: `candyArray[0]` returns the first element (first candy). After that, you can use it as per normal, e.g `.name` to print its name: ```swift= print("First candy: " + candyArray[0].name + ", Second candy: " + candyArray[1].name + ", Third candy: " + candyArray[2].name) ``` You can **add** elements into it using the `append` method: ```swift= candyArray.append(Candy(category: candyCategory.Hard.rawValue, name: "Chocolate Lollipop", price: 2, inStock: false)) ``` You can **remove** elements in arrays using `remove` method: ```swift= var removedCandy:Candy = candyArray.remove(2) // remove the third candy in the list ``` ### Test with Breakpoint Instead of **repeatedly** printing to **inspect** the content of `candyArray` after you modify with `remove` or `append`, you can use a **breakpoint** instead. Click on the line numbers in Xcode as such in the lines you want it to "pause". For instance, here's the content of `candyArray` before we append anything. ![](https://i.imgur.com/3RIn6OY.png) Then click "next" in the debug area to go to the next breakpoint. Now we can see that there are 4 items in the array because the breakpoint stops the execution **after** the `append` instruction is executed: ![](https://i.imgur.com/fIOvb8r.png) We can click another "next" and finally we see that the content of `removedCandy` is now filled with `KitKat` (the candy we removed), and the elements in `candyArray` is back to 3: ![](https://i.imgur.com/tbZdyFa.png) ### Sets A set stores **distinct** values of the **same** type in a collection with **no** defined ordering. You can use a set instead of an array when the order of items isn’t important, or when you need to ensure that an item only appears once. This is **different** from Arrays because items in an arrays are ordered, meaning that they are associated with an *index* and will always be present in that index until we change it. You can create a set pretty much via a similar way like creating an array: ```swift= var candyNameSet: Set<String> = ["Chocolate","Chewy", "Hard", "Other"] ``` A type **must be hashable** in order to be stored in a set. All of Swift’s basic types (such as String, Int, Double, and Bool) are hashable by default, and can be used as set value types or dictionary key types. Custom classes and non-default `enums` however, are **not hashable**. > A hash value is an Int value that’s the same for all objects that compare equally, such that if a == b, the hash value of a is equal to the hash value of b. Try declaring a set of `Candy`, and you'll meet the error: ![](https://i.imgur.com/h4zwlFD.png) So what is Set good for? 1. It is handy to check if it *contains* something: ```swift= if candyNameSet.contains("Chocolate") { print("That's great!") } else { print("Please go get some chocolates.") } ``` 2. Perform **fundamental** set operations: * Combining two sets together, * Determining which values two sets have in common, * Determining whether two sets contain all, some, or none of the same values. ```swift= let oddDigits: Set = [1, 3, 5, 7, 9] let evenDigits: Set = [0, 2, 4, 6, 8] let singleDigitPrimeNumbers: Set = [2, 3, 5, 7] oddDigits.union(evenDigits).sorted() // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] oddDigits.intersection(evenDigits).sorted() // [] oddDigits.subtracting(singleDigitPrimeNumbers).sorted() // [1, 9] oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted() // [1, 2, 9] ``` 3. Determining set **membership** and **equality** ```swift= let houseAnimals: Set = ["🐶", "🐱"] let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"] let cityAnimals: Set = ["🐦", "🐭"] houseAnimals.isSubset(of: farmAnimals) // true farmAnimals.isSuperset(of: houseAnimals) // true farmAnimals.isDisjoint(with: cityAnimals) // true ``` ### Dictionaries Dictionaries store **associations** between **unique** **keys** of the **same** type (must be **hashable**) and **values** of the **same** type in a collection with **no defined ordering**. This is particularly handy if we have aliases in our array. For instance, it is not natural to keep referring to a specific candy as `candyArray[i]`. We can use a Dictionary instead: ```swift= var candyDictionary:[String:Candy] = ["Hershey":Candy(category: candyCategory.Chocolate.rawValue, name: "Hershey", price: 1, inStock: true), "Lindt":Candy(category: candyCategory.Chocolate.rawValue, name: "Lindt", price: 3, inStock: true), "Toblerone":Candy(category: candyCategory.Chocolate.rawValue, name: "Toblerone", price: 2, inStock: false), "KitKat":Candy(category: candyCategory.Other.rawValue, name: "KitKat", price: 2, inStock: false), "Milo Bites":Candy(category: candyCategory.Other.rawValue, name: "Milo Bites", price: 5, inStock: true)] ``` To get the `KitKat` for example, we simply use the Dictionary as `dictionaryInstance[Key]` and this will return you the associated `value` **if it exists**: ```swift= print(candyDictionary["KitKat"]?.inStock ?? false) ``` The symbol `?` is called an `optional`. We will learn more about this later, but the instruction above simply tells the computer to print `false` if `candyDictionary` **does not contain "KitKat"**. You should definitely read more about these collection types [here](https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html#). ## Apply in CandySearch Project Remember that our goal, is to instantiate many `Candy` object instances, and then tell the app to render it as a list of Candies. Paste `Candy.swift` from our test project into the CandySearch project. You should have the declaration of the `Candy` class with `category, name, price, inStock` properties, as well as the `enum candyCategory`. Open `MasterViewController.swift` and let's fill the `array` `candies` wil values: ```swift= candies = [ Candy(category:candyCategory.Chocolate.rawValue, name:"Chocolate Bar", price:1, inStock: true), Candy(category:candyCategory.Chocolate.rawValue, name:"Chocolate Chip", price:1, inStock: true), Candy(category:candyCategory.Chocolate.rawValue, name:"Dark Chocolate", price:1, inStock: true), Candy(category:candyCategory.Hard.rawValue, name:"Lollipop", price:1, inStock: true), Candy(category:candyCategory.Hard.rawValue, name:"Candy Cane", price:1, inStock: true), Candy(category:candyCategory.Hard.rawValue, name:"Jaw Breaker", price:1, inStock: true), Candy(category:candyCategory.Other.rawValue, name:"Caramel", price:1, inStock: true), Candy(category:candyCategory.Other.rawValue, name:"Sour Chew", price:1, inStock: true), Candy(category:candyCategory.Other.rawValue, name:"Gummi Bear", price:1, inStock: true) ] ``` Run the App and you should see now that the list of candies are populated (press "back" to master first). ## Using Storyboard (Introduction) Suppose what we want is to click on any candy on the left, and then observe that its name and category is printed on the area on the right as follows: ![](https://i.imgur.com/3lNK3If.png) To do that, add `ImageView` into DetailScene (Under View >> StackView). You can think of the storyboard like a graphical way of illustrating where things are placed in your app: ![](https://i.imgur.com/EU6LwFn.jpg) Remember to put Image View as a child of Stack View, same level as Label. Now click on Editor tab and open "Assistant". This will pull out the script that controls the Scene (`DetailViewController.swift`). ### Outlets What we want now is to dictate the content of the Image View and Description label to match the **current candy** being selected. How do we know the *candy* that is currently selected? We set it from the previous Scene, which is the `MasterViewController`. Open `MasterViewController.swift`, and take a look of the following instructions: ```swift= // MARK: - Segues override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showDetail" { if let indexPath = tableView.indexPathForSelectedRow { let candy: Candy if searchController.isActive { candy = filteredCandies[(indexPath as NSIndexPath).row] } else { candy = candies[(indexPath as NSIndexPath).row] } // preparing the controller as the destination let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController controller.detailCandy = candy controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem controller.navigationItem.leftItemsSupplementBackButton = true } } } ``` This function is essentially called when there's a transition between one scene to another, called **Segues**. > A segue defines a **transition** between two view controllers in your app’s storyboard file. The **starting** point of a segue is the button, table row, or gesture recognizer that **initiates** the segue. The end point of a segue is the **view controller** you want to display. A segue always presents a new view controller, but you can also use an unwind segue to dismiss a view controller. Therefore we set the property `detailCandy` in `DetailViewController` as the current selected `candy` whenever the segue **showDetail** is trigggered. This passes the data about the current `candy` to `DetailViewController`. Now what is left is to pass the data to the respective Label and ImageView. In the storyboard, ctrl + drag Image View you just created to create an **outlet**: ![](https://i.imgur.com/sZKBJ1C.png) Do the same for the Detail Description Label: ![](https://i.imgur.com/fgOXtvR.png) This creates a variable as follows that serve as a reference for these two objects in the scene. ```swift= @IBOutlet weak var detailDescriptionLabel: UILabel! @IBOutlet weak var candyImageView: UIImageView! ``` Finally, we need to configure the value of these two objects when `detailCandy` is set by the MasterViewController. Paste the following code inside `configureView()` function in `DetailViewController.swift` and study it: ```swift= if let detailCandy = detailCandy { if let detailDescriptionLabel = detailDescriptionLabel, let candyImageView = candyImageView { detailDescriptionLabel.text = detailCandy.name candyImageView.image = UIImage(named: detailCandy.name) title = detailCandy.category } } ``` The statement `if let detailCandy...` **safely unwraps** the **optional** property `detailCandy`. Compile and you shall see DetailViewController ImageView and Label object populated once any candy is selected. ## Optional Variables In Swift, optionals can take a value of nil or have a proper value. We need to **unwrap** them to use it properly. In the above example, `detailCandy` is an **optional**. If not `nil`, it takes a reference to `Candy`. ### Why do we need optionals? Take an example of **filling up** forms: sometimes user just choose not to populate some fields. If a variable contains no value, it is nil, regardless of the type of the variable, and provided it’s declared as an optional. It doesn’t matter whether it’s a string, an integer, a view controller, a button, an image or an object – if it’s declared as an optional, it can be nil. ### Creating Optionals Try out this code: ```swift= let name:String? = "KitKat" print(name) ``` What is the output? Youn will see something like `Optional("KitKat")`. You can also declare: ```swift= let name:String? = nil print(name) ``` This will print out `nil`. Try out this code to study more about optionals: ```swift= class Vehicle { var wheels:Int = 4 var name:String? } let car = Vehicle() print(car.wheels) print(car.name) car.name = "Maserati" print(car.name) ``` * **wheels** is not an optional, so it **must have a value** (we have seen before that we need to initialise it when coding `Candy`.) * **name** is an optional, so it can be `nil`. ### Unwrapping Optionals You need to **unwrap** optionals to safely use them. You can unwrap optionals in 4 different ways: * With **force** unwrapping, using `!` * With optional **binding**, using `if let` * With **implicitly** unwrapped optionals, using !`` * With optional **chaining**, using `a?.b?.c` ### Force unwrapping ```swift= let name:String? = "KitKat" if name != nil { print(name!) } ``` If you **dont check** that name is `nil` before printing with `force unwrapping`, then you will be met with **fatal error** if `name` is `nil`. ### Optional Binding ```swift= let name:String? = "KitKat" if let candyName = name { print(candyName) } ``` *Optional binding* assigns the **value** of `name` to the constant `candyName` if it’s not `nil`. (Important!) You can think about optional binding as a **unwrapping** an optional by using a **conditional** and a **constant**. * When the conditional evaluates to `true`, (optional is not nil) the optional's value is assigned to the constant. * Vice versa, if `false`, then the conditional body is **not executed**. You can also use **multiple optional bindings**: ```swift= if let candyname = candynameField.text, let candytype = candytypeField.text, let candyprice = candypriceField.text { // Do something ... } ``` You can use optional binding for any optional value, so also for a function that returns an **optional** like the function `Int()`: ```swift= if let score = Int("42") { print(score) } ``` ### Optional Chaining It is tempted to check multiple optionals as such, where `.weight` is a property of `candy` and they're both optionals: ```swift= if let candy = candyField, let weight = candy.weightText { // do something } ``` Imagine what happens when you have 10 UI components in your view controller, with optional properties, that you access in several parts of your code. It’ll quickly become a big mess of conditionals, squiggly brackets and indentations. This is called the *Pyramid of Doom*. We can use **optional chaining** as such: ```swift= candyField?.weightText?.value = "150" ``` At this point, one of two things happens: * The variable `candyField` is **not** nil, and the call to property `weightText` **succeeds** * The variable usernameField is **nil**, and the call to property `weightText` **fails** * Same logic to `.value` In the above example, the value property of `weightText` isn’t changed when candyField is nil or weightText is nil. You can also combine optional chaining with optional binding. Like this: ```swift= if let candyWeightValue = candyField?.weightText?.value { // do something } ``` ### Implicitly Unwrapped Optional Due to the interest of time, you can implicitly unwrap an optional when declaring a variable: ```swift= @IBOutlet var candyField:UITextField! ``` You can read more about it [here](https://cocoacasts.com/when-should-you-use-implicitly-unwrapped-optionals). ### Summary In short, when dealing with an **optional**, always **safely unwrap them**: ```swift= candyField!.text = "KitKat" // Unsafe candyField?.text = "KitKat" // Safer (but not bug-free) ``` Here's a [good article](https://medium.com/@avg5074/unwrapping-optionals-in-swift-the-right-way-f8d826a67655) to enhance your knowledge about **optionals**. # Summary This short Swift workshop is meant to introduce you to the very basics of Swifts, and a glimpse of how to build an iOS App using the *older* method: storyboards and view controller. It will be useful if you reverse engineer how this simple app works, given that it only has a few views. In the next session, we will learn more advanced programming techniques in Swift such as **control flow** and OOP.