Swift Project – Food Delivery App

FREE Online Courses: Dive into Knowledge for Free. Learn More!

This project will guide you in developing a basic food delivery application using SwiftUI. With this app, users can easily peruse a selection of fast food items, search for fast food items, add them to a cart, and order their desired choices. The order data will be stored in Firebase Firestore. By following this swift food delivery app project, you will gain hands-on experience in building a basic iOS app with SwiftUI and integrating it with Firebase.

About Food Delivery App using Swift

The aim of this swift food delivery app project is to build a food delivery application using SwiftUI and Firebase that empowers users to explore various food items, add them to their cart, and seamlessly place orders.

Prerequisites for Swift Food Delivery App Project

To follow along with this project, you should have the following:

  • Basic knowledge of SwiftUI and iOS app development
  • Xcode installed on your Mac
  • An active Firebase account

Download Swift Food Delivery App Project

Please download the source code of the Swift Food Delivery App Project: Swift Food Delivery App Project Code.

Steps to Create a Food Delivery App Using Swift

Step 1: Launch Xcode and initiate a new SwiftUI project.

Step 2: Set Up Firebase Firestore

Step 3: Create a FoodItem Model to store Food Items.

Step 4: Create a Location Manager to get the user’s location

Step 5: Designing the FoodItemRow view.

Step 6: Designing the CartItemRow view.

Step 7: Designing the FoodtemDetail view.

Step 8: Create a Cart Manager to manage the user’s items in the Cart

Step 9: Connecting Views in ContentView

Step 1: Launch Xcode and initiate a new SwiftUI project.

a. Click on the “Create a new Xcode Project” option after opening Xcode.

create a xcode project

b. Now select platform as “iOS” and application type as “App”.

select platform ios

c. Now, Enter the name of the app, and organization identifier, and select SwiftUI interface for building the UI of the app. Also, select Swift as a language for creating the app.

name of the app

d. Select the folder where you want to save the app and click on Create.

e. Now your project is ready for development, and you will see something like below.

select the folder

Step 2: Set Up Firebase Firestore

a. Go to the Firebase console (https://console.firebase.google.com/) and create a new project.

go to the firebase

b. After adding a new project in the Firebase, the overview page for your project will open. Select the iOS platform for adding Firebase to your app.

ios platform

c. Complete All the Steps in the order it guides you to complete

guides you to complete

d. After Completing all the tasks, your project in Xcode will look like this.

project in xcode

e. Enable Firebase Firestore for your project by selecting the Firestore Database in the sidebar, then following the guide. Now your database will be created and will look like below.

firestore database

Step 3: Create a FoodItem Model to store Food Items.

a. Create a new swift file called “FoodItem.swift”.
b. It includes properties such as name, description, price, and image to store the food item.
c. It adopts Identifiable, Decodable, and Equatable protocols.

import Foundation


struct FoodItem: Identifiable, Decodable, Equatable {
    let id = UUID()
    let name: String
    let description: String
    let price: Int
    let image: String
}

Step 4: Create a Location Manager to get the user’s location

a. Create a new swift file called “LocationManager.swift”.
b. Inside this file, the LocationManager class is created to manage location-related functionality.
c. It adopts the ObservableObject and CLLocationManagerDelegate protocols.
d. The latitude and longitude properties hold the user’s current location.
e. In the init method, the location manager is set up, and the user’s location is continuously updated.
f. The locationManager(_:didUpdateLocations:) method updates the latitude and longitude properties when a new location is received.

import Foundation
import CoreLocation


class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    var latitude: Double = 0.0
    var longitude: Double = 0.0
    private let locationManager = CLLocationManager()
    override init() {
        super.init()
        self.locationManager.delegate = self
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.startUpdatingLocation()
        self.locationManager.requestAlwaysAuthorization()
    }
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        latitude = location.coordinate.latitude
        longitude = location.coordinate.longitude
    }
}

Step 5: Designing the FoodItemRow view.

This view will display a single food item in a list format, including the image, name, and price.
a. Create a new swift view file called “FoodItemRow.swift”.
b. Define FoodItemRow Struct view.
c. It displays an image, name, and price of a food item in a vertical stack.
d. The image is loaded from a URL, and the view is designed to be resizable and have a corner radius. We have used the CachedAsyncImage library to cache the images from the internet.

import Foundation
import SwiftUI
import CachedAsyncImage

struct FoodItemRow: View {
    let item: FoodItem
    
    var body: some View {
        VStack {
            Spacer(minLength: 7)


            CachedAsyncImage(url: URL(string: item.image),transaction: Transaction(animation: .easeInOut)) { phase in
                if let image = phase.image {
                    image
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: .infinity, height: .infinity, alignment: .center)
                        .cornerRadius(10)
                } else {
                    HStack {
                        Spacer()
                        Image(systemName: "photo")
                            .imageScale(.large)
                        Spacer()
                        // Insert your placeholder here
                    }
                }
            }


               
            Text(item.name)
                .font(.title3)
                .fontWeight(.heavy)
                .frame(alignment: .center)
            Text("₹ \(item.price)")
                .font(.headline)
            Spacer(minLength: 5)
        }
    }
}

