Skip to content

Yukiiyi/Recipefy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Recipefy 🍳

Turn Your Ingredients Into Delicious Meals

Recipefy is an iOS app that uses AI-powered image recognition to identify ingredients from photos and generate personalized recipe suggestions. Simply snap a photo of your fridge, pantry, or ingredients, and let Recipefy do the rest. This app is developed for 67-443 Mobile App Development at Carnegie Mellon University, Fall 2025.

Swift iOS Firebase Xcode


πŸ“Έ Screenshots

Landing Home Ingredients

Recipes Recipe Detail Settings


πŸ“± Features

Core Functionality

  • πŸ“Έ Multi-Image Scanning β€” Capture up to 5 photos per session (fridge, pantry, countertop)
  • πŸ€– AI Ingredient Recognition β€” Powered by Google's Gemini 2.5 Flash for accurate identification
  • ✏️ Ingredient Management β€” Add, edit, or delete ingredients manually after scanning using intuitive IOS UI. Or start with a blank slate and add ingredients manually.
  • 🍽️ Smart Recipe Generation β€” Get 3 unique recipes based on your available ingredients initially; request more batches as needed
  • πŸ“– Recipe Detail View β€” Tabbed interface showing Ingredients, Steps, and Nutrition breakdown
  • ❀️ Favorites β€” Save and organize your favorite recipes
  • πŸ‘€ User Profiles β€” Edit display name, email, and password; track usage stats

Dietary Preferences

  • Diet Types β€” Vegetarian, Vegan, Pescatarian, Gluten-Free, Dairy-Free, Low-Carb
  • Allergen Management β€” Peanuts, Tree Nuts, Shellfish, Fish, Eggs, Dairy, Gluten, Soy, Sesame
  • Food Dislikes β€” Exclude specific ingredients you don't enjoy
  • Cooking Time Limits β€” Set maximum preparation time for recipes

Authentication

  • Email/Password β€” Traditional account creation with password reset
  • Sign in with Apple β€” Native Apple authentication with secure nonce
  • Google Sign-In β€” OAuth-based Google account integration

πŸ› οΈ Technology Stack

Layer Technology Purpose
UI Framework SwiftUI (iOS 26) Declarative, modern iOS interface
Language Swift 5.0 Type-safe, modern programming language
Architecture MVVM + Controllers Separation of concerns, testability
AI/ML Firebase AI (Gemini 2.5 Flash) Ingredient recognition, recipe generation
Authentication Firebase Auth Multi-provider auth (Email, Apple, Google)
Database Cloud Firestore Real-time NoSQL document storage
Storage Firebase Storage Image hosting for scanned ingredients
Testing Swift Testing Framework Modern, declarative test assertions

Key Dependencies

firebase-ios-sdk 12.4.0      β€” Core Firebase services
GoogleSignIn-iOS 9.0.0       β€” Google authentication
swift-protobuf 1.32.0        β€” Protocol buffer support
CryptoKit (native)           β€” SHA256 for Apple Sign-In nonce

πŸ—οΈ Architecture & Design Decisions

High-Level Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        SwiftUI Views                         β”‚
β”‚  (HomeView, ScanView, RecipeView, SettingsView, etc.)       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚ @EnvironmentObject
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      Controllers                             β”‚
β”‚  AuthController β”‚ ScanController β”‚ IngredientController β”‚    β”‚
β”‚                 β”‚ RecipeController β”‚ NavigationState         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚ Protocol-based DI
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     Service Layer                            β”‚
β”‚  GeminiService β”‚ FirebaseFirestoreService β”‚ StorageService  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   Firebase Backend                           β”‚
β”‚  Firestore β”‚ Auth β”‚ Storage β”‚ AI (Gemini)                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Design Decisions

1. Protocol-Oriented Service Layer

All external services are abstracted behind protocols, enabling:

  • Testability β€” Mock implementations for unit testing
  • Flexibility β€” Swap implementations without changing business logic
  • Decoupling β€” Controllers don't depend on concrete Firebase classes
