319 lines
11 KiB
Markdown
319 lines
11 KiB
Markdown
# 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
|
|
|
|
---
|
|
|
|
## 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
|
|
|
|
---
|
|
|
|
This architecture demonstrates production-quality SwiftUI development while delivering a polished, competitive user experience.
|