Camera & Face Detection
Complete camera module with photo capture, live preview, face detection using Vision framework, and face landmark overlay (iOS only)
Copies a short instruction with your private download link. Paste it into your agent: it downloads the whole skill (the source code, the setup steps and the mistakes we already made for you) and builds the screen.
Works with
What it does
Platform: iOS only — this module uses AVFoundation and Vision framework APIs not available on macOS.
Provides a production-ready camera module for iOS apps with live preview, photo capture, pinch-to-zoom, front/back camera switching, photo library picker, and optional real-time face landmark detection using the Vision framework — all wrapped in SwiftUI views ready to drop into any app.
Permission timing strategy: this skill uses the use-site request pattern — camera authorization is requested when SWCameraManager is initialized. For an overview of all three permission patterns (use-site / onboarding prefetch / deferred), see Permission Prefetch Pattern.What you get
- ✓1,255 lines of source code, ready to paste
- ✓Architecture
- ✓Dependencies
- ✓Implementation
- ✓Integration Checklist
- ✓Common Customizations
- ✓Known Pitfalls
Architecture
iOS App
SWCameraManager (@Observable, NSObject)
|
|--- AVCaptureSession (session lifecycle, photo capture, zoom)
| |--- AVCaptureDeviceInput (front/back camera)
| |--- AVCapturePhotoOutput (photo capture delegate)
| |--- AVCaptureVideoDataOutput (frame-by-frame for Vision)
|
|--- Vision Framework (opt-in face tracking)
| |--- VNSequenceRequestHandler
| |--- VNDetectFaceLandmarksRequest
| |--- Normalized landmark coordinates -> SWFaceLandmarkGroup[]
|
v
SWCameraView (photo capture UI) SWFaceCameraView (face detection UI)
|--- SWCameraPreview (AVCaptureSession) |--- SWFaceCameraPreview (AVCaptureSession)
|--- PhotosPicker (photo library) |--- SWFaceTrackingOverlay (Canvas rendering)
|--- Pinch-to-zoom + zoom slider |--- Landmark toggle button
|--- Shutter button + camera switch |--- Shutter button + camera switch
Data Models
SWFaceLandmarkRegion (enum) — 11 face region types
SWFaceLandmarkGroup (struct) — region + normalized points
SWFaceLandmarkColors (struct) — configurable color schemesIntegration Checklist
1. Add Info.plist Camera Permission
Add the camera usage description to your Info.plist (or Xcode target > Info tab):
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take photos</string>Without this key, the app will crash on first camera access attempt.
2. Add All Four Files to Your Project
Copy the files into your Xcode project in the following order (dependency order):
SWFaceLandmark+iOS.swift-- data models, no dependenciesSWCameraManager+iOS.swift-- depends on SWFaceLandmarkSWCameraView+iOS.swift-- depends on SWCameraManager and the component-alert skill (SWAlertManager)SWFaceCameraView+iOS.swift-- depends on SWCameraManager and SWFaceLandmark
If your app also builds for macOS, limit these files to iOS in Build Phases > Compile Sources (the +iOS suffix marks them as iOS-only).
3. Wire Up Error Handling
SWCameraView uses SWAlertManager.shared.show(...) for error display. Make sure your root view has the .swAlert() modifier attached. If you are not using the ShipSwift alert system, replace the onError callback in onAppear:
cameraManager.onError = { message in
// Replace with your own error handling
print("Camera error: \(message)")
}4. Present SWCameraView
Neither camera view has a close button of its own, so give the user a way out: push the view with a NavigationLink (the back button closes it), or present it inside a NavigationStack with a close button in the toolbar. SWCameraView calls dismiss() after a capture or a library pick, which pops or dismisses it either way.
@State private var capturedImage: UIImage?
@State private var showCamera = false
Button("Take Photo") { showCamera = true }
.fullScreenCover(isPresented: $showCamera) {
NavigationStack {
SWCameraView(image: $capturedImage)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Close") { showCamera = false }
}
}
}
}5. Present SWFaceCameraView
SWFaceCameraView stays on screen after a capture and hands each photo to onCapture, so close it yourself when you are done:
@State private var showFaceCamera = false
Button("Face Camera") { showFaceCamera = true }
.fullScreenCover(isPresented: $showFaceCamera) {
NavigationStack {
SWFaceCameraView { capturedImage in
// handle the captured UIImage
processPhoto(capturedImage)
showFaceCamera = false
}
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Close") { showFaceCamera = false }
}
}
}
}Known Pitfalls
Camera Permission Denied Has No Recovery
If the user denies camera permission, iOS does not allow the app to re-prompt. The only recovery path is directing the user to Settings. Both SWCameraView and SWFaceCameraView include an "Open Settings" button for this case.
Simulator Does Not Support Camera
AVCaptureDevice.default(...) returns nil on the iOS Simulator. The manager will trigger onError with "Unable to access camera". Always test camera features on a physical device.
Face Detection Performance
Vision's VNDetectFaceLandmarksRequest runs per-frame on the video data output queue. On older devices, this may cause frame drops. The implementation uses alwaysDiscardsLateVideoFrames = true and a .userInitiated QoS queue to mitigate this, but performance may vary.
Front Camera Mirroring
The front camera preview is mirrored by AVCaptureVideoPreviewLayer by default. The Vision face detection code accounts for this by using .leftMirrored orientation for front-camera frames. If you modify the preview mirroring behavior, you must also update the orientation in captureOutput(_:didOutput:from:).
Session Start Timing
SWCameraManager handles the race condition where startSession() is called before setupCamera() completes by queuing a pendingStartSession flag. You do not need to add your own delay or retry logic.
Photo Capture Format
The implementation prefers JPEG format for captured photos. If JPEG is not available on the device, it falls back to the default format. The captured UIImage is always returned via the completion handler regardless of the underlying codec.
Video Data Output Always Attached
The AVCaptureVideoDataOutput is always added to the session even when face tracking is disabled. This is by design so that face tracking can be toggled at runtime without reconfiguring the session. When faceTrackingEnabled is false, the delegate method returns immediately with negligible overhead.
More in Main screens