protocol GeminiServiceProtocol {
  func analyzeIngredients(image: UIImage) async throws -> [Ingredient]
  func getRecipe(ingredients: [String]) async throws -> [Recipe]
}

protocol FirestoreServiceProtocol {
  func saveIngredients(scanId: String, ingredients: [Ingredient]) async throws -> [Ingredient]
  func loadRecipes(userId: String) async throws -> (recipes: [Recipe], scanId: String?)
  // ... more operations
}

2. @MainActor for Thread Safety

All controllers and view-related classes are marked with @MainActor to ensure UI updates happen on the main thread:

@MainActor
final class RecipeController: ObservableObject {
  @Published var currentRecipes: [Recipe]?
  @Published var isRetrieving = false
  // ...
}

Rationale: Swift's strict concurrency model requires explicit main-thread guarantees for @Published properties. Using @MainActor at the class level eliminates race conditions and makes the code safer.

3. Environment Object State Sharing

Controllers are shared across the app using SwiftUI's environment:

@main
struct RecipefyApp: App {
  @StateObject private var authController = AuthController()
  @StateObject private var scanController = ScanController(...)
  @StateObject private var ingredientController = IngredientController(...)
  @StateObject private var recipeController = RecipeController(...)

  var body: some Scene {
    WindowGroup {
      NavigationBarView()
        .environmentObject(authController)
        .environmentObject(scanController)
        .environmentObject(ingredientController)
        .environmentObject(recipeController)
    }
  }
}

Rationale: This ensures a single source of truth across all views and tabs, enabling seamless data flow when navigating between screens.

4. Scan-to-Recipe Data Flow

The app maintains data association between scans and their generated content:

Scan (images) β†’ ScanController.currentScanId
      ↓
Ingredients β†’ IngredientController.currentScanId  
      ↓
Recipes β†’ RecipeController.lastGeneratedScanId

Rationale: This ensures recipes are never mixed from different scanning sessions, preventing confusing user experiences.

5. Robust Category Parsing

AI responses can be unpredictable. The IngredientCategory enum includes fuzzy matching:

static func from(string: String) -> IngredientCategory {
  let normalized = string.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)
  switch normalized {
  case "vegetable", "vegetables", "veggie", "veggies", "veg":
    return .vegetables
  case "protein", "proteins", "meat", "meats":
    return .proteins
  // ... more variations
  default:
    return .other
  }
}

Rationale: Gemini might return "Veggies" instead of "Vegetables". Graceful fallback prevents crashes and ensures all ingredients are categorized.

6. Parallel Image Analysis

Multiple images are analyzed concurrently using Swift's structured concurrency:

let allIngredients = try await withThrowingTaskGroup(of: [Ingredient].self) { group in
  for image in images {
    group.addTask {
      try await self.geminiService.analyzeIngredients(image: image)
    }
  }
  
  var results: [Ingredient] = []
  for try await ingredients in group {
    results.append(contentsOf: ingredients)
  }
  return results
}

Rationale: Users can scan fridge + pantry + countertop. Processing them in parallel reduces wait time from ~30s to ~10s for 3 images.

7. Dietary Preferences in AI Prompts

User preferences are injected directly into AI prompts:

func toPromptString() -> String {
  var prompt = "\n\nUSER DIETARY PREFERENCES:\n"
  
  if !dietTypes.isEmpty {
    prompt += "- Diet Types: \(dietTypes.map { $0.displayName }.joined(separator: ", "))\n"
  }
  
  if !allergies.isEmpty {
    prompt += "- ALLERGIES (CRITICAL - MUST AVOID): \(allergies.map { $0.rawValue }.joined(separator: ", "))\n"
  }
  
  prompt += "\nIMPORTANT: Generate recipes that strictly respect these dietary constraints.\n"
  return prompt
}

Rationale: This approach leverages Gemini's instruction-following capabilities rather than post-filtering recipes, resulting in more relevant suggestions.

8. Simulator Camera Support

Development on simulators is enabled through mock camera support:

