Download as pdf or txt
Download as pdf or txt
You are on page 1of 56

ICS 224: Mobile App Development

Lab Manual
Winter 2020

Michael Horie
Camosun College

ICS 224 Lab Manual

Lab 1: A First iOS App 7


Goals 7
Preparation 7
1. Setting up Source Control the First Time 7
2. Adding a Button, a Label, and a Switch 8
3. Adding a Text Field 11
4. Completion 11
Lab 2: Improving the User Experience 12
Goals 12
Preparation 12
1. Working with Constraints 12
2. Handling Input Errors 13
3. Writing UI Tests 14
4. Completion 15
Lab 3: Debugging and Profiling Apps 16
Goals 16
Preparation 16
1. Setting Breakpoints 16
2. Analyzing NSUnknownKeyException Crashes 17
3. Running on an Actual Device 18

Page 2 of 56
ICS 224 Lab Manual

4. Profiling an App 19
5. Completion 19
Lab 4: Creating a Master-Detail App 21
Goals 21
Preparation 21
1. Setting up Source Control the Second Time 21
2. Creating a Model 22
3. Creating a Detail View 22
4. Adding a Default Image 23
5. Adapting the Master View 24
6. Creating a Custom Table View Cell 25
7. Completion 25
Lab 5: Refining the Master-Detail App 26
Goals 26
Preparation 26
1. Handling UITextViewDelegate Events 26
2. Adding Persistence (Load/Save) 26
3. Using ScrollViews 29
4. Completion 30
Lab 6: Adding Photo Support 31

Page 3 of 56
ICS 224 Lab Manual

Goals 31
Preparation 31
1. Adding Camera Access 31
2. Adding Photo Library Access via a Gesture Recognizer 33
3. Adding Date Support 34
4. Completion 34
Lab 7: Documents and Segues 35
Goals 35
Preparation 35
1. Creating a Document-Based App 35
2. Setting up Segues 37
3. Completion 39
Lab 8: A First Look at ARKit 40
Goals 40
Preparation 40
1. Setting up the Project 40
2. Exploring the Project 40
3. Adding an Object 41
4. Adding Behaviour 41
5. Adding Notifications 42

Page 4 of 56
ICS 224 Lab Manual

6. Completion 42
Lab 9: Surface Detection and Physics 43
Goals 43
Preparation 43
1. Setting up the Project 43
2. Exploring the Project 43
3. Adding a Scene Asset 43
4. Monitoring AR Session Management 44
5. Positioning an Asset 45
6. Adding Physics 46
7. Detecting Surfaces 48
8. Completion 49
Lab 10: Challenge Lab 50
Goals 50
Preparation 50
1. Creating the App 50
2. Completion 50
Appendix A: Code Marking Scheme 51
Appendix B: Xcode Icons 53
Top Panel (Left) 54

Page 5 of 56
ICS 224 Lab Manual

Top Panel (Right) 54


Left Sidebar 54
Right Sidebar 55
Right Sidebar (Scene Only) 55
Storyboard Bottom Panel (Right) 55
Debug Area Top Panel (Left) 56
Segues (Above View Controllers in Storyboard) 56

Page 6 of 56
ICS 224 Lab Manual

Lab 1: A First iOS App


Due Date: This work must be completed by the date posted on D2L
This lab is out of 10 marks; all stamps are required to pass this lab
Goals
• Become familiar with Xcode
• Build a simple single-view app

