Welcome to Little Lemon, a premium iOS application developed as the final Capstone Project for the Meta iOS Developer Professional Certificate.
This app is built for a Mediterranean restaurant, featuring a dynamic food menu fetched from a remote server, Core Data persistence, custom image caching, real-time input validation, and user profile management.
The application was designed and built from initial wireframes to actual working code. Here is the visual progression of the app:
Early design wireframes outlining the user flow and structure:

A clean registration screen implementing real-time validation and persistent local login states:
The primary landing screen displaying restaurant dishes fetched from the API with clean custom styling:
A detailed profile settings card with interactive preferences, customizable avatar image picker, and local file storage persistence:
Dynamic search matching and active category filters (Starters, Mains, Desserts, Drinks) acting instantly on the Core Data store:
- Language: Swift 5.10
- UI Framework: SwiftUI
- Database & Persistence: Core Data (with SQLite back-end) &
UserDefaults - Networking:
URLSessionutilizingCodablefor JSON parsing - Image Management: Custom
NSCache-backed caching (CachedAsyncImage) to minimize network request overhead - Validation: Regular Expressions &
NSPredicate - Architecture: MVVM (Model-View-ViewModel)
- Root Navigation: Set
OnboardingViewas the root view inLemonCapstoneApp.swiftto handle incoming users. - Global Keys: Created constants
kFirstName,kLastName,kEmail, andkIsLoggedInfor robust keys referencing. - Form & Validation Logic:
- Real-time text field binding for first name, last name, and email.
- Checks that no text fields are empty.
- Verifies email formatting against standard pattern regex via
NSPredicate. - Persists session status (
isLoggedIn = true) toUserDefaultson successful registration, immediately routing the user to theHomeview. - Adds an
onAppearlifecycle check to skip the onboarding flow for users who are already logged in.
- Styling: Leveraged official brand colors: Primary Green (
#495E57) for headers/hero sections, Primary Yellow (#F4CE14) for action buttons, and Charcoal (#333333) for high-contrast typography.
- Navigation Flow: Implemented programmatic routing in
OnboardingViewusing aNavigationLinkbound to the$isLoggedInstate. - Home Structure: Created
HomeView.swiftwhich houses a Selection-boundTabViewwith two tabs:- Menu (index
0, system image"list.dash") - UserProfile (index
1, system image"square.and.pencil")
- Menu (index
- Back Button Control: Disabled the native navigation bar back button to ensure a clean, modern user experience.
- Circular Avatar: Designed a reusable
AvatarViewthat renders custom UIImages in a filled circle, falling back to a placeholder asset if no custom avatar is set. - Interactive Profile Form: Includes editable text fields for first name, last name, email, phone number, and toggles for email notification categories (Order Status, Password Changes, Special Offers, Newsletter).
- Avatar File Persistence: Custom profile pictures selected via the system
ImagePickerare saved locally in the App Documents directory as JPEGs. The path is saved underkAvatarPathso it persists between app launches. - Input Cleaning: Applied real-time filters using
.onChangeto restrict phone number inputs strictly to digits and standard phone symbols (0-9,-,(,),+). - Visual Validation Feedback: Direct, inline feedback shown via red alert messages for validation issues.
- Decodable Entities: Created
MenuItemandMenuListpayloads mapping to database entities. - Core Data Integration: Swapped default models for
ExampleDatabase.xcdatamodelddefining theDishentity (title,image,price,dishDescription,category). - Fetch Management: Fetches restaurant data asynchronously from a remote endpoint using
URLSession. It parses JSON on the main thread and writes new dishes to Core Data. - Duplicate Prevention: Before triggering a network request, checks if dishes are already present in the database to avoid redundant queries.
- Network & Image Optimization: Implemented
CachedAsyncImagepowered by a staticNSCacheinstance, downloading remote food images once and serving them instantly from memory on future requests. - Local Fallbacks: Configured local asset fallbacks (
lemon-dessertandgrilled-fish) to guarantee correct display regardless of external asset server availability.
- Core Data Predicates & Sorting:
- Implemented
buildSortDescriptors()to sort dishes alphabetically using standard localized compare. - Formulated compound predicates in
buildPredicate()combining search bar texts (title CONTAINS[cd] %@) and selected category chips (category == %@).
- Implemented
- Dynamic Category Chips: Custom category buttons (Starters, Mains, Desserts, Drinks) toggle active filters dynamically. Selected buttons display in high-contrast inverted colors.
- Hero Search Integration: Interactive search bar embedded directly inside the hero backdrop to enable instant list updates.
The project has been refactored into a clean MVVM structure:
LemonCapstone/
├── App/
│ ├── LemonCapstoneApp.swift
│ └── Assets.xcassets (moved)
├── Core/
│ ├── Persistence.swift
│ ├── FetchedObjects.swift
│ ├── Constants.swift (NEW - contains shared UserDefaultsKeys)
│ └── Components/
│ └── AvatarView.swift
└── Features/
├── Onboarding/
│ ├── Views/
│ │ └── OnboardingView.swift (renamed from Onboarding.swift)
│ └── ViewModels/
│ └── OnboardingViewModel.swift (NEW)
├── Menu/
│ ├── Views/
│ │ ├── MenuView.swift (renamed from Menu.swift)
│ │ ├── CategoryButton.swift (NEW - extracted helper view)
│ │ └── CachedAsyncImage.swift (NEW - extracted helper view/cache)
│ ├── ViewModels/
│ │ └── MenuViewModel.swift (NEW)
│ └── Models/
│ ├── MenuItem.swift
│ └── MenuList.swift
├── UserProfile/
│ ├── Views/
│ │ ├── UserProfileView.swift (renamed from UserProfile.swift)
│ │ ├── CustomTextField.swift (NEW - extracted helper view)
│ │ ├── ToggleItem.swift (NEW - extracted helper view)
│ │ └── ImagePicker.swift (NEW - extracted UIViewControllerRepresentable)
│ └── ViewModels/
│ └── UserProfileViewModel.swift (NEW)
└── Home/
└── Views/
└── HomeView.swift (renamed from Home.swift)
Follow these steps to build and run the app locally:
- A Mac running macOS Sonoma or later.
- Xcode 15 or later installed.
- An active Simulator or physical iOS device running iOS 17.0 or later.
- Clone or download the repository to your local machine.
- Open Xcode.
- Go to
File > Open...and select theLemonCapstone.xcodeprojfile located in the root of the project. - Let Xcode index the files and load the Core Data models.
- In the Scheme menu in Xcode (top toolbar), select the
LemonCapstonetarget. - Choose a target device/simulator (e.g., iPhone 17 simulator).
- Build and run by pressing
⌘ + R(or clicking the Play button in the top left). - The app will build and boot up in your selected Simulator!
Note: Code signing is pre-configured to allow local Simulator compilation without needing a developer account.



