A4: ChefOS - SwiftUI
Original Author: Vin Bui
Midpoint Due: Wednesday April 23, 2025 11:59 pm Final Due: Friday April 25, 2025 11:59 pm
Overview
In this assignment, you will be creating a recipe book app. You will be able to fetch recipes from a server, filter by category, and save them for later!
Learning Objectives
Developer Skills
How to organize your project directory
How to use Postman to test HTTP requests
How to read data received from the backend to structure frontend code
How to work with Git and GitHub for version control
How to read documentation from outside resources
How to format and structure your code to follow MVC design pattern
How to follow common styling conventions used in industry
How to implement designs created on Figma
Course Material
How to set up multiple collection views and communicate between them in SwiftUI
How to filter data using higher order functions
How to save data locally using
UserDefaults
How to represent lists of data using a
UICollectionView
and aUICollectionViewCell
How to send GET requests to a backend API using Alamofire
How to write callbacks (completion handlers) to handle asynchronous calls
How to create a
NetworkManager
singleton class to contain network callsHow to decode a JSON using a
JSONDecoder
in SwiftHow to handle errors with networking calls
Academic Integrity
This assignment can be done with ONE partner. You are also free to come to the instructors or any course staff for help. Programming forums like Stack Overflow or Hacking with Swift are allowed as long as you understand the code and are not copying it exactly. The majority of code (excluding external libraries) must be written by you or your partner. Code written through AI means such as ChatGPT is NOT ALLOWED. However, you may use these resources for assistance, although we highly encourage consulting Ed Discussion or office hours instead.
Getting Help
Grading Rubric
UI
: implements the user interfaceF
: implements the functionalityEC
: extra credit
PART I: Recipe CollectionView
_ / 4
UI: Name, Image, Time, Rating
_ / 2
UI: 2 columns, Dynamic number of cells (adding a new item to the array creates a new item/cell)
_ / 1
UI: Each cell is unique and represents a different Recipe
_ / 1
PART II: Detailed Recipe View
_ / 3
UI: Image
_ / 1
UI: Name and Description
_ / 1
F: Tapping on a Recipe cell pushes a detailed view
_ / 1
PART III: Filtering
_ / 3
UI: Collection view for filters WITH horizontal scrolling
_ / 1
UI: Selected filter is highlighted (separate from functionality)
_ / 1
F: Tapping on a filter filters the recipe data (one at at time; stacking filters is extra credit)
_ / 1
PART IV: Fetching Recipes
_ / 1
F: GET Request to Fetch Recipes
_ / 1
PART V: Bookmark Recipes
_ / 2
F: Bookmarking from the detailed view updates the collection view using delegation
_ / 1
F: Saved recipes are stored locally via UserDefaults (restart app to check)
_ / 1
OTHER
_ / 2
Feedback Survey
_ / 1
onAppear
calls networking functions
_ / 1
SUBTOTAL
_ / 15
EC: Custom back button
+ 1
EC: Stacking filters
+ 1
EC: Nesting collection views
+ 1
EC: Separate bookmark page
+ 2
Deduction: Crash Tax
-1 point
GRAND TOTAL
_ / 15 (+5)
Getting Started
Using Postman
Using Figma
Creating a new SwiftUI Project
Go to Xcode -> File -> New -> Project -> App -> [MAKE SURE THAT Interface = SwiftUI] and just proceed from there via clicking Next and Create.
Using Git
If you are having trouble understanding how we will be using Git in this course, please read the A1 handout under Understanding Git and GitHub section, or visit office hours so we can assist you. As a reminder:
Stage:
git add .
Commit:
git commit -m "YOUR MESSAGE HERE"
Push:
git push
Pushing to the Repository
Navigate to a folder on your device where you will keep all of your assignments. You can navigate to the folder using cd
in Terminal.
Clone the repository on GitHub:
Replace NETID with your NetID
Replace SEM with the semester (such as
fa23
orsp24
)
If you have a partner, replace NETID1 and NETID2. Try changing the order if the former does not work.
If you are lost or getting any kind of error, create a post on Ed Discussion or come to office hours.
Assignment Files
Color File
You may find the following code helpful to put into a Color.swift
file, as it will allow for you to input the hex values of colors you have from Figma into Swift directly.
You will now be able to make colors via the following syntax. Note that the "0x" at the beginning is telling Swift that the proceeding values should be interpreted in hexadecimal base.
External Libraries
You will need to import Alamofire. Please import this via Cocoapods or SwiftPackageManager (SPM). You should be able to figure out how to install via Cocoapods via previous projects, but for SPM, here are the instructions!
XCode -> File -> Add Package Dependencies
Look up Alamofire -> Add Package
Click on the Project Settings Page (the very first icon in your file navigation bar, on the top left)
Click on General on the top bar of the newly opened project settings page
Scroll down to Frameworks, Libraries, and Embedded Content and add Alamofire.
You're ready to import Alamofire into your code and write some solid networking code!
Part I: Recipe CollectionView
Notice that on Figma, the screens are broken down into the different stages you need to implement.
Your first task is to create a "Collection View" to display the recipes. We encourage using the textbook and the internet for syntax and modifier help! We will not guide you as much as we did with the other assignments, but here is a general blueprint for what to do:
Make a RecipeCell view in a new file called
RecipeCell.swift
Set up a preview if you'd like!
Add a property to the view called
recipe
which should allow for you to input any recipe you want from your dummy data (so your string should be using recipe.[property])
Unlike a normal VStack, we need to initialize columns and input that as a parameter, so make sure to do that
After that, just write a ForEach inside of the LazyVGrid. You might need to make sure that your Recipe struct conforms to certain things before you can proceed with this, but XCode should tell you exactly what you need to do in this regard!
Wrap your LazyVGrid with a ScrollView
Wrap your LazyVGrid + ScrollView component in a VStack, and add some text right above it in the VStack that says "ChefOS" just how the Figma has it (so the scroll only scrolls through the items, not the ChefOS title)
Make your your styling matches the Figma!
Side notes:
You are not required to implement the bookmark icon until Part V, but you are free to do so now.
You will not be implementing the filters until Part III.
You do not have to worry about dynamic cell size. Set the text labels’ line limit to
2
lines and the height of the cell to around216
. The width, however, will depend on the size of the screen. Remember, we want to have two columns. Hint: We can multiply/divide the screen’s width by a certain factor.Do not save recipe images in the Assets catalog. You must use AsyncImage for displaying these images
Once you are done, stage, commit, and push to GitHub.
Part II: Detailed Recipe View
Your task is to create a view controller representing a detailed recipe view. You will push this view controller when tapping on the collection view cell. This detailed view will be unique to the recipe.
Detailed Recipe Page
This is very straight-forward and there aren’t any tricks. Make a new view called RecipePage.swift
and implement what you see on Figma. Make sure that recipe is a property of the view (like before) so that you can input the recipe you want as a parameter.
Remember to use AsyncImage to download the images (feel free to copy and paste).
Navigating to the Detailed Recipe Page
This is super simple: first, wrap your ENTIRE ContentView in a NavigationView. Then, wrap each of your RecipeCells in the ForEach from before with NavigationLink, with a destination parameter of RecipePage(recipe: recipe). Feel free to refer back to SwiftUI II's lecture for a code snippet on this.
Once you are done, stage, commit, and push to GitHub.
Part III: Filtering
Your task is to create a horizontally scrolling collection view that represents the filter pills as well as adding filtering functionality.
Here is a quick demo of what we’re expecting:
Adding a Filter Collection View
In UIKit, this would be very difficult. Thankfully for you, you're coding in SwiftUI!
Make a selectedDifficulty property in ContentView and make a difficulties property in ContentView. Feel free to copy this in, as long as you understand why we will need the @State. Your default selectedDifficulty should be "All"
Make a ScrollView containing a HStack between the ChefOS title and your recipe cells. The following shows how to allow for horizontal scrolling.
Make a ForEach in this HStack that looks through difficulties. You will need to set it up with the id parameter as well, since difficulties doesn't conform to identifiable.
Make these little capsule filter buttons inside the for each. One way to do this is by making each of them a Button. The code / function that the Button actually calls should be as easy as setting selectedDifficulty = Difficulty. Then, make the label a Text component that uses the .background() modifier with a capsule inside
Filtering Logic
You do not need to handle filter stacking. This is somewhat advanced so we will leave that for extra credit.
There are many ways to determine if a cell is selected, so I will leave this up to you to decide. Make sure that the currently selected tab is highlighted with a white text color. If you are lost and have no idea where to start, feel free to ask on Ed Discussion or come to office hours.
To filter the array of recipes, you can use the
filter
higher order function. Feel free to look up documentation for this, or just check out A1.
Once you are done, stage, commit, and push to GitHub.
✋🏻 This is the stopping point for the midpoint submission. We will grade you for completion based on your GitHub commit history.
No further action is required, but if you would like for us to read over it, create an Ed Discussion post. Otherwise, you can keep working.
Part IV: Fetching Recipes
Your task is to create a GET request to fetch all recipes from this API:
You can use Postman to test the HTTP request. You will need to create a NetworkManager
class with a shared
singleton instance. You will be using Alamofire so make sure to import this library. See the lecture, textbook, or A3 for reference.
Error handling is not required but is nice to have. You will know if you integrated it correctly if there are more recipes than the dummy data. As a reminder, the JSON uses snake_case but Swift uses camelCase.
Networking is one of the most important but difficult concepts to learn and implement. We want you to get as much practice as you can to prepare you for the Hack Challenge. If you are confused, please create a post on Ed Discussion or visit office hours.
One caveat for SwiftUI is that you will need to call your fetch function (e.g. NetworkManager.fetchRecipes()) inside of a .onAppear modifier instead of viewDidLoad() like in UIKit
Make sure that filtering still works properly!
Once you are done, stage, commit, and push to GitHub.
Part V: Bookmark Recipes
Your task is to implement bookmarking functionality for these recipes. You will need a way to keep track of bookmarked recipes to save them locally via UserDefaults.
First, figure out what data structure you will use to keep track of bookmarked recipes. Then, think of a key that you will use to access through UserDefaults.
Recipes that are bookmarked should have a bookmark icon in their cell. See the Figma for UI details.
You will need to create a
UIBarButtonItem
to represent the bookmark button. This button will be in the detailed recipe view on the top right corner. If the recipe is already saved, the bookmark button will be filled and tapping on it will remove it from the saved recipes.The bookmark icon should change immediately on press. You will also need to use delegation to reload the recipe collection view so that the cells will be properly updated. Remember to use a weak reference!
All saved recipes should be stored locally. You can check by restarting the app. If the saved recipes do not reset, then you are good to go.
Here is a quick demo of what we are looking for:
Once you are done, stage, commit, and push to GitHub.
✋🏻 If you reach this point, you are done with the assignment. However, feel free to challenge yourself with the extra credit features.
Extra Credit
Extra credit will only be given if the features are fully implemented. These are unordered and you can choose as many as you like.
1: Custom Back Button (+1 pt)
2: Stacking Filters (+1)
Right now, you can only select one filter at a time. Your task is to allow for filter stacking. All selected filters should be highlighted and the collection view should contain all selected filters.
3: Nesting CollectionViews (+1)
Right now, you have two separate collection views: one for the filters and the other for the recipes. Because these collection views have different scrolling directions, if we wanted to make them both scrollable vertically, then we will have to nest collection views. Your task here is to nest collection views so that the filters scroll with the recipes. In other words, if I scroll up, the filters should scroll up as well while maintaining its horizontal scrolling attribute.
4: Separate Bookmark Page (+2)
Your task here is to create a page listing out all bookmarked recipes. The design is up to your creativity, but there needs to be some way to push the detailed recipe view where you can then bookmark/unbookmark. You may also need to use delegation to update this bookmark list, similar to what you did in Part V.
Once you are done, stage, commit, and push to GitHub.
Submission
Double check that all of your files are properly pushed to GitHub.
Clone your repository into a separate folder on your local computer drive.
Run your project and make sure that your code does not crash and everything works as needed.
If you are satisfied, download this TXT file and fill it out. Make sure to use the Clone SSH path.
Last updated
Was this helpful?