Skip to content

Liampronan/DualCameraKit

Repository files navigation

DualCameraKit

Swift SPM Compatible iOS WIP License: MIT

Simultaneous front & back iOS camera capture made simple.


Photo Capture

Video Capture

Status Overview

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.

Table of Contents

What It Does

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

Installing

Since DualCameraKit is published using Swift Package Manager, you can install it by following these steps:

In Xcode:

  1. Go to File > Add Packages...
  2. In the search bar, paste this repository URL.
  3. Select the version rule (e.g., "Up to Next Major" is recommended for most cases)
  4. Click Add Package
  5. Select the DualCameraKit library product
  6. Click Add Package to complete the installation

In Package.swift:

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"]
    )
]

OS/Requirements:

Permissions

  • [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 videoSaveStrategy and photoSaveStrategy for customizing this behavior.

Device

  • Live, nonsimulator device, iOS 17+ for camera usage.
  • The simulator uses a mocked camera.

Importing DualCameraKit

After installation, you can import the library in your Swift files:

import DualCameraKit

Running the Demo App: Local Code Signing Setup

To get the demo app running, you'll need to:

  1. set code signing to automatic,
  2. 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.

Overview: the three ways to use this library

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.

  1. βœ… 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.
  1. 🚧 DualCameraDisplayView and DualCameraController
  • medium customization - useful for using pre-configured dual camera layouts while customizing the control UI and post-capture behavior.
  • the DualCameraDisplayView renders streams managed by the DualCameraController
  • you're responsible for wiring up UI for photo capture, video recording management, and layout config.
  1. 🚧 Raw Components
  • full customization - useful for uncharted territory e.g., you need to manipulate camera streams before they are rendered.

Basic Usage

DualCameraScreen - drop-in, full-screen component

The simplest way to use DualCameraKit is with the default configuration:

struct ContentView: View {
    var body: some View {
        DualCameraScreen()
    }
}

DualCameraScreen - Customization

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)
    }
}

DualCameraScreen - Parameters

Parameter Type Default Description
viewModel DualCameraViewModel .default() Provides complete configuration for the camera screen including layout, video recording options, and media saving strategies.

DualCameraViewModel Configuration

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.

Testing Support

For testing, you can use the mock implementations:

let testViewModel = DualCameraViewModel(
    dualCameraController: DualCameraMockController(),
    videoSaveStrategy: .custom { _ in },
    photoSaveStrategy: .custom { _ in }
)

Customization

Camera Layout Types

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.

.piP(miniCamera:, miniCameraPosition:)


.topLeading

.topTrailing

.bottomLeading

.bottomTrailing

.stackedVertical

.sideBySide

Video Recording Modes

  • This library offers several implementations of DualCameraVideoRecorderType that 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

Troubleshooting

Deep Dives

  • 🚧 Explain our different approaches (dual streams) vs. PiPVideoMixer (single stream) vs ReplayKit (screen capture, requires user permission each time)
  • 🚧 Add some diagrams

Limitations

  • 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

API Reference

🚧

References

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.

License

This project is available under the MIT License.

About

Capture iOS front & back cameras simultaneously.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages