# TMCF The Pitch 2021 iOS App Pre-Work ### Overview Today, we will be making a Marvel character wikipedia app using the [Marvel API](https://developer.marvel.com/). If you have never heard of an API, don't sweat it. We will cover that in the process of developing this app! For now, all you need to know is that we will be using it to get informational data about all of your favorite Marvel characters. That way we can build an app that puts the characters in a scrollable list view! ![Image of Marvel Characters](https://images.sellbrite.com/production/152272/P5297/155fa446-e7e6-5f03-8e3b-46bacfe6f32f.jpg =700x350) ### Core Functionality to Implement - [ ] User can see a list of Marvel characters on their home screen. - [ ] User can tap on a each Marvel character to view details about them. ### iOS Topics Covered in this guide 1. Creating an Xcode project 2. Familiarizing yourself with Xcode 3. Model ViewController architecture 4. Creating network requests and pulling data from the internet 5. Transitioning between screens and passing data from one screen to another 6. Creating scrolling lists (Table Views) 7. AutoLayout :::success 💡 **After completing this tutorial, you will know the basics of App Development and begin building your own iOS apps!** ::: :::spoiler **Additional Resources to supplement this tutorial** * [Xcode tutorial for beinners](https://codewithchris.com/xcode-tutorial/) * [Passing Data using Segues - Tutorial]() * [Making Network Requests in Swift - Tutorial]() * [What are APIs?]() * [Collection Views - Tutorial](https://www.raywenderlich.com/18895088-uicollectionview-tutorial-getting-started) * [Table Views - Apple Docs]() * [SearchBar and UISearchController - Tutorial]() * [TabBarControllers and NavigationController - Tutorial](https://fulmanski.pl/tutorials/apple/ios/navigation-controller-and-tab-bar-controller-swift/) * [AutoLayout - Tutorial](https://www.raywenderlich.com/811496-auto-layout-tutorial-in-ios-getting-started) * [Model View Controller - Tutorial](https://www.raywenderlich.com/1000705-model-view-controller-mvc-in-ios-a-modern-approach) ::: ## Milestone 1: Setting Up Our Operation Marvel App :::danger :warning: Before you start, know that Xcode only works on the MacOS. If you don’t have access to a Mac, there are cloud solutions such as [MacInCloud](https://www.macincloud.com/). However that isn't covered in this tutorial. ::: ### 1. Create Xcode Project Open up Xcode and select the option “Create a new Xcode project” ![Xcode Opening Image](https://i.imgur.com/2OVzl71.png) Select “App” and click “Next” ![Xcode Select App Image](https://i.imgur.com/NJYK7hw.png) 1. Name your project `OperationMarvel` 2. Format your Organization Identifier to start with `com.your_name` 3. Next make sure Interface is set to **Storyboard** 4. Also ake sure to select your Language to be **Swift** :::warning :warning:Team should be set to “None” if it is not automatically with your name ::: ![Xcode App Settings Image](https://i.imgur.com/Okt6Sfl.png) After double checking the above settings, click “Next” and choose a location to save your project. Below is the resulting screen. ![](https://i.imgur.com/ikzhZKC.png) :::success :confetti_ball: Congratulations, You just finished setting up your first Xcode project! ::: :::warning :bulb: For more indepth tutorials on Xcode and its functionality, check out [Apple's SwiftUI Tutorials](https://developer.apple.com/tutorials/SwiftUI). ::: ### 2. Organize your files with Model ViewController (MVC) Architecture 1. Select your `ViewController.swift` file and press **Command + Delete** on your Mac. We will be creating our own files from scratch! 2. A pop up will appear asking if you want to remove the reference or place the file in the trash. Select the button to **place your file in the trash**. 3. Right-click on the **OperationMarvel** folder at the top of our heirarchy and select **New Group**. ![](https://i.imgur.com/FmzZIPD.png) 4. Add 3 new groups: **Model**, **View**, and **Controller**. Your folder structure should look like the following image. ![](https://i.imgur.com/NbhwhOR.png) :::success :confetti_ball: Great work, we officially set up an MVC architecture in our app! ::: :::warning :bulb: Why do we set this up? MVC is a way of organize your code. When building an app, there are so many layers of code involved as you will soon see. If all of your code was in one massive ViewController file, it would be hard to improve and debug your app. It would get incredibly confusing quick. In order to organize everything that goes on, we only let certain files perform certain tasks. * The **Model** is where your data is defined. In the model you define what your model objects in your project are (i.e. What is a character object defined to be in your app?) This is also where you make your networking classes, managers and parsers. * The **View** handles UI objects such as UIView or UILabel. Pretty much any time you have a custom UI class, you will define its properties and methods in the View layer. * The **Controller** handles the logic between the Model and the View. To learn more about MVC, read [this article](https://www.raywenderlich.com/1000705-model-view-controller-mvc-in-ios-a-modern-approach)! ::: ### 3. Creating your first ViewController 1. Right click on your **Controller** folder and select New File…. 2. Select **Cocoa Touch Class** then click Next. 3. Make sure you are subclassing **UIViewController** 4. Name your class `OperationMarvelViewController` 5. **Uncheck** “Also create XIB file” 6. Make sure your language is set to **Swift** 7. A drop down will appear, **leave the location as is** and just click on **Create** ![](https://i.imgur.com/uhUBeCZ.png) ![](https://i.imgur.com/0ttOHN8.png) :::success :confetti_ball: Good job! You should now see your first custom ViewController file! ::: ## Milestone 2: User can scroll through and access information about their Favorite Marvel Characters ### 1. Create a Network Request to get Marvel Character data :::warning :bulb: This sounds more complicated than it is, so let's just take it step by step. First, we are going to start by using the following code to make a GET request from the Marvel API. ::: 1. Copy and paste the block of code below into your `viewDidLoad()` method. ``` let url = URL(string: "https://gateway.marvel.com:443/v1/public/characters?limit=100&ts=1&apikey=f8a56187bab6d605c8c0818ec49a3238&hash=8d3b8da75efd5b50cd7020c938e7139a")! let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 10) let session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue.main) let task = session.dataTask(with: request) { (data, response, error) in // This will run when the network request returns if let error = error { print(error.localizedDescription) } else if let data = data { /* * Marvel API is structured different than other API's, looking at results after getting the Marvel * characters, we can see that information we want is embedded into a "data" key. We will use the * "data" key to extract the info we want. */ let dataDictionary = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] let data = dataDictionary["data"]! as! [String: Any] let characterDictionaries = data["results"] as! [[String: Any]] print(characterDictionaries) // TODO: Get the array of marvel characters // TODO: Store the characters in a property to use elsewhere // TODO: Reload your Table View data } } task.resume() ``` * `viewDidLoad()` is the method that gets called once your screen first loads up, simple right? * Hold the option key and click on the method name, a pop-up will show up with more information regarding this method * Every view controller has a **lifecycle**, essentially, everytime a view controller loads up, appears, or disappears from the main view, a certain set of methods will get called! The first time your app loads a view, the `viewDidLoad()` method gets called, so this is typically where you want to configure your properties. :::info To learn more about the ViewController lifecycle read the [official apple documentation](https://developer.apple.com/tutorials/SwiftUI) on them! ::: 2. Select the **iPhone 11 simulator** at the top of your Xcode window. ![](https://i.imgur.com/DR8YVJ4.png) ![](https://i.imgur.com/jFn2jg4.png) 3. Click on the **Play button located at the top left** of your Xcode window. This will build and run your app using the iphone simulator you just selected! ![](https://i.imgur.com/9A4HGhR.png) :::danger :warning: Oh no! Nothing happened, and your console (area where green square is) shows this weird cryptic message: "Unkown class in Interface Builder file." ::: This is happening because we never told our storyboard (the interface builder file) to use our custom `OperationMarvelViewController` on the ViewController that gets loaded upon first running the app. Let’s fix this! ![](https://i.imgur.com/7xqapTq.png) * Click on **Main.storyboard** * Select the ViewController object by tapping on either areas where there is a green circle in the image above * Select the **Identity Inspector** on the right panel, it’s the button that looks like a driver’s license (pink square in image above) * Type `OperationMarvelViewController` as your custom Class to inform our project that our selected ViewController object on the storyboard should implement the code in our `OperationMarvelViewController.swift` file * Run your app again and pull up the console. We should see a list of marvel characters listed in alphabetical order! ![](https://i.imgur.com/EZUV3t0.png) :::success :confetti_ball: Congratulations! You just created your first network request and pulled data from the Marvel API! ::: :::warning :bulb: What is an API? API stands for Application Programming Interface. The most basic way to describe it is a set of URLs, which serve as paths to access data from different servers. Some API’s require a user’s permissions to access data, which can be thought of us as a locked door. For example, if we wanted to use the Twitter API and pull tweets that we have posted, we would need to acquire an API key that authenticates us and gives our iOS app permission to pull our data. The key opens the door. The Marvel API utlizes API keys. We use an public and private API key to get access to the URL paths to the character information data. ::: ### 2. Setup a Table View to display our Marvel Characters in a scrolling list #### 1. Initializing the Table View 1. Go back to your storyboard and tap on **the !["+"](https://i.imgur.com/Gwmf7Xj.png =20x20) button** on the top right of the screen. 2. A pop up will appear, begin typing `Table View` and select the Table View object highlighted blue in the image below. ![](https://i.imgur.com/399udFl.png) 3. Expand your **Table View** to fit the size of the View Controller. 4. Make sure your Table View is selected and click on **the ![AutoLayout](https://i.imgur.com/AlWsvzs.png =20x20) button** on the bottom right of the Storyboard screen. :::warning :bulb: This is how we add AutoLayout constraints to ensure the Table View will always fit the size of any iPhone screen regardless of what size it is. (iPhone 6 vs iPhone 11) ::: 6. **Set the constraints to be 0** on the left, top, right, and bottom to make it always completely hug the view controller as seen below. 7. Then press **the "Add 4 Constraints" button**. ![](https://i.imgur.com/4TUfgIX.png) 6. Click on **the !["+"](https://i.imgur.com/Gwmf7Xj.png =20x20) button** again. This time type in and select `Table View Cell`. ![](https://i.imgur.com/bP7BfzE.png) 7. Grab the `Table View Cell` and drop it on top of your Table View, make sure your **view heirarchy** looks like the one shown on the lefthand side of the image below. ![](https://i.imgur.com/SBVLopp.png) 8. Go back to your `OperationMarvelViewController.swift` file and right next to `class OperationMarvelViewController: UIViewController` at the top of the file add the following `, UITableViewDelegate, UITableViewDataSource`. 9. An error will show up indicating you are not properly conforming to the protocols, click on **the Fix button** on the bottom right of the error. You should see 2 new methods appear. ![](https://i.imgur.com/na6rWwu.png) 10. In order for our Table View to work, we need to subclass `UITableViewDelegate` and `UITableViewDataSource` and implement the below methods in order to tell our Table View that **OperationMarvelViewController** can behave like a scrolling list. ![](https://i.imgur.com/LMcnu2B.png) 11. Go back to your `Main.storyboard` file. Make sure your ViewController object is selected and click on **the ![Lines](https://i.imgur.com/5ZhvBeF.png =20x20) button**. Then select Assistant from the dropdown menu as seen in the image bellow. 12. You should see your storyboard next to your `OperationMarvelViewController.swift` file's code. ![](https://i.imgur.com/Ayii11O.png) 13. Hold the **Control key**, and click on the **Table View** object from the storyboard, then drag your cursor over to your code right above `viewDidLoad()` method. ![](https://i.imgur.com/jgx3C0b.png) 14. Once you drop your object on the code, a pop up will show up asking you to name your object. Type in `tableView`. Press the "Connect" button. 15. You should see a new variable defined in your code! You just made what is known as an outlet :::warning :bulb: An Outlet is how you connect your storyboard objects to your code. ::: ![](https://i.imgur.com/svUgvDv.png) :::warning :warning: Before we can continue to configuring our Table View, we need to define our Character objects in our app! ::: #### 2. Creating the Marvel Character objects 1. Right click on your **Model** folder and select **New File…**. 2. This time, instead of Cocoa Touch Class, choose **Swift File**. 3. Name your file **Character** and click on **create**. 4. Finally, copy the code below into your newly created `Character.swift` file. ``` class Character { var name: String var description: String init(dictionary: [String: Any]) { name = dictionary["name"] as? String ?? "No Name" description = dictionary["description"] as? String ?? "No Description" } class func characterArray(dictionaries: [[String: Any]]) -> [Character] { var characterArray: [Character] = [] for dictionary in dictionaries { let character = Character(dictionary: dictionary) characterArray.append(character) } return characterArray } } ``` We have just defined what a Character object is in our project based on the data that is returned from the Marvel API! :::warning :bulb:The function `characterArray(dictionaries:_)` takes in an array of dictionaries (json objects), uses each dictionary to initialize a new Character object, and finally returns a new array of Character objects ::: #### 3. Getting network Requests from the Marvel API :::warning :warning: After setting up the character object, we are going to need to add a new model to manage all the networking calls. ::: 1. Right click on your **Model** folder and select **New File…**. 2. Choose **Swift File** again. 3. This time name your file `CharacterAPIManager` and click on **create**. 4. Finally, copy the code below into your newly created `CharacterAPIManager.swift` file. ``` class CharacterAPIManager { static let apiURL = "https://gateway.marvel.com:443/v1/public/characters?limit=100&ts=1&apikey=f8a56187bab6d605c8c0818ec49a3238&hash=8d3b8da75efd5b50cd7020c938e7139a" var session: URLSession init() { session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue.main) } func fetchAllCharacters(completion: @escaping ([Character]?, Error?) -> ()) { let url = URL(string: CharacterAPIManager.apiURL)! let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 10) let task = session.dataTask(with: request) { (data, response, error) in // This will run when the network request returns if let data = data { let dataDictionary = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] /* Marvel API is structured different than other API's, looking at results after getting the Marvel characters, we can see that information we want is embedded into a "data" key. We will use the "data" key to ecxtract the info we want. */ let data = dataDictionary["data"]! as! [String: Any] let characterDictionaries = data["results"] as! [[String: Any]] let characterArray = Character.characterArray(dictionaries: characterDictionaries) completion(characterArray, nil) } else { completion(nil, error) } } task.resume() } } ``` Go back to your `MarvelCharacterViewController.swift` file and delete ALMOST all the code in your `viewDidLoad()` function. (See warning below.) :::danger :warning: Do NOT delete the line `super.viewDidLoad()` ::: Add a new variable at the top of your file, right below your `tableView` outlet and call it **characterArray**. ``` var characterArray: [Character] = [] ``` Create a new function below `viewDidLoad()` and call it `fetchCharacter()` then insert the following code inside it: ``` // This syntax for calling a method is known as a completion handler, if you have done web dev, it's basically an asynchronous function call CharacterAPIManager().fetchAllCharacters { (characterArray: [Character]?, error: Error?) in if let characterArray = characterArray { // populate our global characterArray property with the array returned in our custom networking function self.characterArray = characterArray // access each marvel character object in the array and print it's properties out into our console for character in characterArray { print("\(character.name)\n") } } } ``` Finally, call `fetchCharacter()` **inside** your `viewDidLoad()` function :::success :confetti_ball: Now we can access our Marvel Character properties using our defined Character model! Since our networking code was moved into a new class, our `MarvelCharacterViewController.swift` file looks really clean making it easier to maintain and debug! ::: Inside your `viewDidLoad()` function, **before you call `fetchCharacter()`**, add the following code: ``` tableView.delegate = self tableView.dataSource = self ``` Inside `fetchCharacter()`, **after your for loop**, add the following: ``` self.tableView.reloadData() ``` Finally, inside `func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { }` add the following: ``` return characterArray.count ``` This tells our Table View to display as many rows in our list as there is objects in our array. #### 4. Creating Marvel Character Cells :::warning :warning: Almost there! Before we can implement the `tableView(cellForRowAt:_)` method we need to create a custom cell. ::: 1. Right click on the **View** folder, select **New File…**. 2. This time choose **Cocoa Touch Class**. 3. In the drop down text field, type in **UITableViewCell**. 4. Name your new file `CharacterCell` and click on create. 5. Go back to your **storyboard**, click on **Table View Cell**, go to the **Identity inspector** on the right panel (**the ![Identity Inspector](https://i.imgur.com/0GXlqn8.png =20x20) button**), and, for **custom class**, type in `CharacterCell`. ![](https://i.imgur.com/nSJE4N8.png) 5. Finally, click on **the ![Dial Looking](https://i.imgur.com/q6xzj51.png) button**, directly to the right of the Identity Inspector, and for **Identifier** type in `CharacterCell`. ![](https://i.imgur.com/rNVUk6I.png) 6. Click on **the !["+"](https://i.imgur.com/Gwmf7Xj.png =20x20) button** to add a new object into your storyboard. 7. Select the `Label` object and drag it into the middle of your **Table View Cell**. 8. Click on your cell, select **the ![Triangle ruler looking](https://i.imgur.com/vguY2lr.png =20x20) button**, and change the **row height** to be 80. ![](https://i.imgur.com/yCGXdmA.png) 9. Make sure you center your **Label** by clicking on **the ![Dial Looking](https://i.imgur.com/q6xzj51.png) button** and setting its **Alignment** to the option shown below. ![](https://i.imgur.com/Z7ZYnnM.png) 11. Then add **left and right constraints of 8** and the top and bottom constraints of whatever default numbers show up. ![](https://i.imgur.com/g1S4tKZ.png) 12. Open up the **Assistant Editor** again so that we can make **outlets** from our label to our `CharacterCell.swift` file. 13. Drag the **Label** from your storyboard to your code and name it `nameLabel`. ![](https://i.imgur.com/GmzHouP.png) Your `CharacterCell.swift` file should finally look like this: ![](https://i.imgur.com/akmGpoL.png) Your `OperationMarvelViewController.swift` file should look like this: ![](https://i.imgur.com/rXWpDVa.jpg) ![](https://i.imgur.com/4ICSEuZ.jpg) :::success Build and run your app using the play button at the top left and you should see a list of all the Marvel Characters in alphabetical order! ::: ![](https://i.imgur.com/a3KLe28.png) ## Milestone 3: User can tap on a cell to see the data in a separate view controller Right click on the **Controller** folder and add a new Cocoa Touch Class file of type **UIViewController** and name it `DetailViewController`. Go back to your **Main.storboard** and click on **the !["+"](https://i.imgur.com/Gwmf7Xj.png =20x20) button**, then type `UIViewController` to add a new ViewController into the storyboard. ![](https://i.imgur.com/bHkyb6j.jpg) Make the new ViewController to be of type **DetailViewController** by selecting the identity inspector on the right panel and typing in the custom class ![](https://i.imgur.com/HSTNQ03.png) We are now going to add a **label** to the **DetailViewController**. Go ahead and change the **font size to 30** and **center the text**. Set the **constraints to be 8 on the left and right**, set the **constraints for the top and bottom to the value that shows up**. ![](https://i.imgur.com/TQXepGg.png) Hold the control key and click on the **CharacterCell** in the **OperationMarvelViewController** and drag the cursor to the **DetailViewController** to create a segue. You will be given a list of options, make sure to select **Present Modally**. By doing so, we will present the **DetailViewController** modally everytime we select one of our cells. :::warning :bulb: A segue is the transition between View Controllers. Presenting the segue modally lets the second view controller take up the entire screen instead of being just a pop-up. Learn more about Segueing [here](https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html). ::: ![](https://i.imgur.com/9PLlODG.png) Once the **segue** has been created, your **View Controllers** should look like this. ![](https://i.imgur.com/p1D5oDk.png) Make sure your **DetailViewController** is selected and open up the assistant editor again to display our DetailViewController.swift file next to our storyboard * Once the assistant editor shows up create an outlet from the label to the code and name your outlet **nameLabel** ![](https://i.imgur.com/UL03NRW.png) Below your **nameLabel** outlet add the following property in your `DetailViewController.swift` file: ``` var character: Character? ``` :::warning 💡 Why do we add a “?” * In Swift, we have what are known as Optionals, they’re just like any other variable except we are not sure if they will contain data or be nil * Since sometimes when we pull data, it can have null values, we use optionals as a way to prevent nil errors * We unwrap optionals by adding an “!” if we are positive data will be returned * We can also unwrap optionals by doing an if let variable = variable { } check which is a more conditional type-safe way to check for null values as shown below ::: Inside `viewDidLoad()` method add the following code: ``` if let character = character { nameLabel.text = character.name } ``` Your **DetailViewController** should look like this: ![](https://i.imgur.com/ljtR24l.png) Just like we did before, we are going to add another label to our **DetailViewController**. Go ahead and add this label underneath the nameLabel. Set the constraints for the left, right, and bottom to be 8, set the top constraint to the value that is already there. ![](https://i.imgur.com/k5PSyQv.png) Make sure to set the number of lines to 0. By setting it to 0 we allow the label to disply multi-line text. If we were to set the number of lines to 2 then the label would only have 2 lines of text. ![](https://i.imgur.com/xtlUBjW.png) Make sure your DetailViewController is selected and open up the assistant editor again to display our DetailViewController.swift file next to our storyboard * Once the assistant editor shows up create an outlet from the label to the code and name your outlet descriptionLabel ![](https://i.imgur.com/KoME2Dc.png) Now go ahead and add the following line of code to your **Character** file: ``` description = dictionary["description"] as? String ?? "No Description" ``` ![](https://i.imgur.com/v6ljGA2.png) You shoukd now add the following line of code to your **DetailViewController**: ``` descriptionLabel.text = character.description ``` ![](https://i.imgur.com/ub4lAs8.png) You should now head over to your **OperationMarvelViewController** and add the following block of code outside of your **viewDidLoad()**: ``` // method that calls whenever we trigger a segue override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // the 'sender' is the object we tapped to trigger the segue // in this case it is a UITableViewCell so we create a cell object let cell = sender as! UITableViewCell // grab the indexPath of the cell we tapped to trigger our segue if let indexPath = tableView.indexPath(for: cell) { // grab our Marvel Character object from the cell we tapped let character = characterArray[indexPath.row] // create a reference to the DetailViewController we are segueing into // by using the segue.destination property and casting it to type // DetailViewController in order to access the properties of this file let detailViewController = segue.destination as! DetailViewController // set the detailVC's character property to the character // found in the cell we selected detailViewController.character = character } } ``` :100: Build and Run your app, then tap on a cell! ![](https://i.imgur.com/Hz6M6ie.png) ![](https://i.imgur.com/Z8RD18L.png) :::success Congratulations! You just finished building your first iOS app and finished passing data between ViewControllers. :::