if SimulatorCameraSupport.isRunningOnSimulator {
  capturedImage = SimulatorCameraSupport.generateMockIngredientImage()
  return
}

Rationale: Camera hardware is unavailable on simulators. Mock images allow full flow testing without a physical device.

9. Firestore Connection Pre-warming

Cold start latency is reduced by warming up Firestore on app launch:

func application(_ application: UIApplication, didFinishLaunchingWithOptions...) -> Bool {
  FirebaseApp.configure()
  
  // Pre-warm Firestore connection
  Task {
    let db = Firestore.firestore()
    _ = try? await db.collection("_warmup").document("ping").getDocument()
  }
  
  return true
}

Rationale: First Firestore request can take 2-3 seconds for connection setup. Pre-warming hides this latency behind the splash screen.

10. Batch Firestore Operations

Ingredients are saved using batched writes for atomicity and performance:

func saveIngredients(scanId: String, ingredients: [Ingredient]) async throws -> [Ingredient] {
  let batch = db.batch()
  
  for ingredient in ingredients {
    let docRef = ingredientsCollection.document()
    batch.setData(ingredientData, forDocument: docRef)
  }
  
  try await batch.commit()
  return ingredientsWithIds
}

Rationale: A single batch commit is faster and ensures all-or-nothing saves, preventing partial data states.

11. Batch Recipe Generation

Recipes are generated in batches of 3 to minimize wait time:

func loadMoreRecipesIfNeeded() async {
  guard !isRetrieving, !isLoadingMore, !lastFormattedIngredients.isEmpty else { return }
  
  isLoadingMore = true
  let moreRecipes = try await geminiService.getRecipe(ingredients: lastFormattedIngredients)
  currentRecipes?.append(contentsOf: moreRecipes)
  isLoadingMore = false
}

Rationale: AI recipe generation takes time. Generating 3 recipes initially provides fast results, and users who want more variety can request additional batches on demand rather than waiting upfront for a large set.

12. Manual Deduplication Over Automatic - Intentional Omission From V2

Automatic ingredient deduplication was initially planned but intentionally omitted after testing.

Rationale: Testing revealed that automatic deduplication was overly aggressive β€” unique items with that happened to be in the same scan upload were incorrectly merged. Users found it easier to quickly swipe-to-delete duplicates than to re-add ingredients that were incorrectly removed. This trade-off prioritizes data accuracy over convenience.


πŸ“‚ Project Structure

Recipefy/
β”œβ”€β”€ Recipefy/
β”‚   β”œβ”€β”€ RecipefyApp.swift              # App entry point & DI setup
β”‚   β”œβ”€β”€ AppDelegate.swift              # Firebase initialization
β”‚   β”‚
β”‚   β”œβ”€β”€ Models/
β”‚   β”‚   β”œβ”€β”€ User.swift                 # AppUser with auth providers
β”‚   β”‚   β”œβ”€β”€ Recipe.swift               # Recipe with nutrition data
β”‚   β”‚   β”œβ”€β”€ Ingredient.swift           # Ingredient with categories
β”‚   β”‚   β”œβ”€β”€ Scan.swift                 # Image scan metadata
β”‚   β”‚   β”œβ”€β”€ DietaryPreferences.swift   # Diet types, allergies, dislikes
β”‚   β”‚   └── MeasurementUnit.swift      # Standardized units
β”‚   β”‚
β”‚   β”œβ”€β”€ Controllers/
β”‚   β”‚   β”œβ”€β”€ AuthController.swift       # Authentication state & operations
β”‚   β”‚   β”œβ”€β”€ ScanController.swift       # Image capture & upload
β”‚   β”‚   β”œβ”€β”€ IngredientController.swift # AI analysis & CRUD
β”‚   β”‚   β”œβ”€β”€ RecipeController.swift     # Recipe generation & favorites
β”‚   β”‚   └── NavigationState.swift      # Tab selection state
β”‚   β”‚
β”‚   β”œβ”€β”€ Data/
β”‚   β”‚   β”œβ”€β”€ GeminiService.swift        # AI ingredient/recipe analysis
β”‚   β”‚   β”œβ”€β”€ GeminiServiceProtocol.swift
β”‚   β”‚   β”œβ”€β”€ FirebaseFirestoreService.swift
β”‚   β”‚   β”œβ”€β”€ FirestoreServiceProtocol.swift
β”‚   β”‚   β”œβ”€β”€ FirebaseStorageService.swift
β”‚   β”‚   β”œβ”€β”€ StorageService.swift       # Protocol for storage
β”‚   β”‚   β”œβ”€β”€ FirebaseScanRepository.swift
β”‚   β”‚   β”œβ”€β”€ ScanRepository.swift       # Protocol for scan persistence
β”‚   β”‚   └── FireStorePaths.swift       # Firestore path constants
β”‚   β”‚
β”‚   β”œβ”€β”€ Views/
β”‚   β”‚   β”œβ”€β”€ LandingView.swift          # Onboarding screen
β”‚   β”‚   β”œβ”€β”€ AuthView.swift             # Login/signup UI
β”‚   β”‚   β”œβ”€β”€ HomeView.swift             # Dashboard with quick actions
β”‚   β”‚   β”œβ”€β”€ NavigationBarView.swift    # Tab bar container
β”‚   β”‚   β”œβ”€β”€ ScanView.swift             # Camera interface
β”‚   β”‚   β”œβ”€β”€ ReviewScansView.swift      # Photo review before analysis
β”‚   β”‚   β”œβ”€β”€ IngredientListView.swift   # Detected ingredients
β”‚   β”‚   β”œβ”€β”€ IngredientFormView.swift   # Add/edit ingredient
β”‚   β”‚   β”œβ”€β”€ RecipeView.swift           # Recipe generation trigger
β”‚   β”‚   β”œβ”€β”€ RecipeCardsView.swift      # Swipeable recipe cards
β”‚   β”‚   β”œβ”€β”€ RecipeDetailView.swift     # Full recipe view
β”‚   β”‚   β”œβ”€β”€ FavoriteRecipesView.swift  # Saved recipes
β”‚   β”‚   β”œβ”€β”€ SettingsView.swift         # Profile & preferences
β”‚   β”‚   β”œβ”€β”€ EditProfileView.swift      # Name, email, password
β”‚   β”‚   β”œβ”€β”€ PreferencesView.swift      # Dietary settings
β”‚   β”‚   β”œβ”€β”€ EmptyStateView.swift       # Reusable empty state
β”‚   β”‚   β”œβ”€β”€ Camera/
β”‚   β”‚   β”‚   β”œβ”€β”€ CameraManager.swift    # AVFoundation wrapper
β”‚   β”‚   β”‚   β”œβ”€β”€ CameraPreviewView.swift
β”‚   β”‚   β”‚   β”œβ”€β”€ CameraOverlays.swift   # Grid overlay
β”‚   β”‚   β”‚   └── SimulatorCameraSupport.swift
β”‚   β”‚   └── ... (Help, Privacy, Terms views)
β”‚   β”‚
β”‚   β”œβ”€β”€ Helpers/
β”‚   β”‚   └── AppleSignInHelper.swift    # Nonce generation, SHA256
β”‚   β”‚
β”‚   └── Assets.xcassets/               # App icons, images
β”‚
β”œβ”€β”€ RecipefyTests/                     # Unit tests
β”‚   β”œβ”€β”€ RecipeControllerTests.swift
β”‚   β”œβ”€β”€ IngredientControllerTests.swift
β”‚   β”œβ”€β”€ AuthControllerErrorTests.swift
β”‚   β”œβ”€β”€ DietaryPreferencesTests.swift
β”‚   β”œβ”€β”€ CameraTests.swift
β”‚   └── ... (18 test files)
β”‚
└── RecipefyUITests/                   # UI tests
    β”œβ”€β”€ RecipefyUITests.swift
    └── RecipefyUITestsLaunchTests.swift

πŸš€ Getting Started

Prerequisites

  • Xcode 26.0+
  • iOS 26.0+ device or simulator
  • Firebase project with:
    • Authentication (Email, Apple, Google)
    • Cloud Firestore
    • Firebase Storage
    • Firebase AI (Gemini API enabled)

Installation

  1. Clone the repository

    git clone https://github.com/your-org/Recipefy.git
    cd Recipefy
  2. Open the Xcode project

    open Recipefy/Recipefy.xcodeproj
  3. Configure Firebase

    • Create a Firebase project at console.firebase.google.com
    • Download GoogleService-Info.plist
    • Replace the existing plist in Recipefy/Recipefy/
    • Enable Authentication providers (Email, Apple, Google)
    • Enable Cloud Firestore and Storage
    • Enable Gemini API in Firebase AI
  4. Configure Google Sign-In

    • Add the reversed client ID from GoogleService-Info.plist to URL schemes in Info.plist
  5. Configure Sign in with Apple

    • Add "Sign in with Apple" capability in Xcode
    • Configure in Apple Developer portal
  6. Build and Run

    • Select your target device or simulator
    • Press ⌘R to build and run

πŸ§ͺ Testing

The project uses Swift's modern Testing framework with 240+ unit tests across 18 test files, implementing mock-based dependency injection for fast, deterministic testing.

Testing Philosophy

Test your code, not the SDK. We trust that Firebase and Gemini work β€” our tests verify our business logic.

All tests are:

  • Fast β€” No network calls; all tests run in under 5 seconds
  • Deterministic β€” Same input always produces same output
  • Independent β€” Each test can run alone without dependencies

What IS Tested

Category Coverage Files
Models 95-100% Recipe, Ingredient, Scan, User, DietaryPreferences, MeasurementUnit
Controllers 75-85% RecipeController, IngredientController, ScanController, NavigationState
Helpers/Utils 95-100% AppleSignInHelper, FireStorePaths
View Logic 80-90% Validation logic, data formatting, share text generation
Camera State 90%+ CameraManager state machine, simulator detection

Why these are tested:

  • Models contain core business logic used throughout the app
  • Controller business logic is tested via dependency injection with mocks
  • Pure helper functions are easy to test with high value
  • View validation logic is critical for data integrity

What is NOT Tested (and Why)

Category Reason
Firebase SDK Calls Would require mocking entire Firebase SDK; slow and brittle; Firebase is already tested by Google
Gemini AI API Calls Non-deterministic AI responses; costs money per call; requires API keys; response parsing IS tested with mock data
SwiftUI View Rendering Unit tests don't render UI; this is covered by UI tests and manual QA
App Lifecycle Thin wrappers around Firebase/SwiftUI initialization; rarely changes

Dependency Injection Architecture

Controllers accept protocols, not concrete types, enabling mock injection in tests:

// Protocol defines the contract
protocol GeminiServiceProtocol {
  func analyzeIngredients(image: UIImage) async throws -> [Ingredient]
  func getRecipe(ingredients: [String]) async throws -> [Recipe]
}

// Production uses real service
let controller = RecipeController(
  geminiService: GeminiService(),           // Real AI
  firestoreService: FirebaseFirestoreService()  // Real database
)

// Tests use mock service
let controller = RecipeController(
  geminiService: MockGeminiService(),       // Returns test data
  firestoreService: MockFirestoreService()  // No network calls
)

Mock Service Example

class MockGeminiService: GeminiServiceProtocol {
  var mockRecipes: [Recipe] = []
  var shouldThrowError = false
  
  func getRecipe(ingredients: [String]) async throws -> [Recipe] {
    if shouldThrowError { throw GeminiError.noResponse }
    return mockRecipes
  }
}

Test Files (18 total)

RecipefyTests/
β”œβ”€β”€ Models
β”‚   β”œβ”€β”€ RecipeTests.swift          (14 tests)
β”‚   β”œβ”€β”€ IngredientTests.swift      (12 tests)
β”‚   β”œβ”€β”€ ScanTests.swift            (11 tests)
β”‚   β”œβ”€β”€ AppUserTests.swift         (8 tests)
β”‚   β”œβ”€β”€ DietaryPreferencesTests.swift (33 tests)
β”‚   └── MeasurementUnitTests.swift (10 tests)
β”œβ”€β”€ Controllers
β”‚   β”œβ”€β”€ RecipeControllerTests.swift     (27 tests)
β”‚   β”œβ”€β”€ IngredientControllerTests.swift (17 tests)
β”‚   β”œβ”€β”€ ScanControllerTests.swift       (12 tests)
β”‚   β”œβ”€β”€ NavigationStateTests.swift      (12 tests)
β”‚   └── AuthControllerErrorTests.swift  (10 tests)
β”œβ”€β”€ Views
β”‚   β”œβ”€β”€ HomeViewTests.swift           (6 tests)
β”‚   β”œβ”€β”€ AuthViewTests.swift           (8 tests)
β”‚   β”œβ”€β”€ IngredientFormViewTests.swift (9 tests)
β”‚   └── RecipeDetailViewTests.swift   (11 tests)
β”œβ”€β”€ Helpers
β”‚   β”œβ”€β”€ AppleSignInHelperTests.swift  (15 tests)
β”‚   └── FirestorePathsTests.swift     (5 tests)
└── Camera
    └── CameraTests.swift             (22 tests)

Running Tests

In Xcode, press ⌘U to run all tests, or use the Test Navigator (⌘6) to run individual test files or methods.

Coverage Target

Scope Target
Models 95-100%
Business Logic 85-95%
Overall 85-90%

Note: 100% coverage is not the goal β€” untested Firebase/Gemini wrappers are intentional. Testing SDK calls adds complexity without value.


πŸ“Š Data Model

Swift Models

Model Key Properties Purpose
AppUser uid, email, displayName, authProvider, photoURL Firebase Auth user with provider tracking
Ingredient name, quantity, unit, category Scanned ingredient with categorization
IngredientCategory vegetables, proteins, grains, dairy, seasonings, oil, other Enum for ingredient classification
Recipe title, ingredients, steps, calories, protein, carbs, fat, fiber, sugar, favorited AI-generated recipe with full nutrition
Scan userId, imagePaths, status, createdAt Image upload metadata and processing status
DietaryPreferences dietTypes, allergies, dislikes, maxCookingTime User dietary constraints for AI prompts

Firestore Collections

users/
  └── {userId}/
      β”œβ”€β”€ displayName, email, photoURL, authProvider
      └── preferences/
          └── dietary/
              β”œβ”€β”€ dietTypes: [String]
              β”œβ”€β”€ allergies: [String]
              β”œβ”€β”€ dislikes: [String]
              └── maxCookingTime: Int

scans/
  └── {scanId}/
      β”œβ”€β”€ userId, imagePaths, status, createdAt
      └── ingredients/
          └── {ingredientId}/
              β”œβ”€β”€ name, quantity, unit, category

recipes/
  └── {recipeId}/
      β”œβ”€β”€ title, description, ingredients, steps
      β”œβ”€β”€ calories, servings, cookMin
      β”œβ”€β”€ protein, carbs, fat, fiber, sugar
      β”œβ”€β”€ createdBy, sourceScanId, favorited
      └── createdAt

🎨 UI/UX Design

  • Color Theme β€” Green accent (#5CB85C) representing fresh, healthy cooking
  • Dark & Light Mode β€” Full support for both system appearances
  • Typography β€” SF Pro with semantic sizing for hierarchy
  • Navigation β€” 5-tab structure (Home, Ingredients, Scan, Recipes, Settings)
  • Empty States β€” Informative messages with relevant icons
  • Loading States β€” Progress indicators with status text
  • Cards β€” Rounded corners, subtle shadows, gesture-friendly

πŸ‘₯ Team

  • Streak Honey
  • Jonass Oh
  • Abdallah Abdaljalil
  • Yuqi Zou

About

fall25 67443

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages