- SwiftUI + async/await architecture - Barcode scanning with AVFoundation - Product display with score ring, NOVA badge, nutrition - Alternatives with sort/filter - Auth (login/register) - Favorites & history - Account management - Dark theme - Connected to food.payfrit.com API (Open Food Facts proxy) Co-Authored-By: Claude <noreply@anthropic.com>
4.4 KiB
4.4 KiB
PayfritFood - iOS App
Native Swift/SwiftUI iOS app for scanning food products, viewing health scores, and finding healthier alternatives.
Bundle ID: com.payfrit.food
Display Name: Payfrit Food
API Base: https://food.payfrit.com/api
Build & Deploy
# Build for device
cd ~/payfrit-food-ios && xcodebuild -project PayfritFood.xcodeproj -scheme PayfritFood -destination 'id=00008030-000244863413C02E' -allowProvisioningUpdates build
# Install to phone
xcrun devicectl device install app --device 00008030-000244863413C02E ~/Library/Developer/Xcode/DerivedData/PayfritFood-*/Build/Products/Debug-iphoneos/PayfritFood.app
# Launch app
xcrun devicectl device process launch --device 00008030-000244863413C02E com.payfrit.food
Project Structure
PayfritFood/
├── PayfritFoodApp.swift # App entry point
├── Info.plist # App config, permissions
├── Assets.xcassets/ # App icons, colors
│
├── Models/
│ ├── Product.swift # Product with score, NOVA, nutrition
│ ├── Alternative.swift # Alternative product with links
│ ├── UserProfile.swift # User profile
│ └── ScanHistory.swift # Scan history item
│
├── ViewModels/
│ └── AppState.swift # Central state (@MainActor ObservableObject)
│
├── Services/
│ ├── APIService.swift # Actor-based API client
│ ├── AuthStorage.swift # Keychain token storage
│ ├── BarcodeScanner.swift # AVFoundation barcode scanning
│ └── LocationService.swift # CoreLocation for distance
│
└── Views/
├── RootView.swift # Tab navigation
├── ScanTab/
│ ├── ScanScreen.swift # Camera + manual entry
│ └── ManualEntrySheet.swift
├── ProductTab/
│ ├── ProductScreen.swift # Product detail
│ ├── ScoreRing.swift # Animated score circle
│ ├── NOVABadge.swift # NOVA 1-4 badge
│ ├── DietaryPills.swift # Dietary tags
│ ├── NutritionSection.swift
│ └── IngredientsSection.swift
├── AlternativesTab/
│ ├── AlternativesScreen.swift
│ ├── FilterChips.swift
│ ├── AlternativeCard.swift
│ └── SponsoredCard.swift
├── FavoritesTab/
│ └── FavoritesScreen.swift
├── HistoryTab/
│ └── HistoryScreen.swift
├── AccountTab/
│ ├── AccountScreen.swift
│ ├── LoginSheet.swift
│ └── RegisterSheet.swift
└── Components/
└── ProductCard.swift
Architecture
State Management
- AppState (
@MainActor ObservableObject): Single source of truth- Authentication: userId, userToken, userProfile, isPremium
- Current product and alternatives
- Navigation state (selectedTab, showLoginSheet, etc.)
API Service
- APIService (Swift actor): Thread-safe singleton
- Base URL:
https://food.payfrit.com/api - Auth via
Authorization: Bearer {token}header
Key Endpoints
POST /auth/login- Login with email/passwordPOST /auth/register- Register new userPOST /product/lookup- Lookup product by barcodeGET /product/{id}/alternatives- Get healthier alternativesGET /favorites- Get user favoritesGET /history- Get scan history
Features
Barcode Scanning
- AVFoundation with AVCaptureMetadataOutput
- Supported formats: EAN-8, EAN-13, UPC-A, UPC-E, Code-128
- Manual entry fallback
Product Display
- Animated score ring (0-100)
- NOVA badge (1-4 processing level)
- Dietary pills (vegan, gluten-free, etc.)
- Expandable nutrition facts
- Expandable ingredients list
Alternatives
- Sort by: Rating, Price, Distance, Processing Level
- Filter chips: Dietary (vegan, GF, etc.), Availability (delivery, pickup)
- Sponsored cards with action links (premium users see no sponsored content)
User Features
- Favorites (requires auth)
- Scan history (requires auth)
- Account: export data, logout, delete account
- Premium: removes sponsored content
Permissions
- Camera: Barcode scanning
- Location: Find nearby stores
Dependencies
- None (uses only system frameworks)
iOS Version
- Minimum: iOS 16.0
- SwiftUI + async/await + URLSession