Step 6: Designing the CartItemRow view.

a. Create a new swift view file called “CartItemRow.swift”.
b. Define CartItemRow Struct view.
c. It displays an image, name, description, and price of a food item in a horizontal stack.
The image is loaded from a URL, and the view is designed to have a fixed width and height. We have used the CachedAsyncImage library to cache the images from the internet.

import Foundation
import SwiftUI
import CachedAsyncImage


struct CartItemRow: View {
    let item: FoodItem
    let cart: Cart
    
    var body: some View {
        HStack {


            CachedAsyncImage(url: URL(string: item.image),transaction: Transaction(animation: .easeInOut)) { phase in
                if let image = phase.image {
                    image
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 80, height: 80)
                } else {
                    HStack {
                        Spacer()
                        Image(systemName: "photo")
                            .imageScale(.large)
                        Spacer()
                        // Insert your placeholder here
                    }
                }
            }


            VStack(alignment: .leading) {
                Text(item.name)
                    .font(.headline)
                Text(item.description)
                    .font(.subheadline)
                    .foregroundColor(.secondary)
            }
            Spacer()
            Text("₹ \(item.price)")
                .font(.headline)
        }
    }
}

Step 7: Designing the FoodtemDetail view.

a. Create a new swift view file called “FoodItemDetail.swift”.
b. Define FoodItemDetails Struct view.
c. It displays detailed information about a food item, including the image, name, description, and an “Add to Cart” button.
d. The image is loaded from a URL, and the view is designed with various text styles and padding. We have used the CachedAsyncImage library to cache the images from the internet.
e. The “Add to Cart” button triggers the addItem(_:) method in the Cart class when tapped.

import Foundation
import SwiftUI
import CachedAsyncImage


struct FoodItemDetail: View {
    let item: FoodItem
    let cart: Cart
    
    var body: some View {
        VStack {
            HStack{
                Spacer(minLength: 20)
                CachedAsyncImage(url: URL(string: item.image),transaction: Transaction(animation: .easeInOut)) { phase in
                    if let image = phase.image {
                        image
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: .infinity, height: .infinity, alignment: .center)
                            .cornerRadius(15)
                    } else {
                        HStack {
                            Spacer()
                            Image(systemName: "photo")
                                .imageScale(.large)
                            Spacer()
                            // Insert your placeholder here
                        }
                    }
                }
                Spacer(minLength: 20)
            }
            Text(item.name)
                .font(.title)
                .fontWeight(.bold)
                .padding(10)
            Text(item.description)
                .font(.subheadline)
                .foregroundColor(.secondary)
            Spacer()
            Button(action: {
                cart.addItem(item)
            }) {
                Text("Add to Cart (₹ \(item.price))")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .background(Color.blue)
                    .cornerRadius(50)
            }
            .padding()
        }
    }
}

Step 8: Create a Cart Manager to manage the user’s items in the Cart

a. Create a new swift view file called “Cart.swift”.
b. Create a class Cart to manage the cart items and place orders.
c. The items property is an array of FoodItem objects that represent the items in the cart.
d. The totalPrice attribute computes the overall cost of all the products present in the shopping cart.
e. The addItem(_:) method adds a food item to the cart.
f. The removeItem(at:) method removes a food item from the cart at a specific index.
g. The placeOrder() method sends the order data to Firebase Firestore.

import Foundation
import Firebase
class Cart: ObservableObject {
    @Published var items: [FoodItem] = []
    var totalPrice: Int {
        items.reduce(0) { $0 + $1.price }
    }
    
    func addItem(_ item: FoodItem) {
        items.append(item)
    }
    
    func removeItem(at index: Int) {
        items.remove(at: index)
    }
    
    func placeOrder() {
        let db = Firestore.firestore()
        
        let location = LocationManager()
        let latitude = location.latitude
        let longitude = location.longitude
        
        var order = ""
        
        for item in items {
            order += item.name+", "
        }
        
        let orderData: [String: Any] = [
            "name": "Project Gurukul",
            "location": "\(latitude), \(longitude)",
            "total_price": totalPrice,
            "order": order
            
        ]
        db.collection("orders").addDocument(data: orderData)
    }
}

Step 9: Connecting Views in ContentView

a. Open the “ContentView.swift” file.
b. Replace the default implementation of the ContentView struct with a new implementation that returns tab view with the instance of FoodItemRow and CartItemRow.
c. It uses a tab view with two tabs: one for displaying food items and another for the cart.
d. The foodItems property is an array of FoodItem objects loaded from a JSON file.
e. The cart property is an instance of the Cart class to manage the cart items.
f. In the food items tab, the foodItems array is listed using the FoodItemRow view.
g. Tapping on a food item navigates to the FoodItemDetail view.
h. In the cart tab, the cart items are listed using the CartItemRow view, and swipe-to-delete functionality is implemented.
i. There are three order buttons that show different modes of payment for the food. The options are Cash-on-Delivery, UPI and Bank Cards (i.e., Credit/Debit Cards). The user will have various choices for the mode of payment.
j. An “Order” button triggers the placeOrder() method in the Cart class, and the cart items are cleared after placing the order.

