Recipe App – Code Kit Documentation

Hey there! This project contains the source code of the recipe app. Feel free to use the code as a template for your own project, or to modify the source files as a way to experiment and further your learning.

Reading code written by other people is also one of the most important skills to learn as a developer. I hope that by reading this code, you improve your own understanding of Swift and SwiftUI.

This documentation contains a simple yet detailed explaination of the project, but should you have any questions regarding the code and stuff, please feel free to post it up on our forums.

Folder and File Structure

File organization is one of the most important aspects of a project, as it would affect how developers locate and navigate files during development. This is mostly beneficial to projects that involve multiple developers.

How it affects your projects

File organization affects two factors of your project:

  1. Scalability – the ability to scale your app from small to large over time, and/or vice versa. High scalability means it would be easier to add or to remove files from your project.
  2. Maintainability – the ability for developers who were not original contributors to read, work, or contribute to your project. This is very important for projects that involve a team or teams of developers. High maintainability means it’s easy to onboard new developers to this project, without or with lessen efforts from active contributors.

File organization schemes

The most common organization schemes for organizing project files are:

  1. Grouping by type – Files are grouped by type: Views, ViewModels, Models (MVVM projects), Services, Assets, and so on…
  2. Grouping by feature – Files are grouped by feature: Splash, Login, Registration, Home feed, Profile, Settings…

Screen Shot 2022-04-11 at 11 34 21 PM

On this project, files are organized following a combination of both grouping schemes:

  1. Feature-specific files are grouped together. This approach permits scalability. When adding a new feature, files that are created to build on that feature reside on a single parent group (folder). When removing a feature, you can simply remove the parent group of that feature, and all of the files associated with that feature will be removed as well. This is in contrast to grouping by type, as you need to manually determine which files are affected when you remove a certain feature.
  2. Files that are shared among different features, such as a model, are grouped by type. In some projects, files that are shared among different features are called common files. Common files are ideally grouped by type as it would be easier to locate, and puts safety when removing files from an obsolete feature.

To check more on how the project is organized, I suggest that you open up the project, and open the project navigator by pressing Cmd+1 in Xcode.

How to Change Screen Headings and Subheadings

In order to change the headings, subheadings, as well as the values for the tab view – you need to open up the project’s source code, starting with the app declaration: Recipe_List_App.swift. The app file contains the declaration on which view to show first, so checking the app file is a great starting point when exploring an unfamiliar project codebase.

The Tab View

The app starts with RecipeTabView as its top level screen (as declared in Recipe_List_App.swift).

If you happen to run the project on device or in simulator, or check the app’s behaviour via SwiftUI preview, you can see that the app contains three tabs: the first being the tab for featured recipes, the second for the list of categories, and the third for the list of recipes.

If you need to change the values of the tab themselves (such as the tab image, or the tab title), you can do so by opening RecipeTabViewModel.swift where the images and the text values of each tabs are set.

Simulator Screen Shot - iPhone 13 mini - 2022-04-24 at 23 09 53

If you need to add additional tabs, you can simply add new text and image values in the view model (RecipeTabViewModel), then add the new view declaration inside the TabView of RecipeTabView. Make sure to add a new tag in Constants.swift as well to handle the tab selection.

Heading and Subheading Titles

Each inner tab screens show their own navigation title, indicated by the large text at the top left of each contained views.

To change the title, you need to open up the view file of the screen you need the heading title to change. The view files of each tab are: RecipeFeaturedView.swift, RecipeCategoryView.swift, and RecipeListView.swift respectively.

Heading titles are declared as values for NavigationTitleText a custom view which works like a Text, but contains modifiers to make it look like a navigation title. This is not to be confused with NavigationView.navigationTitle(...) though, as NavigationTitleText is a simple View, and not necessarily contained in a NavigationView heirarchy.

Support for Localization

If you need to support for Localization, make sure to add the string literal in Localizable.swift. Here is a medium article describing the entire process of adding a new language, (only when you need to).

How to Add/Modify Recipes

The project does not interact with any third-party API, so there are no networking calls anywhere in the app. All recipes are stored in a local JSON file in Data/recipes.json.

About the JSON Data File

JSON stands for Javascript Object Notation, and it is a widely used standard for data representation. It is a format used when communicating data in between different systems such as the API server and the app.

Don’t be afraid if you haven’t worked with JSON before. Simply think of your data, in our case, the recipes that we’ll use in the app. Recipes are shown in a lot of our screens, so we will mainly deal with recipes as our data. To check on what recipes are included in our data, open Data/recipes.json in Xcode.

