Simultaneous front & back iOS camera capture made simple.
![]() Photo Capture |
![]() Video Capture |
Current Status: Alpha release working towards 1.0
Pre-release available: v0.3.0-alpha.
Legend: β = Implemented | π§ = In Progress
| Category | Status | Description |
|---|---|---|
| π± UI Components | β | Rendering dual cameras in SwiftUI with various layout options: β’ Picture-in-picture β’ Split vertical β’ Split horizontal |
| πΈ Photo Capture | β | Implemented via screen capture |
| π¬ Video Capture | v1 β | ReplayKit Mode: High-def output (requires permission each time) CPU-based Mode: Medium-def output (one-time permission only) |
| π§© Architecture | π§ | In progress: De-coupling components to offer 3 layers of customizability: β’ Drop-in screen β’ Compositional views β’ Low-level components |
| π₯ GPU Acceleration | π§ | Up Next: Adding GPU video capture for high-def recording without recurrent permission requests |
| π€ Audio | π§ | Future: Adding audio capture support for video recording. |
- What It Does
- Requirements
- Installing
- Running the Demo App
- Basic Usage
- Customization
- Troubleshooting
- Deep Dives
- Limitations
- API Reference
- References
- License
DualCameraKit is an iOS library that makes simultaneous front & back camera capture simple β combining the view and the viewer in a single shot, as seen in apps like Snapchat and BeReal.
For simple, drop-in functionality, you can use DualCameraScreen, a SwiftUI View with buttons for photo capture, video recording, and toggling through the different dual-camera layouts and recording modes.
For deeper customizability, you can access the lower-level components that DualCameraScreen is built on
Since DualCameraKit is published using Swift Package Manager, you can install it by following these steps:
- Go to File > Add Packages...
- In the search bar, paste this repository URL.
- Select the version rule (e.g., "Up to Next Major" is recommended for most cases)
- Click Add Package
- Select the
DualCameraKitlibrary product - Click Add Package to complete the installation
If you're developing a Swift package that depends on DualCameraKit, add it to your package dependencies:
dependencies: [
.package(url: "https://url-to-dualcamerakit-repo.git", from: $VERSION_STRING_HERE$)
],
targets: [
.target(
name: "YourTarget",
dependencies: ["DualCameraKit"]
)
]- [Required] Add camera permissions to your app's
Info.plist-Privacy - Camera Usage Description
- This camera permission is required because this library needs to access cameras in order to be useful.
- [Optional] Add photo library permissions to your app's
Info.plist-Privacy - Photo Library Additions Usage Description
- This photo library addition permission is optional because you don't need to save to the user's media library after capture.
- See
videoSaveStrategyandphotoSaveStrategyfor customizing this behavior.
- Live, nonsimulator device, iOS 17+ for camera usage.
- The simulator uses a mocked camera.
After installation, you can import the library in your Swift files:
import DualCameraKitTo get the demo app running, you'll need to:
- set code signing to automatic,
- select your development team - this dropdown appears after setting code signing to automatic
Since this library currently requires a real device, you cannot run it on a simulator.

Three are three different sets of components this library exposes, ranging from higher-level (drop-in, less customizable) to lower-level (more customizable). Each is built on top of the next lower level.
- β
DualCameraScreen
- drop-in, least customizable.
- a full-screen SwiftUI view which includes buttons for photo capture, video recording, and toggling through the different dual-camera layouts and recording modes.
- π§
DualCameraDisplayViewandDualCameraController
- medium customization - useful for using pre-configured dual camera layouts while customizing the control UI and post-capture behavior.
- the
DualCameraDisplayViewrenders streams managed by theDualCameraController - you're responsible for wiring up UI for photo capture, video recording management, and layout config.
- π§ Raw Components
- full customization - useful for uncharted territory e.g., you need to manipulate camera streams before they are rendered.
The simplest way to use DualCameraKit is with the default configuration:
struct ContentView: View {
var body: some View {
DualCameraScreen()
}
}You can customize the screen by providing your own DualCameraViewModel
// Custom initialization with specific layout
let customViewModel = DualCameraViewModel(
dualCameraController: DualCameraController(),
layout: .sideBySide,
videoRecorderMode: .replayKit(),
videoSaveStrategy: .custom { url in
// Custom video handling
print("Video saved to: \(url)")
},
photoSaveStrategy: .custom { image in
// Custom photo handling
saveToCloudService(image)
}
)
struct ContentView: View {
var body: some View {
DualCameraScreen(viewModel: customViewModel)
}
}| Parameter | Type | Default | Description |
|---|---|---|---|
viewModel |
DualCameraViewModel |
.default() |
Provides complete configuration for the camera screen including layout, video recording options, and media saving strategies. |
| Parameter | Type | Default | Description |
|---|---|---|---|
dualCameraController |
DualCameraControlling |
Device-specific controller | Core camera controller. Uses DualCameraMockController on simulator and DualCameraController on device. |
layout |
DualCameraLayout |
.piP(miniCamera: .front, miniCameraPosition: .bottomTrailing) |
Determines how cameras are displayed (picture-in-picture, side-by-side, or stacked). |
videoRecorderMode |
DualCameraVideoRecordingMode |
.cpuBased(.init(photoCaptureMode: .fullScreen)) |
Configures video recording strategy and quality. |
videoSaveStrategy |
VideoSaveStrategy |
.videoLibrary(service: CurrentDualCameraEnvironment.mediaLibraryService) |
Strategy for saving recorded videos. |
photoSaveStrategy |
PhotoSaveStrategy |
.photoLibrary(service: CurrentDualCameraEnvironment.mediaLibraryService) |
Strategy for saving captured photos. |
Note on Default Media Handling: By default, all photos and videos are saved to the device's photo library. This requires the user to grant permission when first capturing media. The default implementation handles permission requests, file cleanup, and provides success feedback. When using custom strategies, you'll need to implement these aspects yourself if needed.
For testing, you can use the mock implementations:
let testViewModel = DualCameraViewModel(
dualCameraController: DualCameraMockController(),
videoSaveStrategy: .custom { _ in },
photoSaveStrategy: .custom { _ in }
)public enum DualCameraLayout {
case sideBySide
case stackedVertical
case piP(miniCamera: DualCameraSource, miniCameraPosition: MiniCameraPosition)
}Note: these screenshots are using colors to mock the front (purple) and back (yellow) cameras β this is how we're mocking things in the simulator because it doesn't have a hardware camera.
![]() .topLeading |
![]() .topTrailing |
![]() .bottomLeading |
![]() .bottomTrailing |
- This library offers several implementations of
DualCameraVideoRecorderTypethat offer various methods of recording video. - You can choose which type you'd like by invoking
DualCameraControlling.startVideoRecording(recorderType:)
| Type | Implementation Status | Quality | Description | Core Technology | Permissions Required | Layout Dependence |
|---|---|---|---|---|---|---|
.cpuBased |
β | Medium | Takes continuous screenshots | DualCameraPhotoCapturing |
Camera (one-time) | Captures screen layout as-is |
.replayKit |
β | High | System screen recording API | ReplayKit | Camera (one-time) + ReplayKit (per-use) | Captures screen layout as-is |
.gpuBasedUncomposited |
π§ | High | Direct Metal capture | Metal/GPU acceleration | Camera (one-time) | Produces separate video streams (manual composition needed) |
.gpuBasedComposited |
π§ | High | Direct Metal capture with automatic composition | Metal/GPU acceleration | Camera (one-time) | Automatically composes videos according to DualCameraLayout |
- π§ Explain our different approaches (dual streams) vs.
PiPVideoMixer(single stream) vsReplayKit(screen capture, requires user permission each time) - π§ Add some diagrams
- The library works fully on-device only! Limited, non-camera use in simulator (including previews).
- Why? Because the simulator doesn't have access to camera.
- It runs in simulator & previews using mocked implementations for the cameras. The goal here is to allow you to integrate as much as possible using previews, for example, iterating on layouts.
- You should still test on-device as part of your full testing flow to ensure things work as you expect.
- iOS only. iPad support is a future enhancement. Other platforms only have one camera
π§
Part of this project was adapted from Apple's code in AVMultiCamPiP: Capturing from Multiple Cameras. Some significant updates here: this library ports the functionality to SwiftUI, including using a dual-stream approach with multiple types of video recorders vs. Apple's approach of mixing together both streams into a single CVPixelBuffer containing both camera sources.
This project is available under the MIT License.