import SwiftUI
struct ContentView: View {
    let foodItems: [FoodItem] = {
        guard let url = Bundle.main.url(forResource: "food", withExtension: "json") else {
            fatalError("Failed to locate dummy.json file")
        }
        do {
            let data = try Data(contentsOf: url)
            let decoder = JSONDecoder()
            let result = try decoder.decode([String: [FoodItem]].self, from: data)
            return result["food_items"] ?? []
        } catch {
            fatalError("Failed to decode dummy.json file: \(error)")
        }
    }()
    
    @ObservedObject var cart = Cart()


    @State private var searchText = ""


    private var filteredItems: [FoodItem] {
        if searchText.isEmpty {
            return foodItems
        } else {
            return foodItems.filter { $0.name.localizedCaseInsensitiveContains(searchText) }
        }
    }
    
    var body: some View {
        TabView {
            NavigationView {
                List(foodItems) { item in
                    NavigationLink(destination: FoodItemDetail(item: item, cart: cart)) {
                        FoodItemRow(item: item)
                    }
                }
                .navigationTitle("Food Items")
                .searchable(text: $searchText) {
                    // Add searchable modifier
                    ForEach(filteredItems) { item in
                        // Use filteredItems instead of foodItems
                        NavigationLink(destination: FoodItemDetail(item: item, cart: cart)) {
                            FoodItemRow(item: item)
                        }
                    }
                }


            }
            .tabItem {
                Label("Home", systemImage: "house")
            }
            
            NavigationView {
                List {
                    ForEach(cart.items) { item in
                        CartItemRow(item: item, cart: cart)
                    }
                    .onDelete(perform: { indexSet in
                        cart.items.remove(atOffsets: indexSet)
                    })
                    Button(action: {
                        cart.placeOrder()
                        cart.items.removeAll()
                    }) {
                        
                        HStack{
                            VStack{
                                Text("Order (₹ \(cart.totalPrice) INR)")
                                    .font(.title2)
                                    .bold()
                                Text("Using cash on Delivery")
                                    .foregroundColor(.blue)
                                    .bold()
                            }
                            
                            Spacer()
                            
                            Image(uiImage: UIImage(named: "cash.png")!)
                                .resizable()
                                .scaledToFit()
                                .padding(.trailing)
                                .frame(width: 100, height:100, alignment: .trailing)
                        }
                        
                    }
                    Button(action: {
                        cart.placeOrder()
                        cart.items.removeAll()
                    }) {
                        
                        HStack{
                            VStack{
                                Text("Order (₹ \(cart.totalPrice) INR)")
                                    .font(.title2)
                                    .bold()
                                Text("Using UPI")
                                    .foregroundColor(.blue)
                                    .bold()
                            }
                            
                            Spacer()
                            
                            Image(uiImage: UIImage(named: "upi.png")!)
                                .resizable()
                                .scaledToFit()
                                .padding(.trailing)
                                .frame(width: 100, alignment: .trailing)
                        }
                        
                    }
                    Button(action: {
                        cart.placeOrder()
                        cart.items.removeAll()
                    }) {
                        
                        HStack{
                            VStack{
                                Text("Order (₹ \(cart.totalPrice) INR)")
                                    .font(.title2)
                                    .bold()
                                Text("Using Bank Cards")
                                    .foregroundColor(.blue)
                                    .bold()
                            }
                            
                            Spacer()
                            
                            Image(uiImage: UIImage(named: "card.png")!)
                                .resizable()
                                .scaledToFit()
                                .padding(.trailing)
                                .frame(width: 100, alignment: .trailing)
                        }
                    }
                    
                }
                .navigationTitle("Cart")
                .navigationBarItems(trailing: EditButton())
                .toolbar {
                    ToolbarItem(placement: .navigationBarTrailing) {
                        EditButton()
                    }
                }
                .onChange(of: cart.items) { _ in
                    cart.objectWillChange.send()
                }
                
            }
            .tabItem{
                Label("Cart", systemImage: "cart")
            }
            
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Connecting Views

Swift Food Delivery App Output:

swift-food-delivery

swift-delivery-app

swift-delivery-app project

Summary

Congratulations! You have successfully created a food delivery app using SwiftUI. Throughout this Swift Food Delivery App Project, you learned how to design views, manage data using models, implement navigation, and integrate with Firebase. The app allows users to browse food items, add them to the cart, and place orders.

However, this is just the beginning. You can continue enhancing the app by adding features like user authentication, order history, and real-time updates from Firebase. With the knowledge gained from this Swift Food Delivery App Project, you have a solid foundation to build more advanced iOS apps. Happy coding!

Leave a Reply

Your email address will not be published. Required fields are marked *