← All projects
Personal project

Restaurant App

A full-stack restaurant platform I built with a shared backend powering both a web dashboard and an iOS app for order tracking and analytics.

Last updated October 2026

Overview

To contribute to my parent's new and growing restaurant business, I built the backend, a staff dashboard, and a native iOS app, choosing one REST API as the shared source for business logic and data. The system supports order tracking and analytics, with historical orders feeding seven-day demand forecasts.

What it is

Staff use the web dashboard to manage and track orders and review business analytics. Customers use the iOS app to browse the menu, build a cart, check out, and review order history.

The clients share the same backend, which handles restaurant workflows and keeps order data consistent across the dashboard and app.

Why I built it

I wanted a project that forced me to think beyond a single screen or a single language. Restaurants have a natural split between operations (staff) and customers (mobile), which made it a good domain for practicing API-first design.

Building the backend, web client, and iOS app as one system taught me where complexity actually lives in full-stack work—usually at the boundaries between layers while contributing something to my parent's new and growing restaurant business.

Architecture

The backend is a Flask-powered REST API that owns all business logic, validation, and data storage with SQLite. Neither the web dashboard nor the iOS app talks to the database directly—everything flows through the API.

That separation made it possible to develop and test each piece independently, and it mirrors how production teams structure multi-client products.

Architecture diagram: the customer iOS app and staff web dashboard share a Flask REST API backed by SQLite, while historical orders feed ARIMA demand forecasts for staff.
Take a closer look
One API serves both clients; historical orders feed demand forecasts for staff.

What I worked through

On the backend, I modeled restaurant workflows in SQLite and built Flask endpoints for order processing, menu management, and business analytics. The iOS client calls the same API through native Swift with async/await, using SwiftUI views for the menu, cart, checkout with MapKit location, and order history.

I also integrated statistical forecasting into the analytics pipeline: ARIMA models trained on historical orders to predict 7-day demand, with accuracy metrics validated against actuals. Getting three layers—backend, web dashboard, and mobile—to agree on data shape, error handling, and edge cases was as valuable as any individual feature.

Lessons learned

The biggest lesson was where complexity actually lives in full-stack work. It is rarely in a single feature—it is at the boundaries between layers. Getting a web client and an iOS app to agree on the same API contract taught me to think about data shape and error handling as shared responsibilities, not backend-only concerns.

I also learned the value of API-first design as a development strategy, not just an architecture pattern. Defining the contract before building clients meant I could work on each layer independently and have confidence they would integrate cleanly. That discipline pays for itself the moment requirements change.

Highlights

  • Designed an API-first architecture where a single REST backend serves 2 client platforms (web dashboard and native iOS)
  • Built a REST API with validation, business logic, and persistent storage for order and analytics data
  • Delivered a web dashboard giving staff real-time visibility into orders and business performance metrics
  • Developed a native iOS application integrated with the shared backend for the customer-facing experience
  • Practiced cross-layer problem solving by aligning data models, API contracts, and client behavior across 3 codebases

Code

MapKit Delivery ETA with Automatic Address Geocoding

swift
import MapKit
import CoreLocation

class CheckoutLocationManager: NSObject,
    ObservableObject, CLLocationManagerDelegate {

    private let manager = CLLocationManager()
    @Published var location: CLLocation?
    @Published var placemark: CLPlacemark?
    @Published var deliveryETA: TimeInterval?

    private let restaurantCoord = CLLocationCoordinate2D(
        latitude: 37.7749, longitude: -122.4194)

    override init() {
        super.init()
        manager.delegate = self
        manager.requestWhenInUseAuthorization()
        manager.startUpdatingLocation()
    }

    func locationManager(_ manager: CLLocationManager,
        didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.first else { return }
        self.location = location

        // Reverse geocode: turn lat/lng into a street address
        CLGeocoder().reverseGeocodeLocation(location) {
            placemarks, _ in
            self.placemark = placemarks?.first
        }

        // Estimate driving time from restaurant to user
        let request = MKDirections.Request()
        request.source = MKMapItem(
            placemark: MKPlacemark(coordinate: restaurantCoord))
        request.destination = MKMapItem(
            placemark: MKPlacemark(coordinate: location.coordinate))
        request.transportType = .automobile

        MKDirections(request: request).calculate {
            response, _ in
            if let route = response?.routes.first {
                DispatchQueue.main.async {
                    self.deliveryETA = route.expectedTravelTime
                }
            }
        }

        // Distance-based delivery fee
        let dest = CLLocation(latitude: location.coordinate.latitude,
            longitude: location.coordinate.longitude)
        let rest = CLLocation(latitude: restaurantCoord.latitude,
            longitude: restaurantCoord.longitude)
        let meters = dest.distance(from: rest)
        let miles = meters / 1609.34
        self.deliveryFee = miles > 5
            ? 7.99 : miles > 2 ? 3.99 : 0
    }
}

The checkout screen automatically detects the user's location, reverse-geocodes their coordinates into a street address, calculates the driving distance from the restaurant using MKDirections with real routing (not a straight-line approximation), estimates ETA, and sets a distance-based delivery fee — all before the user taps a single field.

Skills & Technologies

PythonFlaskSwiftSwiftUISQLitePandasNumPyscikit-learnTime-Series ForecastingREST APIsHTMLCSSJavaScriptWeb DashboardiOS Development

Glossary

Flask
A lightweight Python framework for building web applications and APIs.
Pandas
A Python library for organizing, cleaning, and analyzing tabular data.
REST APIs
Web interfaces that let software exchange data through standard requests, such as reading or updating a resource.
scikit-learn
A Python library for training and evaluating machine-learning models.
SwiftUI
Apple’s framework for describing and building app interfaces in Swift.
Time-Series Forecasting
Predicting future values from observations recorded over time, such as daily sales.
See more glossary terms