Preparation
If you do not already have Gitlab credentials from ICS 199, create an account on Bitbucket or GitHub using your generic ICS
credentials
If you are using GitHub, but have not yet set up a personal access token, log in to GitHub, go to Settings > Developer
settings > Personal access tokens, and generate a repo token for Xcode (enter Xcode in the Note field and select repo (all
related rights should remain selected). Be sure to record this token; you will need it below
Log in to your Mac
Click on the magnifying glass in the top right corner
Enter Xcode
Launch Xcode
Right-click on the Xcode icon that appeared in the Dock at the bottom of the screen, then select Options > Keep in Dock. This
bookmarks Xcode, making it easier to find next time

1. Setting up Source Control the First Time


From the menu bar, select File > New > Project
From the dialog box, select iOS > Single View App and click Next
Use
Lab1 for the Product Name
ics___ for the Organization Name where the blanks represent the 3 digits of your generic ICS ID
ca.camosun.ics___ for the Organization Identifier where the blanks represent the 3 digits of your generic ICS ID
Swift for the Language
Be sure to select Storyboard for the User Interface. If you skip this step, you will get stuck in a couple of steps from now and
will have to delete the project and start over
Click Next
If you get any additional prompts, go with the defaults
Click on Xcode > Preferences

Page 7 of 56
ICS 224 Lab Manual

Click on the Source Control tab


Click on the Git tab
Enter your generic ICS ID (i.e., ics___) for the Author Name and your generic ICS email address (i.e., ics___@camosun.bc.ca)
for the Author Email, where the blanks represent the 3 digits of your generic ICS ID
Click on the Accounts tab
Click on the + at the bottom of the dialog box
Select either Gitlab, Bitbucket Cloud, or GitHub (whichever you chose during the preparatory steps)
Click on Continue
Enter your user ID and your personal access token that you created earlier; do not enter your password since this access
method is considered insecure and will be discontinued
Click on the Source Control Navigator button (see Appendix B)
Right-click on Lab1 master
Click on Create "Lab1" remote
Make sure the Visibility is Private
Click on the Source Control Navigator button again
Expand Lab1 master
Right-click on Branches
Click on Branch from master
Call the new branch work
Click on Create
Click on Source Control > Push
Click on the Safari icon in the Dock
Log in to Gitlab, Bitbucket Cloud, or GitHub (whichever you chose during the
preparatory steps) Private Repo /2

Show your instructor that your project is Private (Instructor Stamp)

2. Adding a Button, a Label, and a Switch


Click on the Project Navigator button (see Appendix B)
Note the following files:
AppDelegate.swift: Handles events such as launch and incoming notifications
SceneDelegate.swift: Handles events such as going into the background or foreground
ViewController.swift: Handles events specific to the view such as setup of GUI elements and GUI-related events
Main.storyboard: Describes the user interface layout to Xcode
Assets.xcassets: Contains custom icons, images, etc.
LaunchScreen.storyboard: Describes the launch screen to Xcode

Page 8 of 56
ICS 224 Lab Manual

Info.plist: Contains information about the app, such as the version, what the iOS device needs to provide (e.g., GPS), what
private information is used, and which orientations (landscape, portrait) are supported
Click on Main.storyboard
From the menu, make sure Editor > Canvas > Bounds Rectangles is selected, to
make it easy to detect UI elements that have transparent backgrounds
Click on the Library button (see Appendix B)
Drag a Switch, a Button, and a Label onto the canvas, one at a time
Make the UI look as illustrated on the right. You can change the label of an
element by double-clicking on it. If elements are not quite centred, don't worry
about that for now
Run the app by clicking on the Play button (see Appendix B)
Be patient; the first launch will take some time!
Move the switch and push the button; they don't do anything useful because we
have not yet tied them to any actions
Stop the app by pressing the Stop button (see Appendix B)
Note the M (Modified) letter next to Main.storyboard
Go to Source Control > Commit and add a commit message (e.g., "Initial UI")
Select Push to remote

Page 9 of 56
ICS 224 Lab Manual

Make sure that the work branch is selected


Click on Commit
Make sure the M (Modified) letter disappears next to Main.storyboard, indicating that the modifications have been committed
In the Main.storyboard, note that there is a triangle to the left of the View Controller Scene. If required, hold down the Option
key (either the Windows or the Alt key on your keyboard) and then click on the triangle to expand the View Controller Scene
and everything below it
Make sure you see the View Controller, View, Safe Area, Push, Switch, and Number of Pushes lines. If not, repeat the above
step
Click on the Assistant Editor button (see Appendix B)
You should now see both the UI as well as the ViewController.swift source code; if not, make sure that the Assistant Editor is in
Automatic mode, not Manual mode
Your screen should now look similar to the screen in the previous illustration (note especially the circled portions)
Underneath the class ViewController line, add
// MARK: Properties
// MARK: Delegate Functions
These add dividers when clicking on in the Toolbar, making it easier to find properties and delegate functions
Click on in the Toolbar and make sure the Properties and Delegate Functions sections are visible
Control-drag the Switch in the storyboard to the line in between the two MARK comments. Xcode can be a bit finicky about
this, so be persistent.
When the popup dialog appears, enter countSwitch as the property name and click Connect
Note that a variable declaration was just created, binding the UI element to this variable
Repeat the same for the button, calling it countButton
Repeat the same for the label, calling it countLabel
At the bottom of the class, add another line
// MARK: Actions
Control-drag the Switch underneath that comment and enter the name toggleSwitch; make sure that Action is selected, not
Outlet
Change the Any in the code that was generated to UISwitch
Add the following code to the toggleSwitch function, to enable/disable the countButton as appropriate:
if (sender.isOn) {
countButton.isEnabled = true
}
else {
countButton.isEnabled = false
}
Control-drag the Button underneath and enter the name increasePushCount; make sure that Action is selected, not Outlet

Page 10 of 56
ICS 224 Lab Manual

Add the following function to the increasePushCount function, to increase the value in countLabel every time the button is
pushed:
let currentCount = Int(countLabel.text!)
let nextCount = (currentCount ?? 0) + 1
countLabel.text = String(nextCount)
Run the program and confirm that the switch will enable/disable the button and that each button press will increment the current
count by 1. This program has an overflow issue, but don't worry about that for now
Be sure to commit and push the working code to the repository

3. Adding a Text Field


Add a Text Field similar to the way you added the button, label, and switch
Give the Text Field the variable name initialValueTextField
Click on the Attributes Inspector button (see Appendix B) and set the Placeholder (not the Text) to 0
For the Keyboard Type, pick the Number Pad
Modify the code so that the initialValueTextField is taken as the initial value, rather than 0, e.g., if the value is 99, pushing the
button would result in 100, then 101, then 102, etc. If the value is then changed to 1000, pushing the button would result in
1001, then 1002, then 1003, etc. Note that initialValueTextField should not change when pressing the button
Dismiss the keyboard by calling initialValueTextField.resignFirstResponder() whenever the user clicks on Push
Run and test the app
Commit and push the changes
When you are ready to demonstrate, call your instructor. Text Field Updated Correctly /8 Max

(Instructor Stamp)
4. Completion
Click on the Standard Editor button (see Appendix B) to return to the standard view
Log out of your Mac
Congratulations! You have completed this lab. Be sure to hand in this lab and see you next week!


Page 11 of 56
ICS 224 Lab Manual

Lab 2: Improving the User Experience


Due Date: This work must be completed by the date posted on D2L
This lab is out of 20 marks; all stamps are required to pass this lab
Goals
• Design a UI that will work with different kinds of displays
• Validate user input
• Write UI tests

Preparation
Section groups 1 and 2 are independent of each other; if you are stuck in one group waiting for support, continue to work on the
other group

1. Working with Constraints


Start up Xcode
Click on the Lab 1 main storyboard
Find the View as: line (at the bottom of the storyboard panel) and click on it
View the UI using the different devices. Also switch between portrait and landscape orientations. Note that the UI elements are
shifted significantly when screen sizes differ
Select the 4 UI elements you added in the previous lab
Click on the Embed button (see Appendix B)
Select the Stack View
Drag the Stack View to the centre of the screen (a blue guideline going from the very top
of the iPhone screen down to the very bottom of the iPhone screen appears when the
item is centred)
Click on the Align button (see Appendix B)
Select Horizontally in Container
Click on Add 1 Constraint
Now view the UI again using different devices and orientations. The elements should
now remain in the centre horizontally
Similar to the way you centred the elements horizontally, centre them vertically as well
Any constraints you add will be visible in the View Controller Scene, as show in the
screenshot on the right
Run your app again and make sure this scheme works for different devices and

Page 12 of 56
ICS 224 Lab Manual

orientations (in Xcode, to the right of the Play button, you can pick different devices, and in the
Simulator, the Hardware menu allows you to test different screen orientations)
Once the UI layout is working correctly, commit and push your changes
Click on the Count Button and click on the Add New Constraints button (see Appendix B)
Change the top spacing to 32
If you look closely, you will see that an error has occurred; to resolve it, first click on the white-on-red arrow to the right of the
View Controller Scene
Click on the white-on-red dot and delete the top constraint
Note that the error has been resolved; normally, the Count Button would have been moved
down by 32 points, but because the button is part of a Stack View, vertical spacing is
dictated by the Stack View itself. Constraints in this case must be changed as follows:
Click on the Stack View
Click on the Attributes Inspector button
Change the spacing to 32
Now right-click on the main storyboard and select Source Control > Discard Changes to return to
your most-recently committed version.
Change the layout of the initialValueTextField and the countLabel so that they take up the entire
space within the Stack View (see the circled portion on the right)
Click on the Attributes Inspector button to centre the text in the initialValueTextField and the countLabel
Run the app and keep pressing the button repeatedly, to make sure the numbers stay
centred Elements Centred and Full Length /0

Show the resulting layout to your instructor (Instructor Stamp)

2. Handling Input Errors


Run the app and enter a very large number, like 92233720368547758070
Click on the Push button; due to an overflow, the next number will be 1. From a user perspective, this is unexpected and needs
to be fixed
Bring up the Assistant Editor like you did in the last lab
Similar to how you created an action for the toggleSwitch and countButton, create an action for the initialValueTextField,
called validateInput; be sure that Editing Changed is selected
If the Int() conversion of the initialValueTextField results in a nil, make sure the initialValueTextField is set to "0"
Also be sure that there are no leading 0s, e.g., if the initialValueTextField contains 077, it should be changed to 77
Enter the number 9223372036854775807 and click on Push
If your program crashes, be sure to fix it! Specifically, test for NSIntegerMax; if that value is reached, the next Push should
reset everything to 0

Page 13 of 56
ICS 224 Lab Manual

Note that Appendix A will be applied, so be sure to structure your code well, check for errors, and document your
work
Commit and push all changes

3. Writing UI Tests
Click on the main storyboard
For each of the 4 elements:
Click on the element
Click on the Identity Inspector button (see Appendix B)
Enter the variable name of the element as the Identifier (e.g., the switch should be called countSwitch)
Click on the Project Navigator button
Click on Lab1UITests.swift
Click inside testExample()
Click on the red record button
Enter the number 10 and click on Push
Click on the record button again
Note that Xcode has added some code. Modify the code so that it looks like the following:
func test10() {
let app = XCUIApplication()
app.launch()

app.textFields["initialValueTextField"].tap()

let key = app.keys["1"]


key.tap()

let key2 = app.keys["0"]


key2.tap()

app.buttons["countButton"].tap()

XCTAssertEqual(app.staticTexts["countLabel"].label, "11")
}
Now hover over the line to the left of func test10(). A white-on-black arrow should appear. Click on the arrow to launch the test
Make sure the test succeeds (a white-on-green checkmark should appear). If not, retrace all the steps of this Section
Add tests to check for various normal and abnormal cases. Be sure to achieve path coverage! In the process, also refactor
your test code so that it is easy to add new test cases, as well as easy to enter long sequences of digits. Avoid repetitive code
and note that Appendix A will be applied

Page 14 of 56
ICS 224 Lab Manual

Make sure the tests pass. You can run all UI tests in one go by clicking on the Test Navigator button (see Appendix B) and then
clicking on the white-on-gray arrow to the right of Lab1UITests
Show the result to your instructor.
After you have completed running your tests, your instructor will ask you to perform Automatic Tests Pass /0

some additional tests (Instructor Stamp)
Commit and push all changes
Print out a copy of your source code and your test code and attach it to this page. Src/Tst Code

/20
4. Completion
Log out of your Mac
Congratulations! You have completed this lab. Be sure to hand in this lab, along with your 2 (two) printouts, and see you
next week!

Page 15 of 56
ICS 224 Lab Manual

Lab 3: Debugging and Profiling Apps


Due Date: This work must be completed by the date posted on D2L
This lab is out of 10 marks; all stamps are required to pass this lab
Goals
• Use the Xcode debugger
• Be able to run apps on real iOS devices
• Monitor app resource consumption using the Instruments application

Preparation
Using your generic ICS credentials, go to https://appleid.apple.com and create an Apple ID
Obtain an iPad from your instructor at the beginning of the lab

1. Setting Breakpoints
Click on the Project Navigator button
Click on the ViewController.swift file
Make sure the Debug Area at the bottom of the screen is visible. If not, click on the Hide or Show the Debug Area button (See
Appendix B)

Go to the increasePushCount function

Page 16 of 56
ICS 224 Lab Manual

Click on the line number to the left of the first line of code underneath the increasePushCount function. For example, if
increasePushCount is on line 50, click on line 51 (assuming line 51 is not blank). A blue arrow should appear
Now run the app
Click the Push button
Xcode will be brought to the foreground and the execution of the app will stop at the breakpoint you just set
Click on the Step Over button (see Appendix B) to step to the next line
Keep watching the left side of the Debug Area and make sure the variables listed there are containing the values you expected
them to contain
Keep pressing the Step Over button until you get to the last line in the function
Press the Continue Program Execution button (see Appendix B)
Click the Push button again
Step through the function again, and make sure the variables are being updated correctly
From now on, if your app is not working correctly, step through the code first with the debugger and try to resolve the issue on
your own before contacting your instructor. Being able to troubleshoot code on your own is a crucial programming skill to hone!
Click on the Breakpoint Navigator button (see Appendix B)
A list of your breakpoints will appear. There should only be 1; click on it and press the Delete button to remove it

2. Analyzing NSUnknownKeyException Crashes


In the View Controller, rename the countButton to countButton2
Run the app again
Note that the compiler will highlight all lines that are expecting countButton instead of countButton2; rename all of them to
countButton2
Run the app again
The app should crash
Check the right panel of the Debug Area. At the very top, you should see the text "Terminating app due to uncaught exception
'NSUnknownKeyException', reason: '[<Lab1.ViewController 0x...> setValue:forUndefinedKey:]: this class is not key value
coding-compliant for the key countButton.'" (most important words highlighted)
This tells you that the ViewController.swift file is not providing an
instance variable called countButton
Click on the Find Navigator button (see Appendix B)
Enter the term countButton
You should see the storyboard appear.
Click on countButton
Right-click on the View Controller that appears underneath the View Controller Scene
You should see a yellow triangle associated with one of the elements in the right panel

Page 17 of 56
ICS 224 Lab Manual

Click on the x to the left of Count Button


If you go to the ViewController source code, there should be an
empty circle next to countButton2, to indicate that the
element is not connected
Connect the button to countButton2, just like you did in Lab 1
These kinds of crashes are common, so expect to see them
and troubleshoot them on your own!
Run the app and make sure the error has been resolved;
don't forget to run the UI tests as well. Note that every now and then, the Simulator becomes confused and won't pop up the
keyboard, causing the tests to fail. In that case, go to Hardware > Keyboard > Toggle Software Keyboard
Click on the Project Navigator button
Select the modified files
Right-click and select Source Control > Discard Changes

3. Running on an Actual Device


Click on the Issue Navigator button (see Appendix B)
There should be 1 warning about a placeholder team ID. If there are any other warnings (or errors), fix them before going on to
the next step
Click on the Project Navigator button
Click on Lab1 at the very top
Click on Lab1 underneath Target in the middle panel
Click on the Signing & Capabilities tab
You should see the error "Signing for "Lab1" requires a development team"
Plug in your iPad
Go to the Active Scheme dropdown (see Appendix B) and select the iPad
Click on the Add Account button
Sign in with the Apple ID credentials you created earlier
Select the Personal Team from the Team drop-down menu
Run the app
Click to always allow access to your keychain when so prompted by codesign
Once the installation fails, on your iPad, go to Settings > General > Profiles & Device Management and trust your certificate
Run the app again
Click on the Debug Navigator button (see Appendix B) App Running on iPad /5

Record the following: (Instructor Stamp)
CPU: __________

Page 18 of 56
ICS 224 Lab Manual

Memory: __________
Energy I.: __________
Disk: __________
Network: __________
It is a good idea to keep an eye on this; apps that consume too much of a resource are automatically terminated by iOS
Show the running app to your instructor

4. Profiling an App
In Xcode, click on Product > Build for Profiling
Click on Product > Profile
When the Instruments application has launched, select Time Profiler
Click on Choose
Click on the red Record button
Quickly push the Push button repeatedly and observe the resource usage; make sure it goes up when you press the button,
and then goes down when you don't interact with the app for a while
Note that because this app is simple, the system processes consume most of the resources
Stop the profiler
Change the app so that every time you press the Push button, the CountLabel is incremented by 1 for 10,000 times; this is of
course inefficient, but it will help to illustrate how profiling works
Run the program normally and make sure that it works
Build the project again for profiling and run it in Instruments as before
Quickly push the Push button repeatedly
After about 15 seconds of pushing repeatedly, click the Pause button in Instruments
You should see Lab1 at the top, with the highest Weight
In the right panel, the Heaviest Stack Trace should be listed
Double-click on the incrementPushCounter line; it should be the first black line underneath main (system calls are greyed out)
You should now see different counts, with the for loop receiving the highest count (counts are samples; they are not an accurate
count for efficiency reasons)
Show the result to your instructor Heavy Use Line Located /5

Next time your app is running sluggishly, or if it is consuming too much memory, use (Instructor Stamp)
Instruments to locate the source of the issue
Discard the changes; do not commit them

5. Completion
Log out of your Mac

Page 19 of 56
ICS 224 Lab Manual

Delete all photos and your app from the iPad, then return the iPad
Congratulations! You have completed this lab. Be sure to hand in this lab and see you next week!


Page 20 of 56
ICS 224 Lab Manual

Lab 4: Creating a Master-Detail App


Due Date: This work must be completed by the date posted on D2L
This lab is out of 10 marks; all stamps are required to pass this lab
Goals
• Create a master-detail app

Preparation
Obtain an iPad from your instructor at the beginning of the lab

1. Setting up Source Control the Second Time


Start up Xcode
Click on File > New > Project
Select the Master-Detail App option
Call it Lab4
Pick the team you created last time as the Team option
Confirm that Storyboard is used for the User Interface
Click Next
Click Create
Click on the Source Control Navigator button
Right-click on Lab4 Master
Click on Create "Lab4" Remote
Change the Visibility to Private
Click on Create
Expand Lab4 master
Right-click on Branches
Click on Branch from master
Call the new branch work
Click on Create
Click on Source Control > Push
Make sure the work branch is selected
Click on Push
Run the app

Page 21 of 56
ICS 224 Lab Manual

Note that the basic functionality is already there: We can add items, click on items to view their details, and we can delete
items. Make sure you try out all these three actions

2. Creating a Model
Over the next few labs, we will create a travel notes app, allowing the user to take photos, add comments to them, and store this
information in an editable list.
Note that unlike the Single View App, there are two files, each representing a different view:
MasterViewController.swift contains code relevant to the master view
DetailViewController.swift contains code relevant to the detail view
Using just two classes for travel notes app will quickly result in a mess. For such (more complex) apps, an approach
resembling the Model-View-Controller pattern is preferable. To do that, we first need to create a model class. Click on File >
New > File
Select Swift File
Click on Next
Call the file PhotoEntry.swift
Click on Create
In the Project Navigator, drag the PhotoEntry.swift line into the Lab4 folder
Replace the code with the following:
import UIKit

class PhotoEntry: NSObject {


// MARK: - Properties
var photo: UIImage
var notes: String

// MARK: - Initializers
init(photo: UIImage, notes: String) {
self.photo = photo
self.notes = notes
}
}

3. Creating a Detail View


Go to the main storyboard
Remove the label from the Detail view
Add in an Image View and a Text View, each of which must have the same width and height,
as illustrated.

Page 22 of 56
ICS 224 Lab Manual

Based on your work with constraints in Lab 2, group the Image and Text Views into a Stack View
Adjust the width of the Stack View such that its 4 borders touch the Safe Areas
Adjust the height so that the height of the Image and Text Views are the same. Do this by clicking on the Stack View,
selecting the Attributes Inspector, and then selecting Fill Equally for the Distribution
Make sure the UI displays correctly regardless of device and orientation (do not yet worry about anomalies introduced when the
keyboard appears). Don't forget to resolve layout errors, just like last time!
Remove all functions from DetailViewController.swift
Replace the code with the following:
import UIKit

class DetailViewController: UIViewController {

// MARK: - Properties
@IBOutlet weak var photoView: UIImageView!
@IBOutlet weak var notesView: UITextView!
var entry: PhotoEntry?

// MARK: - Delegate Functions


override func viewDidLoad() {
super.viewDidLoad()
photoView.image = entry?.photo
notesView.text = entry?.notes
}
}
Don't forget to link the IBOutlets above with the corresponding elements in the story board!
If you get a complaint about PhotoEntry not being declared, make sure there are no typos in the PhotoEntry file name; if that is
not the issue, try restarting Xcode
In the SceneDelegate.swift file, replace
if topAsDetailController.detailItem == nil {
with
if topAsDetailController.notesView == nil {
In the MasterViewController.swift file, comment out the line
// controller.detailItem = object
Build and run your app on your iPad. Note that you can add and delete entries, and even add notes by tapping on the bottom
of the detail view, although that information is not retained when switching between entries.

4. Adding a Default Image


Disconnect the iPad

Page 23 of 56
ICS 224 Lab Manual

Using the iPad camera, take a picture of yourself (or of your work station, if you prefer); don't take a picture of others without
their consent, especially since we will end up storing the photo on GitHub
Reconnect the iPad; the Photos application should come up
Import the photo
In Xcode, click on Assets.xcassets
Click on the +
Click on New Image Set
Drag the photo from Photos into the 3 blank frames
Rename the image in Xcode to defaultImage

5. Adapting the Master View


Click on MasterViewController.swift
The objects array is used to hold all entries. Change the type from Any to PhotoEntry. Xcode will flag some issues which we
will correct next
insertNewObject() is called whenever the + button is pressed. Check the source code in viewDidLoad() to confirm this
In insertNewObject(), replace
objects.insert(NSDate(), at: 0)
with
objects.insert(PhotoEntry(photo: UIImage(named: "defaultImage")!, notes: "My notes"), at: 0)
If you are curious what the other lines of code are doing, click on a function or constant (e.g., the .automatic) and the click on
the Quick Help Inspector button (see Appendix B) to view a brief explanation
In the prepare() function, replace
let object = objects[indexPath.row] as! NSDate
with
let object = objects[indexPath.row]
Also replace
//controller.detailItem = object
with
controller.entry = object
In tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath), replace
let object = objects[indexPath.row] as! NSDate
cell.textLabel!.text = object.description
with
let object = objects[indexPath.row]
cell.textLabel!.text = object.notes

Page 24 of 56
ICS 224 Lab Manual

6. Creating a Custom Table View Cell


Right now, only "My notes" show up in the table when adding a new entry. Let us change that so that both the photo and the
notes show up.
Similar to PhotoEntry, create a new Swift file called PhotoEntryTableViewCell
Edit the file, making PhotoEntryTableViewCell a subclass of UITableViewCell; don't forget to import UIKit instead of Foundation!
In the main story board, click on the Cell in the Master Scene
Click on the Attributes Inspector and change the Table View Cell Style to Custom. If you cannot do that, you did not click on the
Cell in the Master Scene
Click on the Identity Inspector and change the class to PhotoEntryTableViewCell
You also have to change the size of the PhotoEntryTableViewCell. Click on the Size Inspector button (see Appendix B).
Uncheck the Automatic Row Height and manually adjust the height of the row (to, say, 100)
Drag in a UIImage to the left part of the PhotoEntryTableViewCell
Drag in a UILabel to the right of the PhotoEntryTableViewCell
Work with constraints so that regardless of device and orientation, the appearance will be
as illustrated
Now control-drag the UIImage and UILabel in to the PhotoEntryTableViewCell, calling them
photoView and notesView, respectively. If that is not possible, make sure you have
selected the Cell in the Master Scene
In tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath), change
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
to
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as!
PhotoEntryTableViewCell
and
cell.textLabel!.text = object.notes
Default Photo/Notes in Cell/Detail /5

to
cell.photoView.image = object.photo (Instructor Stamp)
cell.notesView.text = object.notes
Run the app and make sure it works
When you are done, show the result to your instructor Misc. Devices and Orientations /5

Commit and push your work (Instructor Stamp)

7. Completion
Log out of your Mac
Delete all photos and your app from the iPad, then return the iPad
Congratulations! You have completed this lab. Be sure to hand in this lab and see you next week!


Page 25 of 56
ICS 224 Lab Manual

Lab 5: Refining the Master-Detail App


Due Date: This work must be completed by the date posted on D2L
This lab is out of 20 marks; all stamps are required to pass this lab
Goals
• Handle UITextViewDelegate events
• Load/save data
• Use ScrollViews

Preparation
Obtain an iPad from your instructor at the beginning of the lab

1. Handling UITextViewDelegate Events


Currently, changes in the notes section are lost as soon as another PhotoEntryTableViewCell is selected. Unfortunately, unlike
UITextFields, UITextViews do not support actions (at time of printing). Instead, delegation must be used. Bring up the
DetailViewController source code
Replace
class DetailViewController: UIViewController {
with
class DetailViewController: UIViewController, UITextViewDelegate {
Next, tie the UITextViewDelegate to the DetailViewController by adding the line
notesView.delegate = self
at the end of viewDidLoad(). viewDidLoad() is only called when a view has been fully loaded in to memory, with all IBOutlets
initialized, so it is a natural choice when it comes to setting up delegates
Finally, add the following function to the end of the DetailViewController:
func textViewDidChange(_ textView: UITextView) {
entry?.notes = textView.text
}
This function is invoked by UITextViews whenever text is changed
Run the app again and make sure that changes to the notes field are now retained even if another PhotoEntryTableViewCell is
selected

2. Adding Persistence (Load/Save)


Quit the app

Page 26 of 56
ICS 224 Lab Manual

Now launch the app again. All additions and changes you made should have been lost, because we have not yet told iOS how
to save and load data
In the PhotoEntry, replace
class PhotoEntry: NSObject {
with
import os

class PropertyKey {
static let photo = "photo"
static let notes = "notes"
}

class PhotoEntry: NSObject, NSCoding {


static let documentsDirectory = FileManager().urls(for: .documentDirectory,
in: .userDomainMask).first!
static let archiveURL = documentsDirectory.appendingPathComponent("entries")
Add the following at the end of the class:
// MARK: - Load/Save
required convenience init?(coder aDecoder: NSCoder) {
guard let newPhoto = aDecoder.decodeObject(forKey: PropertyKey.photo) as? UIImage else {
os_log("Missing image", log: OSLog.default, type: .debug)
return nil
}
guard let newNotes = aDecoder.decodeObject(forKey: PropertyKey.notes) as? String else {
os_log("Missing notes", log: OSLog.default, type: .debug)
return nil
}
self.init(photo: newPhoto, notes: newNotes)
}

func encode(with aCoder: NSCoder) {


aCoder.encode(photo, forKey: PropertyKey.photo)
aCoder.encode(notes, forKey: PropertyKey.notes)
}
In the MasterViewController, add the following at the end of the class:
// Mark: - Load/Save
func loadObjects() -> [PhotoEntry]? {
do {
let data = try Data(contentsOf: PhotoEntry.archiveURL)
return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [PhotoEntry]
} catch {

Page 27 of 56
ICS 224 Lab Manual

os_log("Cannot load due to %@", log: OSLog.default, type: .debug, error.localizedDescription)


return nil
}
}

func saveObjects() {
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: objects, requiringSecureCoding:
false)
try data.write(to: PhotoEntry.archiveURL)
} catch {
os_log("Cannot save due to %@", log: OSLog.default, type: .debug, error.localizedDescription)
}
}
Fix the following 3 items on your own. If you ask other students or the instructor, you will lose 5 marks! If you help
Independent

Independent
someone, you will also lose 5 marks!!
Add 1 line to fix the os_log() errors (Hint: Look at PhotoEntry)
Work

Work
Add a few lines to load objects from file and store them in the objects variable after the Master view has been loaded (Hint:
Look closely at the loadObjects() return type and the variable declarations at top of the MasterViewController class)
Add a few more lines to save objects whenever an entry is added or deleted (Hint: Look closely at all the functions in
MasterViewController)
Modify the code further so that objects are saved whenever an entry is changed (Hint:
Find the method that (A) you inserted earlier in this lab and that (B) is called whenever PhotoEntryTableViewCell Update /0

a change is made. Then set a flag there. Next, find the method in (Instructor Stamp)
MasterViewController that is the best place to check for that flag and make the code
react accordingly)
Modify the code further so the changed entry is reflected in the corresponding Load/Save IF Needed /0

PhotoEntryTableViewCell (Hint: You may have to call (Instructor Stamp)
tableView.reloadData() to make sure changes are shown in the list of entries)
When you are ready, place a breakpoint on loadObjects() and saveObjects() and show your instructor that
loading and saving is only done when required Source Code

Commit and push all your changes /20
Print out a copy of all your Swift code and attach it to this page. Note that Appendix A will be
applied, so be sure to structure your code well, check for errors, and document your work

Page 28 of 56
ICS 224 Lab Manual

3. Using ScrollViews
Depending on the rotation and the device (e.g., iPhone landscape), the keyboard can completely block out the notes section.
There are various ways to fix this. We'll use Scroll Views
Click on the main story board
Drag a Scroll View into the Detail Scene's View, just underneath the Safe Area (the left panel, not
the right panel)
Drag the Stack View in to the Scroll View, just underneath the Frame Layout Guide
Control-drag from the Scroll View to the Safe Area and select the first 4 options, one at a
time (see screenshot on the right)
The Scroll View should now take up the entire Safe Area (click on the Safe Area to see its
dimensions)
Control-drag from the Stack View to the Content Layout Guide and select the first 4 options,
one at a time (see screenshot on the right)
Select the Stack View in the left panel and click on the Add New Constraints button (see Appendix B)
Control-drag from the Stack View to the Scroll View and select Equal Widths
Click on the Stack View and click on t the Add New Constraints button (see Appendix B)
Set the Height to 1000
In DetailViewController, add the following to the class:
let OFFSET: CGFloat = 10
At the bottom of viewDidLoad() in DetailViewController, add the following code
NotificationCenter.default.addObserver(self, selector: #selector(keyboardAppeared), name:
UIWindow.keyboardDidShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDisappeared), name:
UIWindow.keyboardDidHideNotification, object: nil)
Now add the following methods:
@objc func keyboardAppeared(_ notification: NSNotification) {
guard let frameValue = notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as?
NSValue else {
return
}
let frame = frameValue.cgRectValue
scrollVIew.contentInset.bottom = frame.size.height + OFFSET
scrollVIew.verticalScrollIndicatorInsets.bottom = frame.size.height + OFFSET
}

@objc func keyboardDisappeared(_ notification: NSNotification) {


scrollVIew.contentInset.bottom = 0
scrollVIew.verticalScrollIndicatorInsets.bottom = 0

Page 29 of 56
ICS 224 Lab Manual

}
Run the app on the iPad as well as the iPhone (using the Simulator); check both rotations on both devices to make sure you
can now scroll to the notes section

4. Completion
Log out of your Mac
Delete all photos and your app from the iPad, then return the iPad
Congratulations! You have completed this lab. Be sure to hand in this lab, along with your printouts, and see you next week!


Page 30 of 56
ICS 224 Lab Manual

Lab 6: Adding Photo Support


Due Date: This work must be completed by the date posted on D2L
This lab is out of 20 marks; all stamps are required to pass this lab
Goals
• Add camera support
• Add photo library support
• Use a date picker

Preparation
Obtain an iPad from your instructor at the beginning of the lab

1. Adding Camera Access


Bring up the main story board
Drag a Bar Button Item to the top right hand corner of the Detail Scene's View
In the Attributes Inspector, change the System Item to Camera
Associate the Camera button with an Action called takePhoto(). If you forgot how to do that, review Lab 1
We already turned the DetailViewController into a UITextViewDelegate. To support the next steps, add
UIImagePickerControllerDelegate and UINavigationControllerDelegate to the list
Add the following code to takePhoto():
// Check if the device has a camera
if !UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.camera) {
let alert = UIAlertController(title: "Camera Error", message: "Camera not available",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}

// Launch the camera controller


let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = UIImagePickerController.SourceType.camera
picker.allowsEditing = true
self.present(picker, animated: true, completion: nil)
Now add the following delegates:

Page 31 of 56
ICS 224 Lab Manual

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info:


[UIImagePickerController.InfoKey : Any]) {
photoView.image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
picker.dismiss(animated: true, completion: nil)
}

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {


picker.dismiss(animated: true, completion: nil)
}
Run the app on the iPad you signed out at the beginning of this lab and press on the camera icon
The app should crash with the message: "This app has crashed because it attempted to access privacy-sensitive data without a
usage description. The app's Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the
user how the app uses this data."
This is a privacy safeguard in iOS. We are not allowed to take photos without user consent. The consent text must be placed
in Info.plist, which iOS will then use as part of its consent process. Click on the Project Navigator button
Click on Info.plist
Click on the + next to Information Property List
Enter Privacy - Camera Usage Description (this is the long version of NSCameraUsageDescription; if you prefer the short
version, right-click on Information Property List and select Show Raw Keys/Values)
In the field to the right, enter "Camera access is needed to add photos to the app"
Next, replace the code underneath the // Launch the camera controller comment with the following:
AVCaptureDevice.requestAccess(for: AVMediaType.video) { response in
if response {
DispatchQueue.main.async {
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = UIImagePickerController.SourceType.camera
picker.allowsEditing = true
self.present(picker, animated: true, completion: nil)
}
}
}
You also have to import AVFoundation
Run the app again and press on the camera icon


Page 32 of 56
ICS 224 Lab Manual
Independent

Independent
Do the following on your own. If you ask other students or the instructor, you will lose 5 Marks! If you help other
students, you will also lose 5 Marks!!
Note that photos are not currently saved when selecting another entry; fix this problem, just like you did for the notes portion
Work

Work
of the view
Also, make sure the photos are saved when the app terminates, or when it is launched again
Update the PhotoEntryTableViewCell with the new image
Right now, on an iPad, the camera icon is enabled even if there are no entries, or if no entry has been selected, or all
entries have been deleted. Correct this so that the camera icon is only enabled when an actual entry has been selected. If
you don't recall how to enable or disable buttons, review Lab 1
Also hide or remove the image and notes when all entries have been deleted
Camera Support /0

Test your app thoroughly
(Instructor Stamp)
Commit and push all changes
Demo your working app to your instructor

2. Adding Photo Library Access via a Gesture Recognizer


We want to change the behaviour so that if someone taps on the image, that user will be able to select a photo from the photo
library
From the Object Library, drag a Tap Gesture Recognizer and drop it on top of the Photo View in the DetailViewController
Control drag from the Tap Gesture Recognizer that has appeared on the story board side panel to the DetailViewController
Create an Action (NOT an Outlet!) called selectImageFromPhotoLibrary and select UITapGestureRecognizer as the Type
Confirm that your class responds to the UIImagePickerControllerDelegate and UINavigationControllerDelegate protocols
Add the following lines to selectImageFromPhotoLibrary():
PHPhotoLibrary.requestAuthorization({ status in
if status == .authorized {
DispatchQueue.main.async {
let imagePickerController = UIImagePickerController()
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
self.present(imagePickerController, animated:true, completion:nil)
}
}
})
Also import the Photos module
As in the previous Section, add a privacy description with the key Privacy - Photo Library Usage Description or
NSPhotoLibraryUsageDescription and value "Photo library access is needed to add photos to the app"
Click on the Photo View, select the Attributes Inspector, and check User Interaction Enabled

