This assignment can be completed individually or in pairs. You can use Git to collaborate if you'd like.
Author: Anthony Li
Please leave feedback by posting on Ed or contacting the course staff.
Required Software: macOS Ventura, Xcode 15
Deadline: Thursday, 10/31 @ 11:59 PM
Download GPX Files ↓ or view on GitHub
In this assignment, you'll be building a location-based scavenger hunt game centered around the food and beverage institution we all know and love: Penn Dining.
You'll use the Core Location framework to detect when the user is near a dining hall. Then, you'll use either the Core Motion framework or SwiftUI's gesture support to allow the user to "collect" a dining hall by shaking their device or scribbling on the screen.
This assignment doesn't have any starter code -- the GPX files are there to help you test.
Your game should have two screens:
Specifically, the dining hall screen should:
UIWindow.motionEnded()
will lead to reduced credit.We recommend going with the shaking method as it's more fun, but you should choose the scribble method if you can't test on a physical device (e.g. you don't have a Mac or an iPhone). Do not implement both methods - you will only receive credit for one.
You should keep track of the list of collected dining halls in a view model that is shared by both screens. Each dining hall should be represented by a model struct.
Your game should contain the following dining halls:
Coordinates for these can be found in the GPX files.
NOTE: You can just hardcode the coordinates in your app's code with our Locations.swift file!
Finally, please submit a short, 5-second video of a dining hall being collected along with your project. This will help us grade, just in case we're not sure how to trigger the collection event.
Note: You are not required to store the user's progress between app launches. We will also not prescribe a specific way you should handle errors.
If you don't have access to a physical device or don't feel like making the trek to each of the dining halls, worry not! Xcode lets you simulate your device's location using a GPX file. To use it:
Note that if you're running on a physical device, location simulation will only work while your app is actively being run from Xcode. Running your app from the home screen will use the device's actual location.
(You can also use the Features > Location menu in the Simulator, but doing it through Xcode will let you use our GPX files.)
Some brief instructions to get you started:
Start by reading through these instructions and planning out how you're going to structure your app and its screens.
In particular, think through:
When you're ready, create a new SwiftUI project. You'll most likely want to start by modeling each dining hall using a struct.
You'll want to keep track of a dining hall's name, its location, and maybe some other properties (such as an ID to make it conform to Identifiable
). Here's a starting point for your dining hall struct:
struct DiningHall: Identifiable {
var id: UUID
var name: String
var location: CLLocation
}
CLLocation represents a location -- you can use it to represent the location of each dining hall. (Don't worry about finding the altitude of each dining hall.)
Once you've made this struct, you'll want to create an array of dining halls somewhere in the app. You can hardcode this array using our provided coordinates.
Now, you'll want to create a view model to keep track of which dining halls have been collected. Ideally, this view model should be shared between the home and dining hall screens. Declare a new ObservableObject
, like this:
class DiningHallViewModel: ObservableObject {
// Add a @Published property to keep track of which dining halls have been collected
// Add methods, such as collecting and checking if a dining hall has been collected
}
You can then use @StateObject
in your App
struct to create an instance of this view model for your app. Then, use the .environmentObject
modifier to pass it to your home and dining hall screens, much like we did in lecture 5.
Now, you can start writing the home screen, using the view model and models you wrote in steps 2 and 3. You can use a simple List
view for this.
The dining hall screen is where most of the action happens. This is a pretty complicated screen, so we recommend you split out the dirty logic into a separate view model.
You'll most likely want to start with the first check: seeing if a dining hall has already been collected using the view model you wrote earlier.
CLLocationManager
and its delegateIt's time to start using the Core Location framework. Set up a CLLocationManager, and wire up one of your objects to be its delegate. To help you with this, you can consult our finished code from lecture 7, or Apple's Configuring Your App to Use Location Services tutorial.
Once you've set up your manager and delegate, go ahead and request location permissions from the user, being sure to handle the case where the user has already approved your request. Be sure to set up a purpose string!
Now that you've obtained location permissions, you can start checking if the user is within 50 meters of a given dining hall. To do this:
To test your app, you can use the location simulation steps above!
With location checking out of the way, you can now implement the logic to collect a dining hall, using either the Core Motion framework or SwiftUI's gesture support.
If you're using motion: You'll need to set up a CMMotionManager object, listen for device motion updates, then use the accelerometer data to detect a shake. For help with setting up access to motion data, you can consult our finished code from lecture 7.
If you're using gestures: You'll need to set up a gesture on your view, then use the position data you get from the onChanged
handler to determine whether the user is scribbling. You may want to add a few state variables to help you with this. For help with gesture recognition, you can check out the slides from lecture 6 or our finished code from lecture 6.
Be sure to test collecting multiple dining halls in a run. Don't forget to record a short video of a dining hall being collected!
This is a fairly complex assignment. We encourage you to come to office hours or ask on Ed if you have any questions or are running into trouble. We're here to help!
Some relevant course material:
This assignment is worth 100 points, broken down as follows:
CLLocationManager
and its delegateIf you're using Core Motion:
CMMotionManager
object and listens for device motion updatesIf you're using SwiftUI gestures:
@StateObject
, @ObservedObject
, and/or @EnvironmentObject
to share the view model between the home and dining hall screens
@Observable
, you can also use @State
, @Bindable
, and/or @Environment
General bugs (those not covered in the above deductions) will be subject to these deductions:
Bugs encountered when testing extra credits will be deducted from their respective extra credit points.
.sheet
modifier to display a message explaining why the app needs the user's location.
Before you submit, make sure you:
Once you're done with those steps, upload a zip (or .tar.gz) of your entire Xcode project, including GPX files (if any) and video to Gradescope. If you're submitting in pairs, have one person submit and add the other person as a group member.
Submit on Gradescope →