Update documentation and add branding system integration
Documentation: - Update README.md with complete feature list and SelfieCam branding - Update AI_Implementation.md with current architecture and branding details - Add SelfieCam-specific sections to AGENTS.md (premium, branding, camera) Features: - Add branding debug section to SettingsView (icon generator, preview) - Add BrandingConfig.swift with app colors and launch screen config - Add LaunchBackground.colorset for seamless launch experience - Wrap app in AppLaunchView for animated launch screen
This commit is contained in:
parent
1999f7c137
commit
815b91f6ca
163
AGENTS.md
163
AGENTS.md
@ -497,3 +497,166 @@ Color.Primary.background
|
|||||||
|
|
||||||
- If installed, make sure SwiftLint returns no warnings or errors before committing.
|
- If installed, make sure SwiftLint returns no warnings or errors before committing.
|
||||||
- Verify that documentation reflects any new functionality or behavioral changes.
|
- Verify that documentation reflects any new functionality or behavioral changes.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# SelfieCam-Specific Guidelines
|
||||||
|
|
||||||
|
The following sections are specific to this app's architecture and features.
|
||||||
|
|
||||||
|
|
||||||
|
## App Architecture
|
||||||
|
|
||||||
|
SelfieCam uses the following architectural patterns:
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- **Bedrock**: Local Swift package for design system, branding, and cloud sync
|
||||||
|
- **MijickCamera**: SwiftUI camera framework for capture and preview
|
||||||
|
- **RevenueCat**: Subscription management for premium features
|
||||||
|
|
||||||
|
### Key Protocols
|
||||||
|
|
||||||
|
| Protocol | Purpose | Conforming Types |
|
||||||
|
|----------|---------|------------------|
|
||||||
|
| `RingLightConfigurable` | Ring light settings (size, color, opacity) | `SettingsViewModel` |
|
||||||
|
| `CaptureControlling` | Capture actions (timer, flash, shutter) | `SettingsViewModel` |
|
||||||
|
| `PremiumManaging` | Subscription state and purchases | `PremiumManager` |
|
||||||
|
|
||||||
|
|
||||||
|
## Premium Features
|
||||||
|
|
||||||
|
### Adding a New Premium Feature
|
||||||
|
|
||||||
|
1. **Add setting to `SyncedSettings`** with an appropriate default value
|
||||||
|
2. **Use `PremiumGate.get()`** in the getter:
|
||||||
|
```swift
|
||||||
|
var myPremiumFeature: Bool {
|
||||||
|
get { PremiumGate.get(cloudSync.data.myFeature, default: false, isPremium: isPremiumUnlocked) }
|
||||||
|
set {
|
||||||
|
guard PremiumGate.canSet(isPremium: isPremiumUnlocked) else { return }
|
||||||
|
updateSettings { $0.myFeature = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. **Add crown icon** in the UI to indicate premium status
|
||||||
|
4. **Wire up paywall** trigger when non-premium users tap the control
|
||||||
|
|
||||||
|
### Current Premium Features
|
||||||
|
|
||||||
|
- Custom ring light colors
|
||||||
|
- Premium color presets (Ice Blue, Soft Pink, Warm Amber, Cool Lavender)
|
||||||
|
- Flash sync with ring light color
|
||||||
|
- HDR mode
|
||||||
|
- High quality photos
|
||||||
|
- True mirror mode
|
||||||
|
- Skin smoothing
|
||||||
|
- Center Stage
|
||||||
|
- Extended timers (5s, 10s)
|
||||||
|
- Video and Boomerang capture modes
|
||||||
|
|
||||||
|
|
||||||
|
## Settings & iCloud Sync
|
||||||
|
|
||||||
|
### How Settings Work
|
||||||
|
|
||||||
|
1. All settings are stored in `SyncedSettings` struct
|
||||||
|
2. `CloudSyncManager<SyncedSettings>` handles iCloud synchronization
|
||||||
|
3. `SettingsViewModel` exposes properties that read/write through the sync manager
|
||||||
|
4. Slider values use debounced saves (300ms) to prevent excessive writes
|
||||||
|
|
||||||
|
### Adding a New Setting
|
||||||
|
|
||||||
|
1. Add property to `SyncedSettings` with default value
|
||||||
|
2. Add corresponding property in `SettingsViewModel`
|
||||||
|
3. For premium settings, use `PremiumGate` utilities
|
||||||
|
4. Add UI in `SettingsView`
|
||||||
|
|
||||||
|
|
||||||
|
## Branding System
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
The app uses Bedrock's branding system for:
|
||||||
|
- Animated launch screen
|
||||||
|
- App icon generation
|
||||||
|
- Consistent color scheme
|
||||||
|
|
||||||
|
### Key Files
|
||||||
|
|
||||||
|
- `Shared/BrandingConfig.swift` - App icon and launch screen configuration
|
||||||
|
- `Resources/Assets.xcassets/LaunchBackground.colorset/` - Launch screen background color
|
||||||
|
- `App/SelfieCamApp.swift` - Wraps ContentView with AppLaunchView
|
||||||
|
|
||||||
|
### Modifying Branding
|
||||||
|
|
||||||
|
1. Update colors in `BrandingConfig.swift` → `Color.Branding`
|
||||||
|
2. Update `LaunchBackground.colorset` to match primary color
|
||||||
|
3. Adjust icon/launch screen config as needed
|
||||||
|
4. Use Icon Generator in Settings → Debug to create new app icon
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
See `Bedrock/Sources/Bedrock/Branding/BRANDING_GUIDE.md` for complete branding documentation.
|
||||||
|
|
||||||
|
|
||||||
|
## Camera Integration
|
||||||
|
|
||||||
|
### MijickCamera
|
||||||
|
|
||||||
|
The app uses MijickCamera for camera functionality:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
import MijickCamera
|
||||||
|
|
||||||
|
// Camera position
|
||||||
|
var cameraPosition: CameraPosition // .front or .back
|
||||||
|
|
||||||
|
// Flash modes
|
||||||
|
var flashMode: CameraFlashMode // .off, .on, .auto
|
||||||
|
```
|
||||||
|
|
||||||
|
### Camera Features
|
||||||
|
|
||||||
|
- Front/back camera switching
|
||||||
|
- Pinch-to-zoom
|
||||||
|
- Photo capture with quality settings
|
||||||
|
- Video recording (premium)
|
||||||
|
- HDR mode (premium)
|
||||||
|
|
||||||
|
|
||||||
|
## Ring Light System
|
||||||
|
|
||||||
|
### How It Works
|
||||||
|
|
||||||
|
The ring light is a colored overlay (`RingLightOverlay`) that surrounds the camera preview:
|
||||||
|
|
||||||
|
- **Size**: Adjustable border width (10-120pt)
|
||||||
|
- **Color**: Preset colors or custom color picker
|
||||||
|
- **Opacity**: Adjustable brightness (10%-100%)
|
||||||
|
- **Toggle**: Can be enabled/disabled
|
||||||
|
|
||||||
|
### Color Presets
|
||||||
|
|
||||||
|
| Color | ID | Premium |
|
||||||
|
|-------|-----|---------|
|
||||||
|
| Pure White | `pureWhite` | No |
|
||||||
|
| Warm Cream | `warmCream` | No |
|
||||||
|
| Ice Blue | `iceBlue` | Yes |
|
||||||
|
| Soft Pink | `softPink` | Yes |
|
||||||
|
| Warm Amber | `warmAmber` | Yes |
|
||||||
|
| Cool Lavender | `coolLavender` | Yes |
|
||||||
|
| Custom | `custom` | Yes |
|
||||||
|
|
||||||
|
|
||||||
|
## Documentation Files
|
||||||
|
|
||||||
|
When making changes, update the appropriate documentation:
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `README.md` | User-facing app overview, setup instructions |
|
||||||
|
| `AI_Implementation.md` | Technical architecture, implementation details |
|
||||||
|
| `AGENTS.md` | Development guidelines (this file) |
|
||||||
|
|
||||||
|
Always commit documentation updates with the related code changes.
|
||||||
|
|||||||
@ -1,68 +1,272 @@
|
|||||||
# AI_Implementation.md
|
# AI Implementation Guide
|
||||||
|
|
||||||
## How This App Was Architected & Built
|
## How This App Was Architected & Built
|
||||||
|
|
||||||
This project was developed following strict senior-level iOS engineering standards, with guidance from an AI assistant (Grok) acting as a Senior iOS Engineer specializing in SwiftUI and modern Apple frameworks.
|
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)
|
||||||
|
|
||||||
### Guiding Principles (from AGENTS.md)
|
|
||||||
- **Protocol-Oriented Programming (POP) first**: All shared capabilities defined via protocols before concrete types
|
- **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
|
- **MVVM-lite**: Views are "dumb" — all logic lives in `@Observable` view models
|
||||||
- **No third-party dependencies**: Pure Apple frameworks only (SwiftUI, AVFoundation, StoreKit 2, CoreImage)
|
- **Bedrock Design System**: Centralized design tokens, no magic numbers
|
||||||
- **No magic numbers**: All dimensions, opacities, durations from centralized `Design` constants
|
|
||||||
- **Full accessibility**: Dynamic Type, VoiceOver labels/hints/traits/announcements
|
- **Full accessibility**: Dynamic Type, VoiceOver labels/hints/traits/announcements
|
||||||
- **Modern Swift & SwiftUI**: Swift 6 concurrency, `@MainActor`, `foregroundStyle`, `clipShape(.rect)`, `NavigationStack`
|
- **Modern Swift & SwiftUI**: Swift 6 concurrency, `@MainActor`, `foregroundStyle`, `clipShape(.rect)`, `NavigationStack`
|
||||||
- **Testable & reusable design**: Protocols enable mocking and future SPM package extraction
|
- **Testable & reusable design**: Protocols enable mocking and future package extraction
|
||||||
|
|
||||||
### Architecture Overview
|
---
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
Shared/
|
Shared/
|
||||||
├── DesignConstants.swift → Semantic design tokens (spacing, radii, sizes, etc.)
|
├── DesignConstants.swift → Uses Bedrock design tokens
|
||||||
├── Color+Extensions.swift → Ring light color presets
|
├── 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/
|
├── Protocols/
|
||||||
│ ├── RingLightConfigurable.swift → Border, color, brightness, mirror, smoothing
|
│ ├── RingLightConfigurable.swift → Border, color, brightness
|
||||||
│ ├── CaptureControlling.swift → Timer, grid, zoom, capture mode
|
│ ├── CaptureControlling.swift → Timer, grid, zoom, capture
|
||||||
│ └── PremiumManaging.swift → Subscription state & purchase handling
|
│ └── PremiumManaging.swift → Subscription state
|
||||||
└── Premium/
|
├── Premium/
|
||||||
└── PremiumManager.swift → Native StoreKit 2 implementation
|
│ └── PremiumManager.swift → RevenueCat integration
|
||||||
|
├── Services/
|
||||||
|
│ └── PhotoLibraryService.swift → Photo saving service
|
||||||
|
└── Storage/
|
||||||
|
└── SyncedSettings.swift → iCloud-synced settings
|
||||||
|
|
||||||
Features/
|
Features/
|
||||||
├── Camera/ → Main UI, preview, capture logic
|
├── Camera/ → Main camera UI
|
||||||
├── Settings/ → Configuration screens
|
│ ├── ContentView.swift → Screen coordinator
|
||||||
└── Paywall/ → Pro subscription flow
|
│ ├── 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
|
## Key Implementation Decisions
|
||||||
|
|
||||||
1. **Ring Light Effect**
|
### 1. Ring Light Effect
|
||||||
- Achieved by coloring the safe area background and leaving a centered rectangular window for camera preview
|
- Achieved using `RingLightOverlay` view that creates a colored border around the camera preview
|
||||||
- Border width controlled via user setting
|
- Border width controlled via user setting (10-120pt range)
|
||||||
- Gradient support added for directional "portrait lighting"
|
- Multiple preset colors with premium custom color picker
|
||||||
|
- Adjustable opacity/brightness (10%-100%)
|
||||||
|
- Enabled/disabled toggle for quick access
|
||||||
|
|
||||||
2. **Camera System**
|
### 2. Camera System
|
||||||
- `AVCaptureSession` with front camera default
|
- Uses **MijickCamera** framework for SwiftUI-native camera handling
|
||||||
- `UIViewRepresentable` wrapper for preview with pinch-to-zoom
|
- Supports front and back camera switching
|
||||||
- Video data output delegate for future real-time filters (skin smoothing placeholder)
|
- 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**
|
### 3. Capture Enhancements
|
||||||
- Timer with async countdown and accessibility announcements
|
- Self-timer with countdown (3s free, 5s/10s premium)
|
||||||
- Volume button observation via KVO on `AVAudioSession.outputVolume`
|
- Post-capture preview with share functionality
|
||||||
- Flash burst: temporarily sets brightness to 1.0 on capture
|
- Auto-save option to Photo Library
|
||||||
|
- Front flash using screen brightness
|
||||||
|
- Support for photo, video, and boomerang modes
|
||||||
|
|
||||||
4. **Freemium Model**
|
### 4. Freemium Model
|
||||||
- Built with pure StoreKit 2 (no RevenueCat)
|
- Built with **RevenueCat** for subscription management
|
||||||
- `PremiumManaging` protocol enables easy testing/mocking
|
- `PremiumManager` wraps RevenueCat SDK
|
||||||
- Clean paywall with benefit list and native purchase flow
|
- `PremiumGate` utility for clean premium feature access
|
||||||
|
- Settings automatically fall back to free defaults when not premium
|
||||||
|
|
||||||
5. **Reusability Focus**
|
### 5. iCloud Sync
|
||||||
- All shared logic extracted to protocols
|
- Uses **Bedrock's CloudSyncManager** for settings synchronization
|
||||||
- Ready for future extraction into SPM packages:
|
- `SyncedSettings` model contains all user preferences
|
||||||
- `SelfieCameraKit`
|
- Debounced saves for slider values (300ms delay)
|
||||||
- `SelfieRingLightKit`
|
- Real-time sync status display in Settings
|
||||||
- `SelfiePremiumKit`
|
- Available to all users (not a premium feature)
|
||||||
|
|
||||||
### Development Process
|
### 6. Branding System
|
||||||
- Iterative feature additions guided by competitive analysis of top App Store selfie apps
|
- Uses **Bedrock's Branding** module for launch screen and app icon
|
||||||
- Each new capability (timer, boomerang, gradient, subscriptions) added with protocol-first design
|
- `BrandingConfig.swift` defines app-specific colors and symbols
|
||||||
- Strict adherence to no magic numbers, full accessibility, and clean separation
|
- `LaunchBackground.colorset` matches launch screen primary color
|
||||||
- Final structure optimized for maintainability and future library extraction
|
- Animated launch with configurable duration and pattern style
|
||||||
|
- Icon generator available in DEBUG builds
|
||||||
|
|
||||||
This app demonstrates production-quality SwiftUI architecture while delivering a delightful, competitive user experience.
|
---
|
||||||
|
|
||||||
|
## 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 |
|
||||||
|
| Capture modes | Photo | Photo, Video, Boomerang |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|||||||
158
README.md
158
README.md
@ -1,42 +1,60 @@
|
|||||||
# SelfieRingLight
|
# SelfieCam
|
||||||
|
|
||||||
A modern, professional-grade selfie camera app for iOS that simulates a high-quality ring light using the device's screen. Built entirely with SwiftUI, Swift 6, and AVFoundation.
|
A modern, professional-grade selfie camera app for iOS featuring a customizable ring light overlay, premium camera controls, and beautiful branding. Built entirely with SwiftUI, Swift 6, and the MijickCamera framework.
|
||||||
|
|
||||||
Perfect for low-light selfies, video calls, makeup checks, or professional portrait lighting on the go.
|
Perfect for low-light selfies, content creation, video calls, makeup application, or professional portrait lighting on the go.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### Core Camera & Lighting
|
### Core Camera & Lighting
|
||||||
- Full-screen front-camera preview with true mirror option
|
- Full-screen camera preview with front/back camera switching
|
||||||
- Configurable **screen-based ring light** with adjustable border thickness
|
- Configurable **screen-based ring light** with adjustable border thickness (10-120pt)
|
||||||
- Multiple color temperature presets (Pure White, Warm Cream, Ice Blue, Rose Pink, etc.)
|
- Multiple color temperature presets (Pure White, Warm Cream, Ice Blue, Soft Pink, Warm Amber, Cool Lavender)
|
||||||
- **Directional gradient lighting** for flattering portrait effects
|
- **Ring light brightness control** with adjustable opacity
|
||||||
- Real-time screen brightness control (overrides system brightness while in use)
|
- **Flash modes**: Off, On, Auto
|
||||||
- Flash burst on capture for extra fill light
|
- **Front Flash**: Uses screen brightness for front camera flash effect
|
||||||
|
- Real-time camera preview with smooth performance
|
||||||
|
|
||||||
### Capture Modes
|
### Capture Modes
|
||||||
- Photo capture (saved to Photo Library)
|
- **Photo capture** with high-quality output
|
||||||
- Video recording
|
- **Video recording** (Premium)
|
||||||
- **Boomerang** mode (3-second looping short video)
|
- **Boomerang mode** for looping short videos (Premium)
|
||||||
- 3-second and 10-second self-timer with countdown overlay and VoiceOver announcements
|
- Self-timer with 3-second (free), 5-second, and 10-second (Premium) options
|
||||||
- Pinch-to-zoom gesture
|
- Pinch-to-zoom gesture support
|
||||||
- Volume button shutter support (photo or video start/stop)
|
|
||||||
- Rule-of-thirds grid overlay (toggleable)
|
- Rule-of-thirds grid overlay (toggleable)
|
||||||
|
- Post-capture preview with share functionality
|
||||||
|
|
||||||
### Premium Features (Freemium Model)
|
### Premium Features (Freemium Model)
|
||||||
- All advanced color presets + custom colors
|
- **Custom ring light colors** with full color picker
|
||||||
- Gradient and directional lighting
|
- **Premium color presets**: Ice Blue, Soft Pink, Warm Amber, Cool Lavender
|
||||||
- Advanced beauty filters (coming soon)
|
- **Flash Sync**: Match flash color with ring light color
|
||||||
- Unlimited boomerang length
|
- **HDR Mode**: High Dynamic Range photo capture
|
||||||
- No watermarks
|
- **High Quality Photos**: Maximum resolution output
|
||||||
|
- **True Mirror Mode**: Horizontally flipped preview like a real mirror
|
||||||
|
- **Skin Smoothing**: Real-time subtle skin smoothing filter
|
||||||
|
- **Center Stage**: Automatic subject tracking/centering
|
||||||
|
- **Extended Timers**: 5-second and 10-second self-timer options
|
||||||
|
- **Video & Boomerang**: Video recording and looping video capture
|
||||||
- Ad-free experience
|
- Ad-free experience
|
||||||
|
|
||||||
|
### iCloud Sync
|
||||||
|
- Automatic settings synchronization across all devices
|
||||||
|
- Real-time sync status with last sync timestamp
|
||||||
|
- Manual "Sync Now" option
|
||||||
|
- Available to all users (free and premium)
|
||||||
|
|
||||||
|
### Branding & Launch
|
||||||
|
- **Animated launch screen** with customizable design
|
||||||
|
- **App icon generator** for creating consistent branding
|
||||||
|
- Seamless transition from launch to main app
|
||||||
|
- Configurable colors, patterns, and layout styles
|
||||||
|
|
||||||
### Accessibility & Polish
|
### Accessibility & Polish
|
||||||
- Full VoiceOver support with meaningful labels, hints, and announcements
|
- Full VoiceOver support with meaningful labels, hints, and announcements
|
||||||
- Dynamic Type and ScaledMetric for readable text
|
- Dynamic Type and ScaledMetric for readable text at all sizes
|
||||||
- String Catalog localization ready (`.xcstrings`)
|
- String Catalog localization ready (`.xcstrings`)
|
||||||
- Prevents screen dimming during use
|
- Consistent design system using Bedrock framework
|
||||||
- Restores original brightness on background/app close
|
- Prevents screen dimming during camera use
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
*(Add App Store-ready screenshots here once built)*
|
*(Add App Store-ready screenshots here once built)*
|
||||||
@ -46,12 +64,17 @@ Perfect for low-light selfies, video calls, makeup checks, or professional portr
|
|||||||
- Xcode 16+
|
- Xcode 16+
|
||||||
- Swift 6 language mode
|
- Swift 6 language mode
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **[MijickCamera](https://github.com/Mijick/Camera)** - Modern SwiftUI camera framework
|
||||||
|
- **[RevenueCat](https://www.revenuecat.com)** - Subscription management
|
||||||
|
- **Bedrock** - Internal design system and UI components (local package)
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
### 1. Clone the Repository
|
### 1. Clone the Repository
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/yourusername/SelfieRingLight.git
|
git clone https://github.com/yourusername/SelfieCam.git
|
||||||
cd SelfieRingLight
|
cd SelfieCam
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Configure API Keys
|
### 2. Configure API Keys
|
||||||
@ -60,7 +83,7 @@ This project uses `.xcconfig` files to securely manage API keys. **Never commit
|
|||||||
|
|
||||||
1. Copy the template file:
|
1. Copy the template file:
|
||||||
```bash
|
```bash
|
||||||
cp SelfieRingLight/Configuration/Secrets.xcconfig.template SelfieRingLight/Configuration/Secrets.xcconfig
|
cp SelfieCam/Configuration/Secrets.xcconfig.template SelfieCam/Configuration/Secrets.xcconfig
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Edit `Secrets.xcconfig` with your actual API key:
|
2. Edit `Secrets.xcconfig` with your actual API key:
|
||||||
@ -91,7 +114,17 @@ To test premium features without a real subscription during development:
|
|||||||
|
|
||||||
This unlocks all premium features in DEBUG builds only.
|
This unlocks all premium features in DEBUG builds only.
|
||||||
|
|
||||||
### 5. CI/CD Configuration
|
### 5. Branding Configuration
|
||||||
|
|
||||||
|
The app uses the Bedrock branding system for launch screen and app icon:
|
||||||
|
|
||||||
|
1. **BrandingConfig.swift** defines colors, icons, and launch screen settings
|
||||||
|
2. **Launch Screen Background Color** is set in Assets.xcassets and project build settings
|
||||||
|
3. **Icon Generator** available in Settings → Debug (DEBUG builds only)
|
||||||
|
|
||||||
|
See `Bedrock/Sources/Bedrock/Branding/BRANDING_GUIDE.md` for complete documentation.
|
||||||
|
|
||||||
|
### 6. CI/CD Configuration
|
||||||
|
|
||||||
For automated builds, set the `REVENUECAT_API_KEY` environment variable in your CI/CD system:
|
For automated builds, set the `REVENUECAT_API_KEY` environment variable in your CI/CD system:
|
||||||
|
|
||||||
@ -108,32 +141,77 @@ Add `REVENUECAT_API_KEY` as a secret in your Xcode Cloud workflow.
|
|||||||
- Camera access required for preview and capture
|
- Camera access required for preview and capture
|
||||||
- Photo Library access required to save photos/videos
|
- Photo Library access required to save photos/videos
|
||||||
- Microphone access required for video recording
|
- Microphone access required for video recording
|
||||||
|
- iCloud access for settings synchronization (optional)
|
||||||
- No data collection, no analytics, no tracking
|
- No data collection, no analytics, no tracking
|
||||||
|
|
||||||
## Monetization
|
## Monetization
|
||||||
Freemium model with optional "Pro" subscription:
|
Freemium model with optional "Pro" subscription:
|
||||||
- Free: Basic ring light, standard colors, photo/video, timer, zoom
|
- **Free**: Basic ring light, standard colors (Pure White, Warm Cream), photo capture, 3s timer, grid, zoom
|
||||||
- Pro: Full color palette, gradients, advanced features, future updates
|
- **Pro**: Full color palette, custom colors, HDR, high quality, flash sync, true mirror, skin smoothing, center stage, extended timers, video, boomerang
|
||||||
|
|
||||||
Implemented with RevenueCat for reliable subscription management.
|
Implemented with RevenueCat for reliable subscription management.
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
SelfieRingLight/
|
SelfieCam/
|
||||||
├── App/ # App entry point
|
├── App/ # App entry point with launch screen
|
||||||
|
├── Configuration/ # xcconfig files (API keys)
|
||||||
├── Features/
|
├── Features/
|
||||||
│ ├── Camera/ # Camera preview, capture, view model
|
│ ├── Camera/ # Main camera UI
|
||||||
│ ├── Paywall/ # Pro subscription flow
|
│ │ ├── ContentView.swift # Main screen coordinator
|
||||||
│ └── Settings/ # Configuration screens
|
│ │ ├── Views/ # Camera UI components
|
||||||
|
│ │ │ ├── CustomCameraScreen.swift
|
||||||
|
│ │ │ ├── RingLightOverlay.swift
|
||||||
|
│ │ │ ├── CaptureButton.swift
|
||||||
|
│ │ │ ├── ExpandableControlsPanel.swift
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ ├── GridOverlay.swift # Rule of thirds overlay
|
||||||
|
│ │ └── PostCapturePreviewView.swift
|
||||||
|
│ ├── Paywall/ # Pro subscription flow
|
||||||
|
│ │ └── ProPaywallView.swift
|
||||||
|
│ └── Settings/ # Configuration screens
|
||||||
|
│ ├── SettingsView.swift
|
||||||
|
│ ├── SettingsViewModel.swift
|
||||||
|
│ └── ...
|
||||||
├── Shared/
|
├── Shared/
|
||||||
│ ├── Configuration/ # xcconfig files (API keys)
|
│ ├── BrandingConfig.swift # App icon & launch screen config
|
||||||
│ ├── Premium/ # PremiumManager (RevenueCat)
|
│ ├── DesignConstants.swift # Design tokens (uses Bedrock)
|
||||||
│ ├── Protocols/ # Shared protocols
|
│ ├── Color+Extensions.swift # Ring light color presets
|
||||||
│ ├── Color+Extensions.swift # Ring light color presets
|
│ ├── Models/ # Data models
|
||||||
│ └── DesignConstants.swift # Design tokens
|
│ │ ├── CameraFlashMode.swift
|
||||||
└── Resources/ # Assets, localization
|
│ │ ├── CameraHDRMode.swift
|
||||||
|
│ │ ├── PhotoQuality.swift
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── Protocols/ # Shared protocols
|
||||||
|
│ │ ├── RingLightConfigurable.swift
|
||||||
|
│ │ ├── CaptureControlling.swift
|
||||||
|
│ │ └── PremiumManaging.swift
|
||||||
|
│ ├── Premium/ # Subscription management
|
||||||
|
│ │ └── PremiumManager.swift
|
||||||
|
│ ├── Services/ # App services
|
||||||
|
│ │ └── PhotoLibraryService.swift
|
||||||
|
│ └── Storage/ # Persistence
|
||||||
|
│ └── SyncedSettings.swift # iCloud-synced settings model
|
||||||
|
└── Resources/ # Assets, localization
|
||||||
|
├── Assets.xcassets/
|
||||||
|
│ ├── AppIcon.appiconset/
|
||||||
|
│ ├── LaunchBackground.colorset/
|
||||||
|
│ └── ...
|
||||||
|
└── Localizable.xcstrings
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Key Technologies
|
||||||
|
|
||||||
|
| Technology | Purpose |
|
||||||
|
|------------|---------|
|
||||||
|
| SwiftUI | User interface framework |
|
||||||
|
| Swift 6 | Modern concurrency with strict checking |
|
||||||
|
| MijickCamera | Camera capture and preview |
|
||||||
|
| RevenueCat | Subscription management |
|
||||||
|
| Bedrock | Design system and branding |
|
||||||
|
| CloudKit | iCloud settings synchronization |
|
||||||
|
| AVFoundation | Low-level camera access |
|
||||||
|
|
||||||
## License
|
## License
|
||||||
*(Add your license here)*
|
*(Add your license here)*
|
||||||
|
|||||||
@ -762,10 +762,20 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Branding Debug Section
|
// MARK: - Branding Debug Section
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
private var brandingDebugSection: some View {
|
private var brandingDebugSection: some View {
|
||||||
VStack(spacing: Design.Spacing.small) {
|
VStack(spacing: Design.Spacing.small) {
|
||||||
|
// Debug Premium Toggle
|
||||||
|
SettingsToggle(
|
||||||
|
title: "Enable Debug Premium",
|
||||||
|
subtitle: "Unlock all premium features for testing",
|
||||||
|
isOn: Binding(
|
||||||
|
get: { viewModel.premiumManager.isDebugPremiumToggleEnabled },
|
||||||
|
set: { viewModel.premiumManager.isDebugPremiumToggleEnabled = $0 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.tint(Color.Status.warning)
|
||||||
// Icon Generator
|
// Icon Generator
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
IconGeneratorView(config: .selfieCam, appName: "SelfieCam")
|
IconGeneratorView(config: .selfieCam, appName: "SelfieCam")
|
||||||
|
|||||||
@ -25,16 +25,25 @@ final class PremiumManager: PremiumManaging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Debug Override
|
// MARK: - Debug Override
|
||||||
|
|
||||||
/// Check if debug premium is enabled via environment variable.
|
/// Debug premium toggle stored in UserDefaults (only available in DEBUG builds)
|
||||||
/// Set "ENABLE_DEBUG_PREMIUM" = "1" in your scheme's environment variables to unlock all premium features during debugging.
|
@AppStorage("debugPremiumEnabled") private var debugPremiumEnabled = false
|
||||||
|
|
||||||
|
/// Check if debug premium is enabled via UserDefaults toggle or environment variable.
|
||||||
|
/// The toggle in Settings > Debug takes precedence over environment variables.
|
||||||
private var isDebugPremiumEnabled: Bool {
|
private var isDebugPremiumEnabled: Bool {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
return ProcessInfo.processInfo.environment["ENABLE_DEBUG_PREMIUM"] == "1"
|
return debugPremiumEnabled || ProcessInfo.processInfo.environment["ENABLE_DEBUG_PREMIUM"] == "1"
|
||||||
#else
|
#else
|
||||||
return false
|
return false
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Public getter/setter for debug premium toggle (DEBUG builds only)
|
||||||
|
var isDebugPremiumToggleEnabled: Bool {
|
||||||
|
get { debugPremiumEnabled }
|
||||||
|
set { debugPremiumEnabled = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
var isPremium: Bool {
|
var isPremium: Bool {
|
||||||
// Debug override takes precedence
|
// Debug override takes precedence
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user