Page 33 of 56
ICS 224 Lab Manual

Run the app and confirm that you can add photos from the photo library. If the photo
library is empty, take a few photos to populate it, then try again to add the photos from Photo Library Support /0

the photo library to your app (Instructor Stamp)
Demonstrate to your instructor that photo library access works

3. Adding Date Support


Independent

Independent
Do this Section on your own. If you ask other students or the instructor, you will lose 5 Marks! If you help others, you
will also lose 5 Marks!!
Add a Date Picker from the Object Library to the bottom of the DetailViewController
Work

Work
Assuming you name the Outlet date, you can have a function called dateChanged() be invoked whenever the date is changed
by adding the following code:
date.addTarget(self, action: #selector(dateChanged(_:)), for: .valueChanged)
Also add the lines:
@objc func dateChanged(_ sender: UIDatePicker) {
// Add code here
}
Now add support for adding dates. Dates must be saved and loaded just like notes and photos are. You may want to do some
research on UIDatePicker using the built-in Quick Help in order to be able to set dates
Demonstrate to your instructor that date support works Date Picker Support /0

(Instructor Stamp)

4. Completion
Print out a copy of ALL your modified Swift code and attach it to this page. Highlight the changes you made since the
last Lab. You can use the Version Editor (see Appendix B) to help you view all changes. Note that Appendix A will be
applied, so be sure to structure your code well, check for errors, and document your work
Log out of your Mac Source Code

Delete all photos and your app from the iPad, then return the iPad /20
Congratulations! You have completed this lab. Be sure to hand in this lab and see you next week!

Page 34 of 56
ICS 224 Lab Manual

Lab 7: Documents and Segues


Due Date: This work must be completed by the date posted on D2L
This lab is out of 10 marks; all stamps are required to pass this lab
Goals
• Create document-based apps
• Create multi-screen apps

Preparation
Obtain an iPad from your instructor at the beginning of the lab
For more details, consult Creating Document-Based iOS Apps (https://swiftdevjournal.com)

1. Creating a Document-Based App


Create a new Document-Based App project called Lab7
Push it to your Gitlab, Bitbucket Cloud, or GitHub account as a private project, just like you did in Lab
4. Don't forget to create and work off the work branch
Check that, among other files, a DocumentBrowserViewController.swift and a Document.swift file were
created. The former file is used to display a file chooser, the latter is used to describe the document
structure
In the Project Navigator, click on the first Lab7 line (the one with the white-on-blue icon)
Click on Info
Completely expand Document Types. Currently, your app:
can handle only images
can only view images because the CFBundleTypeRole is set to Viewer, not Editor
is one of many apps that can handle images because the LSHandlerRank value is Alternate. If we
were creating our own document type, the value should be Owner
Replace Images with RTF
Replace public.image with public.rtf
Replace Viewer with Editor
Now click on the main storyboard
Go to the Document View Controller Scene
Add a Text View to the outer Stack View
Make the interface look as illustrated on the right. Your layout should work for any iPad or iPhone
In the Attributes Inspector, change the Text from Plain to Attributed

Page 35 of 56
ICS 224 Lab Manual

Check Allows Editing Attributes


Be sure to add an outlet for the Text View called textView
Now we must create a document template. Click on File > New > File
Select Rich Text File
Click on Next
Call it New Document
Click on Create
Open the DocumentBrowserViewController.swift file
Replace
let newDocumentURL: URL? = nil
with
let newDocumentURL: URL? = Bundle.main.url(forResource: "New Document", withExtension: "rtf")
Replace
importHandler(newDocumentURL, .move)
with
importHandler(newDocumentURL, .copy)
Open the Document.swift file
At the top of the class, add the line
var text: NSAttributedString? = nil
At the top of the contents function, add the lines
if text != nil {
return try NSKeyedArchiver.archivedData(withRootObject: text!, requiringSecureCoding: false)
}
Add the following to the load function
guard let data = contents as? Data else {
return
}
text = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? NSAttributedString
In the DocumentViewController, change
var document: UIDocument?
to
var document: Document?
Also add the line
self.textView.attributedText = self.document?.text
Based on the source code, you should be able to figure out where this line has to go
To dismissDocumentViewController, add
if document?.text != textView.attributedText {
document?.text = textView.attributedText

Page 36 of 56
ICS 224 Lab Manual

document?.updateChangeCount(.done)
}
before the call to dismiss
Run the app and make sure you can create new documents, edit them, save them, and load them again.

2. Setting up Segues
Add 5 buttons underneath the file name. Use a label with a single space character
to separate the last button
Create outlets and actions for each of the Style buttons
Now create a new Swift file called CustomizeViewController and make it a
subclass of UIViewController
In the main storyboard, add a new View Controller
Click on the View Controller (not the View; best to use the sidebar to the left)
Click on the Identity Inspector
Change the Class to CustomizeViewController. The name should change to CustomizeViewController in the storyboard as well
Add a picker, a text field, and 3 buttons and arrange them as shown on the right
Add outlets for these 5 elements
Also add the line
var pickerData = [String]()
at the top of the CustomizeViewController
Embed the CustomizeViewController in a Navigation Controller
Control drag from the Customize button to the Navigation Controller
Select Show
Click on the Show segue to "Navigation Controller"
Click on the Attribute Inspector
Change the Identity to ShowController
Click on Customize View Controller
Add a Bar Button Item to the Customize View Controller
Using the Attribute Inspector, change its System Item to Done
In DocumentViewController.swift, add the following code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if segue.identifier != "ShowController" {
return
}

Page 37 of 56
ICS 224 Lab Manual

guard let customizeViewController = segue.destination.children[0] as? CustomizeViewController else


{
fatalError("Unexpected destination \(segue.destination)")
}
customizeViewController.pickerData = ["Style 1", "Style 2", "Style 3", "Style 4"]
}

@IBAction func unwindToDocumentViewController(sender: UIStoryboardSegue) {


if let sourceViewController = sender.source as? CustomizeViewController {
print(sourceViewController.pickerData)
}
}
Save the current file
In the main storyboard, control-drag from the CustomizeViewController's Done button to the Exit Segue icon (see Appendix B)
just above it
Select unwindToDocumentViewControllerWithSender
Run the app and make sure that pressing the Customize button will get you to the CustomizeViewController, and pressing
Done will get you back to the DocumentViewController
For the remainder of this Section, you must work on your own (using Apple's documentation and resources like

Independent Work
Independent Work

StackOverflow is fine). If you ask other students or the instructor, you will lose 5 Marks! If you help others, you will
also lose 5 Marks!!
Make it possible to change the button labels of the DocumentViewController from the CustomizeViewController. For example, if
we pick "Style 4" in CustomizeViewController and change the label to "Emphasize", the picker should be updated, and the
corresponding button label should also be updated (see below). Specifically:

Page 38 of 56
ICS 224 Lab Manual

CustomizeViewController must conform to the UIPickerViewDataSource and UIPickerViewDelegate protocols. Review the
methods that are part of the 2 protocols and implement the correct ones
CustomizeViewController must be declared as a delegate of the picker. There is a particular method that is called once the
View has been set up. That method is best suited for the delegate declaration
Every time an entry is picked from the picker, the button label in CustomizeViewController must be updated accordingly
Every time the button label is changed, the picker in CustomizeViewController must also change accordingly. This involves

Independent Work
Independent Work

sending a reload message to the picker


Every time the CustomizeViewController is dismissed, the Style buttons in DocumentViewController should be updated (you
can use button.setTitle("label", for: .normal) for that)
Be sure to avoid repetitive code; use loops and functions whenever possible
Test your app
Commit and push the source code
Further modify the code so that each style can have its own combination of bold, italics, and underline attributes. So for
example, if selecting Style 1 and then pressing the Bold button and Italics button, Style 1 should now be associated with bold
and italics characteristics. This also needs to be reflected in CustomizeViewController, setting the title colours of the Bold and
Italics button to red from blue. Use the button setTitleColor() function for that
Finally, make sure that pressing the style buttons in the DocumentViewController actually apply the style. For example, if
applying the above Style 1, the selected text should turn to bold italics. To keep things simple, pressing the buttons a second
time does not undo the action. You can use:
let range = textView.selectedRange to get the location of the selected text, if any
attributedString.addAttribute(NSAttributedString.Key.underlineStyle, value:
NSUnderlineStyle.single.rawValue, range: range) to underline the text Style Support /0

UIFontDescriptor to set bold and italic styles (you have to research this a little bit) (Instructor Stamp)
Test your app thoroughly. When you are ready, call your instructor. Your instructor will
ask you to perform some additional tests.
Print out a copy of ALL your modified Swift code and attach it to this page. Note that Appendix A will be applied, so be
sure to structure your code well, check for errors, and document your work
Commit and push all changes Source Code

/20
3. Completion
Log out of your Mac
Delete all photos and your app from the iPad, then return the iPad
Congratulations! You have completed this lab. Be sure to hand in this lab and see you next week!

Page 39 of 56
ICS 224 Lab Manual

Lab 8: A First Look at ARKit


Due Date: This work must be completed by the date posted on D2L
This lab is out of 10 marks; all stamps are required to pass this lab
Goals
• Start to become familiar with ARKit

Preparation
Obtain an iPad from your instructor at the beginning of the lab

1. Setting up the Project


Create a new Project called Lab8. Be sure to select the Augmented Reality App option
Set up a private git repository as before. Don't forget to create and work off the work branch
Connect your iPad to your Mac
Run the app
When prompted for camera access permission, grant access and then quit the app via Xcode
Disconnect your iPad
Run the app again
While remaining seated, slowly move the iPad back and forth, up and down, left and right; be sure to keep looking at the
screen. You should eventually come across a superimposed object
You can look at the object from different angles, but if you leave your station, please remain aware of your surroundings (other
students, obstacles, cables, etc.) at all times

2. Exploring the Project


Take a look at the ViewController. Note that there is only minimal code required to set up the AR scene
Take a look at the main storyboard. Note that it consists only of an ARView at this point
Click on Experience.rcproject
Click on Open in Reality Composer
Click on the grid
Confirm that the Anchor Type is Horizontal (designed for horizontal surfaces like tables)
Note that the object itself can be viewed from different perspectives by dragging on the grid

Page 40 of 56
ICS 224 Lab Manual

3. Adding an Object
Using Safari, go to https://developer.apple.com/augmented-reality/quick-look/ and download the biplane
Drag the biplane in to the Reality Composer
Delete the default box
Place the biplane in the centre of the scene using the Transform menu
Name it biplane
Save the scene
Run the program again and confirm that the box has been replaced by the biplane

4. Adding Behaviour
In the Reality Composer, click on View > Show Behaviours
Click on +
Select Custom Behaviour
Click on the Trigger box
Select Tap
Select the object you chose earlier
Click on Done
Click on the Action Sequence box
Select USDZ Animation (this file format was developed by Apple and Pixar for AR, and is based on Pixar's USD scene
description format)
Choose the biplane
Click on the + next to Action Sequence
Select Move, Rotate, Scale To
Choose the biplane
Change to duration to 5 seconds
Pick a new destination position
Now click on the Play button. After tapping on the biplane, it should move to the indicated position and then turn on the
propellers. This is not quite what we want.
Drag the USDZ Animation onto Move, Rotate, Scale To
Click on the Play button again
Adjust the Iterations of the USDZ Animation so that the propeller keeps moving for at Moving Plane /5

least as long as the plane is moving (Instructor Stamp)
Save the scene
Run the program again and confirm that the plane is moving once it has been tapped. Show the result to your instructor
Commit and push your code

Page 41 of 56
ICS 224 Lab Manual

5. Adding Notifications
Add a View to your ARView Scene, and inside, place an ARView and a Button, as pictured on the
right side
Back in the Reality Composer, place another object into the scene using Insert > Object. You may
have to download the object first by clicking on the cloud icon to the right of the object's section title
Add another Custom Behaviour
Click on Trigger
Select Notification
Call it startNotification
For the Action Sequence, select Orbit
Select the biplane as the Selected Object and the object you inserted above as the Center
Change the duration to 5 seconds
In ViewController.swift, modify the code so that
boxAnchor becomes an instance variable
pushing the button results in a call to boxAnchor.notifications.startNotification.post()
arView is correctly linked to the ARView you added above
When the app is working correctly, call your instructor for a demo
Commit and push all changes

6. Completion
Log out of your Mac
Delete all photos and your app from the iPad, then return the iPad Orbiting Plane /5

Congratulations! You have completed this lab. Be sure to hand in this lab and see you (Instructor Stamp)
next week!

Page 42 of 56
ICS 224 Lab Manual

Lab 9: Surface Detection and Physics


Due Date: This work must be completed by the date posted on D2L
This lab is out of 10 marks; all stamps are required to pass this lab
Goals
• Apply physics to AR assets
• Detect surfaces using ARKit

Preparation
Obtain an iPad from your instructor at the beginning of the lab

1. Setting up the Project


Create a new Project called Lab9. Be sure to select the Augmented Reality App and the SceneKit options
Set up a private git repository as before. Don't forget to create and work off the work branch
Connect your iPad to your Mac
Run the app
When prompted for camera access permission, grant access and then quit the app via Xcode
Disconnect your iPad
Run the app again
While remaining seated, slowly move the iPad back and forth, up and down, left and right; be sure to keep looking at the
screen. You should eventually come across a superimposed object
You can look at the object from different angles, but if you leave your station, please remain aware of your surroundings (other
students, obstacles, cables, etc.) at all times

2. Exploring the Project


Take a look at the main storyboard. Note that it consists only of an ARSCNView (different from ARView) at this point
Expand the art.scnassets folder. The object as well as some texture data are stored in this folder
Click on the object. Note that the object itself can be viewed from different perspectives by clicking on the drop down menus at
the bottom of the scene graph, and that the scene can be edited via the inspector panel on the right of the scene graph
Take a look at the ViewController. Note that there is only minimal code required to set up the ARSCNView scene

3. Adding a Scene Asset


Click on the yellow Lab9 folder in the Project Navigator

Page 43 of 56
ICS 224 Lab Manual

Click on File > Add Files to Lab9


Create a new folder called ics___.scnassets, where the blank represents the number of your generic user ID
Click on Add
A new, empty ics___.scnassets folder should appear in your Project Navigator. Confirm that this is not a plain blue folder icon,
but one that looks just like the art.scnassets folder. If not, you mistyped the extension; delete the folder and repeat the steps in
this Section
Click on the ics___.scnassets folder and select File > New File
A SceneKit Scene.scn file should appear. Rename it to ics___Scene.scn, where the blank represents the number of your
generic user ID
From the Object Library, drag a Box into the scene
Click on the box and position it in the centre (x = y = z = 0) using the Node Inspector (see Appendix B)
Also change all dimensions to 0.5
Adjust the box rotation and elevation using the arrows and arcs. You can view the box from different angles by dragging the
canvas outside of the box. Spend some time becoming familiar with this. If you become lost, use the Node Inspector to set
the position manually
Click on the Finder icon (the blue face in the Dock) and go to Macintosh HD > Library > Desktop Pictures
Select a picture you like and drag it in to the ics___.scnassets folder
Drag the picture onto the box
Note: This is for educational purposes only. Before releasing any product, make sure that you have obtained licenses
for all assets that you deploy. Later, you can add your own COLLADA artwork to this app; Xcode will convert this when
selecting Editor > Convert to SceneKit scene file format
Change the ViewController so that your scene is loaded, not the default scene
Run the program and show the result to your instructor Textured Box /2

Commit and push all changes (Instructor Stamp)

4. Monitoring AR Session Management


Go to the main storyboard and add a Visual Effect View and a Label, as shown
on the right
Add appropriate constraints
Create an Outlet for the Label called statusLabel
Add the following to the bottom of viewDidLoad() in the ViewController:
if !ARWorldTrackingConfiguration.isSupported {
statusLabel.text = "World tracking not supported"
}
else {

Page 44 of 56
ICS 224 Lab Manual

statusLabel.text = "Ready"
let config = ARWorldTrackingConfiguration()
config.worldAlignment = .gravity
config.providesAudioData = false
}
Also add status messages to
func session(...)
func sessionWasInterrupted(...)
func sessionInterruptionEnded(...)
Create a function called func session(_ session: ARSession, cameraDidChangeTrackingState camera: ARCamera)
Update the statusLabel with a detailed status explanation for every possible camera tracking state. You will have to explore the
documentation a bit to find all possible states
Run the app and make sure the status messages change when:
Switching to another app and then back to your app
Covering the back camera lens

5. Positioning an Asset
Add sliders and labels to your app as illustrated on the right
Create Outlets for the labels named xLabel, yLabel, and zLabel, from top to
bottom
Create Actions for the sliders named changeX, changeY, and changeZ, from
top to bottom
For each slider, click on the Attributes Inspector, and change the default Value
to 0, the Minimum to -1, and the Maximum to +1
Make sure that the correct label is updated whenever a slider is moved
Note that you can control the formatting of a floating point value by using the
command
String(format: "%.2f", x)
This will print x as a floating number with no more than two digits after the decimal point
Make sure the box moves in accordance with the slider values. You can use the following code as a basis for this:
if let box = scene?.rootNode.childNode(withName: "box", recursively: false) {
box.position = SCNVector3Make(x, y, z)
}
where scene needs to be the same variable as scene in viewDidLoad
Test your app with varying rotations

Page 45 of 56
ICS 224 Lab Manual

When the app is working correctly, call your instructor for a demo
Commit and push all changes Sliders Move Box, Update Labels /2

(Instructor Stamp)

6. Adding Physics
At the top, add 2 buttons, one titled Launch, the other titled Adjust
Make the Adjust button trigger a segue to go to a settings screen; if you don't recall how to
implement segues, review Lab 7. Don't forget to add the prepare and unwind methods in
the main view controller, as well as a Done button
Set up the settings screen just like on the right-hand side. Specifically:
Create a settings view controller class. Don't forget to update the main story board with
this information
The settings view controller class must have an instance variable var physicsBody:
SCNPhysicsBody? that is passed in from the main view controller
Create outlets for each of the values on the right
Create outlets for each of the slider
Slider values must be derived from the corresponding value in the physicsBody
Slider values must range from 0 to 1
Create actions for each of the sliders that update the corresponding values on the right
Slider values must also be applied to the physics model of the box asset
In the main view controller, add the following to viewDidLoad
physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
physicsBody?.isAffectedByGravity = true
physicsBody?.allowsResting = true
In the launch function, apply the physics model and position the new box using the following code:
guard let frame = self.sceneView.session.currentFrame else {
return
}
let transform = SCNMatrix4(frame.camera.transform)
let position = SCNVector3(transform.m41, transform.m42, transform.m43)

let boxScene = SCNScene(named: "ics000.scnassets/ics000Scene.scn")


guard let box = boxScene?.rootNode.childNode(withName: "box", recursively: false) else {
return
}

let newPhysicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)


newPhysicsBody.isAffectedByGravity = self.physicsBody!.isAffectedByGravity
newPhysicsBody.allowsResting = self.physicsBody!.allowsResting

Page 46 of 56
ICS 224 Lab Manual

newPhysicsBody.angularDamping = self.physicsBody!.angularDamping
newPhysicsBody.centerOfMassOffset = self.physicsBody!.centerOfMassOffset
newPhysicsBody.charge = self.physicsBody!.charge
newPhysicsBody.damping = self.physicsBody!.damping
newPhysicsBody.friction = self.physicsBody!.friction
newPhysicsBody.mass = self.physicsBody!.mass
newPhysicsBody.restitution = self.physicsBody!.restitution
newPhysicsBody.rollingFriction = self.physicsBody!.rollingFriction

let newBox = box.clone()


newBox.physicsBody = newPhysicsBody
newBox.position = position
self.sceneView.scene.rootNode.addChildNode(newBox)
Furthermore, to stop the ship from falling forever (and thereby consuming needless CPU resources), add the following function:
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
DispatchQueue.main.async {
for node in self.sceneView.scene.rootNode.childNodes {
if node.name == "box" && node.presentation.position.y < -10 {
node.removeFromParentNode()
}
}
}
}
Also, in viewDidLoad, replace
scene = SCNScene(named: "ics000.scnassets/ics000Scene.scn")
with
scene = SCNScene()
to work with a clean, unmodified scene
Start the app and point the iPad straight down
Click on Launch. You should see the box drop to the floor and disappear
Now change the Damping (not Angular Damping) factor to 1 and repeat
Wait until the app state goes from Limited: initializing to Normal
Press the Launch button and slowly move back a bit
You should see the box drop to the floor much more slowly
Change Damping back to 0.1 and see what happens when you launch another box above the one that is still dropping to the
floor
Now change the Mass to 0 and launch another box
Try adjusting more values and observe changes (if any), but note that not all settings will have a visible effect in the current
setup

Page 47 of 56
ICS 224 Lab Manual

Show the result to your instructor


Physics Applied /2

(Instructor Stamp)
7. Detecting Surfaces
At the top of the main view controller class, add the following:
var firstPlane = true
Change config in viewDidLoad to an instance variable
Also add
config.planeDetection = .horizontal
underneath
config.providesAudioData = false
Change viewWillAppear so that it uses config instead of configuration
Add the following functions to the bottom of the class:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
if firstPlane {
firstPlane = false
}
else {
return
}

guard let planeAnchor = anchor as? ARPlaneAnchor else {


return
}

DispatchQueue.main.async {
let planeGeometry = SCNPlane(width: CGFloat(planeAnchor.extent.x), height:
CGFloat(planeAnchor.extent.z)) // y is always 0 for both .horizontal/.vertical
planeGeometry.firstMaterial?.diffuse.contents = UIColor.init(red: 0.5, green: 0.5, blue: 0.5,
alpha: 0.5)
let planeNode = SCNNode(geometry: planeGeometry)
planeNode.position = SCNVector3Make(planeAnchor.center.x, 0, planeAnchor.center.z)
planeNode.transform = SCNMatrix4MakeRotation(-Float.pi / 2.0, 1, 0, 0) // SCNPlane is not
oriented as we would expect; and only visible from one side!
planeNode.physicsBody = SCNPhysicsBody(type: .kinematic, shape: SCNPhysicsShape(geometry:
planeGeometry, options: nil))
planeNode.physicsBody?.restitution = 0.5
planeNode.physicsBody?.friction = 0.5
node.addChildNode(planeNode)
}
}

Page 48 of 56
ICS 224 Lab Manual

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor, node.childNodes.count > 0, let planeGeometry =
node.childNodes[0].geometry as? SCNPlane else {
return
}

DispatchQueue.main.async {
planeGeometry.width = CGFloat(planeAnchor.extent.x)
planeGeometry.height = CGFloat(planeAnchor.extent.z)
node.childNodes[0].position = SCNVector3Make(planeAnchor.center.x, 0, planeAnchor.center.z)
}
}
Download the program to the iPad
Stop the program, then untether the iPad
Run the program again
Slowly move the iPad. It should detect table tops and floors. Note that the detection is not very accurate; but keep in mind that
the CPU in your brain is a lot more capable than the one in the iPad!
Observe what happens when you drop boxes onto the surfaces or surface edges Boxes Can Be Placed On Desk /4

Show that you can place a virtual box on your desk to your instructor. You may have to (Instructor Stamp)
adjust Damping and other characteristics of the box to get this to work

8. Completion
Log out of your Mac
Delete all photos and your app from the iPad, then return the iPad
Congratulations! You have completed this lab. Be sure to hand in this lab and see you next week!

Page 49 of 56
ICS 224 Lab Manual

Lab 10: Challenge Lab


Due Date: This work must be completed by the date posted on D2L
This lab is out of 20 marks; all stamps are required to pass this lab
Goals
• Develop a simple iOS app independently

