# AI Implementation Guide ## How This App Was Architected & Built This project was developed following strict senior-level iOS engineering standards, with guidance from AI assistants acting as Senior iOS Engineers specializing in SwiftUI and modern Apple frameworks. --- ## Guiding Principles (from AGENTS.md) - **Protocol-Oriented Programming (POP) first**: All shared capabilities defined via protocols before concrete types - **MVVM-lite**: Views are "dumb" — all logic lives in `@Observable` view models - **Bedrock Design System**: Centralized design tokens, no magic numbers - **Full accessibility**: Dynamic Type, VoiceOver labels/hints/traits/announcements - **Modern Swift & SwiftUI**: Swift 6 concurrency, `@MainActor`, `foregroundStyle`, `clipShape(.rect)`, `NavigationStack` - **Testable & reusable design**: Protocols enable mocking and future package extraction --- ## Architecture Overview ``` Shared/ ├── DesignConstants.swift → Uses Bedrock design tokens ├── BrandingConfig.swift → App icon & launch screen config ├── Color+Extensions.swift → Ring light color presets ├── Models/ │ ├── CameraFlashMode.swift → Flash mode enum │ ├── CameraHDRMode.swift → HDR mode enum │ ├── PhotoQuality.swift → Photo quality settings │ └── CapturedPhoto.swift → Photo data model ├── Protocols/ │ ├── RingLightConfigurable.swift → Border, color, brightness │ ├── CaptureControlling.swift → Timer, grid, zoom, capture │ └── PremiumManaging.swift → Subscription state ├── Premium/ │ └── PremiumManager.swift → RevenueCat integration ├── Services/ │ └── PhotoLibraryService.swift → Photo saving service └── Storage/ └── SyncedSettings.swift → iCloud-synced settings Features/ ├── Camera/ → Main camera UI │ ├── ContentView.swift → Screen coordinator │ ├── Views/ → UI components │ └── GridOverlay.swift → Rule of thirds ├── Settings/ → Configuration │ ├── SettingsView.swift → Settings UI │ └── SettingsViewModel.swift → Settings logic + sync └── Paywall/ → Pro subscription flow ``` --- ## Key Implementation Decisions ### 1. Ring Light Effect - Achieved using `RingLightOverlay` view that creates a colored border around the camera preview - Border width controlled via user setting (10-120pt range) - Multiple preset colors with premium custom color picker - Adjustable opacity/brightness (10%-100%) - Enabled/disabled toggle for quick access ### 2. Camera System - Uses **MijickCamera** framework for SwiftUI-native camera handling - Supports front and back camera switching - Pinch-to-zoom with smooth interpolation - Flash modes: Off, On, Auto (with premium flash sync) - HDR mode support (premium feature) - Photo quality settings (medium free, high premium) ### 3. Capture Enhancements - Self-timer with countdown (3s free, 5s/10s premium) - Post-capture preview with share functionality - Auto-save option to Photo Library - Front flash using screen brightness - **Camera Control button** (iPhone 16+): Full press captures, light press locks focus/exposure - **Hardware shutter**: Volume buttons trigger capture via `VolumeButtonObserver` ### 4. Freemium Model - Built with **RevenueCat** for subscription management - `PremiumManager` wraps RevenueCat SDK - `PremiumGate` utility for clean premium feature access - Settings automatically fall back to free defaults when not premium ### 5. iCloud Sync - Uses **Bedrock's CloudSyncManager** for settings synchronization - `SyncedSettings` model contains all user preferences - Debounced saves for slider values (300ms delay) - Real-time sync status display in Settings - Available to all users (not a premium feature) ### 6. Branding System - Uses **Bedrock's Branding** module for launch screen and app icon - `BrandingConfig.swift` defines app-specific colors and symbols - `LaunchBackground.colorset` matches launch screen primary color - Animated launch with configurable duration and pattern style - Icon generator available in DEBUG builds --- ## Camera Control Button Integration ### Overview The app supports the **Camera Control** button on iPhone 16+ via `AVCaptureEventInteraction` (iOS 17.2+). ### Files Involved | File | Purpose | |------|---------| | `Shared/Protocols/CaptureEventHandling.swift` | Protocol defining hardware capture event handling | | `Features/Camera/Views/CaptureEventInteraction.swift` | `AVCaptureEventInteraction` wrapper and SwiftUI integration | | `Features/Camera/Views/VolumeButtonObserver.swift` | Volume button capture support (legacy) | ### Supported Hardware Events | Event | Hardware | Action | |-------|----------|--------| | **Primary (full press)** | Camera Control, Action Button | Capture photo | | **Secondary (light press)** | Camera Control | Lock focus/exposure | | **Volume buttons** | All iPhones | Capture photo | ### Implementation Details ```swift // CaptureEventInteractionView is added to the camera ZStack CaptureEventInteractionView( onCapture: { performCapture() }, onFocusLock: { locked in handleFocusLock(locked) } ) // The interaction uses AVCaptureEventInteraction (iOS 17.2+) AVCaptureEventInteraction( primaryEventHandler: { phase in /* capture on .ended */ }, secondaryEventHandler: { phase in /* focus lock on .began/.ended */ } ) ``` ### Device Compatibility - **iPhone 16+**: Full Camera Control button support (press + light press) - **iPhone 15 Pro+**: Action button support (when configured for camera) - **All iPhones**: Volume button shutter via `VolumeButtonObserver` --- ## Premium Feature Implementation ### How Premium Gating Works The app uses a centralized `PremiumGate` utility for consistent premium feature handling: ```swift // In SettingsViewModel var isMirrorFlipped: Bool { get { PremiumGate.get(cloudSync.data.isMirrorFlipped, default: false, isPremium: isPremiumUnlocked) } set { guard PremiumGate.canSet(isPremium: isPremiumUnlocked) else { return } updateSettings { $0.isMirrorFlipped = newValue } } } ``` ### Premium Features List | Feature | Free Value | Premium Value | |---------|-----------|---------------| | Ring light colors | Pure White, Warm Cream | All presets + custom | | Timer options | Off, 3s | Off, 3s, 5s, 10s | | Photo quality | Medium | Medium, High | | HDR mode | Off | Off, On, Auto | | True mirror | Off | Configurable | | Skin smoothing | Off | Configurable | | Flash sync | Off | Configurable | | Center stage | Off | Configurable | --- ## Settings & Persistence ### SyncedSettings Model All user preferences are stored in a single `SyncedSettings` struct that syncs via iCloud: - Ring light: size, color ID, custom color RGB, opacity, enabled - Camera: position, flash mode, HDR mode, photo quality - Display: mirror flip, skin smoothing, grid visible - Capture: timer, capture mode, auto-save - Premium features: flash sync, center stage ### Debounced Saves Slider values (ring size, opacity) use debounced saving to prevent excessive iCloud writes: ```swift private func debouncedSave(key: String, action: @escaping () -> Void) { debounceTask?.cancel() debounceTask = Task { try? await Task.sleep(for: .milliseconds(300)) guard !Task.isCancelled else { return } action() } } ``` --- ## Branding Implementation ### Files Involved 1. **BrandingConfig.swift** - Defines app icon and launch screen configurations 2. **LaunchBackground.colorset** - Asset catalog color matching primary brand color 3. **SelfieCamApp.swift** - Wraps ContentView with AppLaunchView ### Color Scheme ```swift extension Color { enum Branding { static let primary = Color(red: 0.85, green: 0.25, blue: 0.45) // Vibrant magenta static let secondary = Color(red: 0.45, green: 0.12, blue: 0.35) // Deep purple static let accent = Color.white } } ``` ### Launch Screen Configuration ```swift static let selfieCam = LaunchScreenConfig( title: "SELFIE CAM", tagline: "Look Your Best", iconSymbols: ["camera.fill", "sparkles"], cornerSymbol: "sparkle", patternStyle: .radial, // ... colors and sizing ) ``` --- ## Development Workflow ### Adding a New Feature 1. **Define the protocol** (if shared behavior) 2. **Add to SyncedSettings** (if needs persistence) 3. **Implement in SettingsViewModel** (with premium gating if applicable) 4. **Add UI in SettingsView** 5. **Update documentation** (README, this file) ### Adding a Premium Feature 1. Add setting to `SyncedSettings` with appropriate default 2. Use `PremiumGate.get()` for the getter with free default 3. Use `PremiumGate.canSet()` guard for the setter 4. Add premium indicator (crown icon) in UI 5. Wire up paywall trigger for non-premium users ### Testing Premium Features Set environment variable in scheme: - **Name:** `ENABLE_DEBUG_PREMIUM` - **Value:** `1` --- ## Reusability & Extraction The codebase is structured for future extraction into reusable packages: | Potential Package | Contents | |-------------------|----------| | **SelfieCameraKit** | Camera views, capture logic, preview components | | **RingLightKit** | Ring light overlay, color presets, configuration | | **PremiumKit** | Premium manager, gating utilities, paywall | | **SyncedSettingsKit** | CloudSyncManager, settings model pattern | --- ## Key Dependencies | Dependency | Purpose | Integration | |------------|---------|-------------| | **Bedrock** | Design system, branding, cloud sync | Local Swift package | | **MijickCamera** | Camera capture and preview | SPM dependency | | **RevenueCat** | Subscription management | SPM dependency | --- ## Code Quality Standards - **No magic numbers**: All values from Design constants - **Full accessibility**: Every interactive element has VoiceOver support - **Protocol-first**: Shared behavior defined via protocols - **Separation of concerns**: Views are dumb, ViewModels contain logic - **Modern APIs**: Swift 6, async/await, @Observable - **Documentation**: Code comments, README, implementation guides --- ## Known Issues / TODO ### Camera Control Button Light Press - NOT WORKING **Status:** ❌ Broken - Needs Investigation The Camera Control button (iPhone 16+) **full press works** for photo capture, but the **light press (secondary action) does NOT work**. Testing revealed that the "secondary" events in logs were actually triggered by **volume button**, not Camera Control light press. The volume button works because `onCameraCaptureEvent` handles all hardware capture buttons. #### What Works: - ✅ Camera Control full press → triggers photo capture - ✅ Volume up/down → triggers secondary event (focus lock) #### What Doesn't Work: - ❌ Camera Control light press → no event received at all - ❌ Camera Control swipe gestures (zoom) → Apple-exclusive API #### User Action Required - Check Accessibility Settings: **Settings > Accessibility > Camera Control**: - Ensure **Camera Control** is enabled - Ensure **Light-Press** is turned ON - Adjust **Light-Press Force** if needed - Check **Double Light-Press Speed** These system settings may affect third-party apps differently than Apple Camera. #### Investigation Areas: 1. **Accessibility settings may block third-party light press** - User reports light press works in Apple Camera but not SelfieCam - System may require explicit light-press enablement per-app 2. **MijickCamera session configuration** - The third-party camera framework may interfere with light press detection - MijickCamera manages its own AVCaptureSession - may conflict - Try testing with raw AVCaptureSession to isolate the issue 3. **`onCameraCaptureEvent` secondaryAction limitations** - The `secondaryAction` closure receives volume button events correctly - Camera Control light press may use different event pathway - Apple may internally route light press to their Camera app exclusively 4. **Light press may require AVCapturePhotoOutput configuration** - Secondary events might need specific photo output settings - Check if `AVCapturePhotoSettings` has light-press related properties 5. **Possible Apple restriction (most likely)** - Light press and swipe gestures appear restricted to first-party apps - Similar to swipe-to-zoom which is Apple-exclusive - No public API documentation confirms light press availability --- ## Future Enhancements Potential areas for expansion: - [ ] Real-time filters (beauty, color grading) - [ ] Gesture-based capture (smile detection) - [ ] Widget for quick camera access - [ ] Apple Watch remote trigger - [ ] Export presets (aspect ratios, watermarks) - [ ] Social sharing integrations - [ ] Camera Control button swipe-to-zoom (if Apple makes API public) --- This architecture demonstrates production-quality SwiftUI development while delivering a polished, competitive user experience.