One of the recipes included in the JSON data file is the Eggplant Parmesan which is an Italian recipe.

    {
        "name": "Eggplant Parmesan",
        "category": "Italian",
        "featured": false,
        "image" : "eggplant parmesan",
        "description": "Baked eggplant with bread crumbs and lots of cheese. Delicious!",
        "prepTime": "25 minutes",
        "cookTime": "35 minutes",
        "totalTime": "1 hour",
        "servings": 10,
        "highlights": [
            "Vegetarian",
            "Healthy",
            "Easy clean-up"
        ],
        "ingredients": [
            {"name": "Eggplant, peeled and thinly sliced", "num": 3},
            {"name": "Large eggs", "num": 2},
            {"name": "Italian seasoned bread crumbs", "num": 4, "unit": "cup"},
            {"name": "Spaghetti sauce", "num": 6, "unit": "cup"},
            {"name": "Mozarella cheese, shredded", "num": 2, "unit": "cup"},
            {"name": "Parmesan cheese, grated", "num": 1, "denom": 2, "unit": "cup"},
            {"name": "Dried basil", "num": 1, "denom": 2, "unit": "teaspoon"}
        ],
        "directions": [
            "Preheat oven to 350 degrees F (175 degrees C).",
            "Dip eggplant slices in egg, then in bread crumbs. Place in a single layer on a baking sheet. Bake in preheated oven for 5 minutes on each side.",
            "In a 9x13 inch baking dish spread spaghetti sauce to cover the bottom. Place a layer of eggplant slices in the sauce. Sprinkle with mozzarella and Parmesan cheeses. Repeat with remaining ingredients, ending with the cheeses. Sprinkle basil on top.",
            "Bake in preheated oven for 35 minutes, or until golden brown."
        ]
    }

You can add or remove recipe data from the JSON file as you may wish. You just need to modify the JSON data file, and re-run the app.

If you’re having difficulty with reading the JSON data file, you may want to copy the entire JSON text, and paste it to an online JSON formatting tool like this one.

IMPORTANT: If you’re editing the JSON file, it would be safer to edit it using the online JSON formatter tool above. The formatter tool will validate the JSON syntax after modification, so it’s generally safer than editing it directly from Xcode without syntax guidance. Though, there is nothing stopping you from directly editing the JSON data file in Xcode, only do it when you’re comfortable working with JSON.

Adding Recipe Images

The JSON file contains the image name of the asset to use using the recipe. So in order to add a new recipe image, you first need to add a new image asset file in Assets/Assets.xcasset. After which, you need to make sure that you use the name of the image from Assets/Assets.xcasset as the name of the image property of the new recipe to add in your Data/recipes.json.

For example, the JSON data file contains the data of the Eggplant Parmesan italian recipe which uses an image named eggplant parmesan from our Assets/Assets.xcassets.

    {
        "name": "Eggplant Parmesan",
        "category": "Italian",
        ...
        "image" : "eggplant parmesan",
        ...
    }

Screen Shot 2022-04-25 at 1 46 04 AM

How to Add/Edit Recipe Categories

The recipe categories are based on the category field of each recipe from the JSON data file. In order to add or edit a category you can simply go through each of the recipe data in the JSON file, and look for the category field.

    {
        "name": "Eggplant Parmesan",
        "category": "Italian",
        ...
    },
    {
        "name": "Mushroom Risotto",
        "category": "Italian",
        ...
    }

The actual logic of getting all categories from the JSON data file occurs in RecipeCategoryViewModel.swift.

How to Add a Category Image

Adding images to a category is somehow similar to how images are being added for recipes. If you add a new category, you need to also add an image in Assets/Assets.xcassets with a similar name but in lowercase.

For example, if we have the Italian recipe category from our JSON data file, then we also need to include an image named italian in our Assets/Assets.xcassets.

IMPORTANT: The image name must be the lowercase of the recipe category name. Following the example above, we have Italian as the recipe category name, and italian as the image name in Assets/Assets.xcassets.

    {
        "name": "Eggplant Parmesan",
        "category": "Italian",
        ...
    }

Screen Shot 2022-04-25 at 11 26 45 PM

How to Add a Recipe to a Category

To add a new recipe to a category, you can simply add the recipe as a JSON object inside the JSON data file in Data/recipes.json.

    {
        "name": "Doner Kebab",
        "category": "Turkish",
        ...
    },
    {
        "name": "Katsu Curry",
        "category": "Japanese",
    }

Copyright © 2022 CodeWithChris. All rights reserved.