Preparation

Independent Work
Independent Work

Obtain an iPad from your instructor at the beginning of the lab

1. Creating the App


For this Lab, you must work on your own (using Apple's documentation is fine). If you ask other students or the
instructor, or if you help other students, you will lose up to 20 Marks!
Create a quiz app where:
The user can add, edit, and delete an arbitrary number of quiz questions
Questions must persist, even if the app is terminated
In quiz mode, questions are randomly given; if the answer is correct, a success screen is shown, else a correction screen is
shown
A score is displayed at the end of the quiz, but is not saved
Answers are of the fill-in-the-blank type (but it should be easy to extend to other formats, such as multiple choice)
Once the app is working, create a set of questions and answers related to Swift and
iOS frameworks
App Working /0

Show the working app to your instructor
(Instructor Stamp)
Print out a copy of your source code and your test code and attach it to this
page. Note that Appendix A will be applied
Source Code

2. Completion /20
Log out of your Mac
Delete all photos and your app from the iPad, then return the iPad
That's it! Please hand in this lab, along with a printout of your source code, and see you at the final exam!

Page 50 of 56
ICS 224 Lab Manual

Appendix A: Code Marking Scheme


All submitted code will be evaluated based on the rubric below. Evaluation of each category starts from the bottom up and stops at
the first matching level. Correctness is weighted more heavily than the other categories.

Rating Correctness/Efficiency Documentation Structure/Complexity

***** - passes all tests - well-documented, allowing another programmer to use all - well-engineered, consisting of a modular collection of simple,
perfect functions based on the header comments alone single-purpose functions

- code review reveals no faults - responsibilities of all functions are described well, without - constants are used whenever appropriate
giving implementation details

- efficient (given the requirements) - all parameters, return values, and side effects are explained - globals are not used, unless unavoidable

- no redundant operations - comments within all functions are helpful, without being - all constants and variables are named appropriately
distracting, making it easy to follow along

- no layout abnormalities (eg, missing or improper indentation)

- easily used and reused

- no undesirable side effects, such as debug output

**** - passes nearly all tests - occasionally, there are comments that are not complete, - largely well-engineered, except for a few, minor issues
good helpful, and/or true

- a code review reveals nearly no faults; faults that are found, - could be improved by slightly reworded, slightly more, or - a few, minor issues with constants, variables, or layout
are minor slightly fewer comments

- generally efficient, except in a few minor cases - easily used and reused, except in a few, minor instances

- at most a few redundancies - generally no undesirable side effects

*** - passes half the tests, or more, but the failure rate is too high - in a number of cases, comments are cryptic, false, incomplete, - in need of reengineering due to a number of issues, none, or
ok for a 4-star rating misleading, missing, or redundant almost none of which, are major

- a code review reveals a number of faults, but none, or almost - on a number of occasions, there are issues with constants,
none of them, are major variables, or layout

- somewhat efficient, but there are a small number of major - often easily used and reused, but there are a small number of
inefficiencies major problems, none of which render the work unusable

- potentially many redundancies

Page 51 of 56
ICS 224 Lab Manual

Rating Correctness/Efficiency Documentation Structure/Complexity

** - fails more than half the tests - only little relevant documentation - a number of major issues, requiring major changes
fail

- a code review reveals a high number of faults, including a - comments are often cryptic, false, incomplete, misleading, - not easily used and reused
number of major faults missing, or redundant

- not efficient, although slow progress is being made - missing printout

* - fails nearly all the tests - essentially no legitimate documentation - only a few functions, many of which are responsible for too
fail many things

- code review reveals a proliferation of major faults - essentially not usable or reusable

0 - fails all tests and/or does not run - no legitimate documentation and/or does not run - no legitimate code and/or does not run
fail

Correctness/Efficiency: ______/ 5 x 2 = ______/ 10


Documentation: ______/ 5
Structure/Complexity: ______/ 5
Total: ______/ 20 (this ratio will be used to determine the applicable point score)

Page 52 of 56
ICS 224 Lab Manual

Appendix B: Xcode Icons


Index

Button Name Location Button Name Location

Active Scheme Top Panel (Left) Node Inspector Right Sidebar (Scene Only)

Add New Constraints Storyboard Bottom Panel (Right) Play Top Panel (Left)

Align Storyboard Bottom Panel (Right) Project Navigator Left Sidebar

Assistant Editor Top Panel (Right) in Mojave, Quick Help Inspector Right Sidebar
Right Sidebar in Catalina
Size Inspector Right Sidebar
Attributes Inspector Right Sidebar
Source Control Navigator Left Sidebar
Breakpoint Navigator Left Sidebar
Standard Editor Top Panel (Right)
Continue Program Execution Debug Area Top Panel (Left)
Step Over Debug Area Top Panel (Left)
Debug Navigator Left Sidebar
Stop Top Panel (Left)
Embed Storyboard Bottom Panel (Right)
Test Navigator Left Sidebar
Exit Segue Segues (Above View Controllers
in Storyboard) Version Editor Top Panel (Right)
Find Navigator Left Sidebar

Hide or Show the Debug Area Top Panel (Right)

Identity Inspector Right Sidebar

Issue Navigator Left Sidebar

Library Top Panel (Right)

Page 53 of 56
Project
Navigator Library Play
Source Control
Navigator
Standard

Left Sidebar
Editor Stop
.
Top Panel (Left)

Assistant

Top Panel (Right)


Find Navigator Editor

Issue Version Editor


Navigator

Test Navigator Active Scheme


.
Debug
Navigator
Hide or Show the
Debug Area
Breakpoint
Navigator
.
.

Library
macOS Mojave (left) and macOS Catalina (right) appear slightly different

Version Editor

Page 54 of 56
In Mojave, the Library button is only visible if a story board has been selected
ICS 224 Lab Manual

Hide or Show the


Debug Area

.
ICS 224 Lab Manual

Right Sidebar
.

Quick Help
Inspector

Identity
Inspector

Attributes
Inspector

Size Inspector

Editor Options
(incl. Assistant)

Quick Help
Inspector
Identity
Inspector
Attributes
Inspector

Size Inspector

.
macOS Mojave (left) and macOS Catalina (right) appear slightly different
In macOS Catalina, the Editor Options button is only visible if the storyboard is selected; it will then provide an option to set the
Assistant view

Right Sidebar (Scene Only)


.

Quick Help
Inspector

Node Inspector

Attributes
Inspector

Storyboard Bottom Panel (Right)


.

Embed

Align

Add New
Constraints

Align

Add New
Constraints

Embed

macOS Mojave (left) and macOS Catalina (right) appear slightly different

Page 55 of 56
ICS 224 Lab Manual

Debug Area Top Panel (Left)


.

Continue Program
Execution

Step Over

.
Segues (Above View Controllers in Storyboard)
.

Exit Segue

Page 56 of 56

You might also like