NextLevel: a capture engine under Instagram-style cameras in Swift
⬆️ Media Capture in Swift
At a glance
- What is it?
- NextLevel wraps AVFoundation and ARKit behind one shared camera object with photo, video, multi-clip and streaming support, still built for iOS 16 and Swift 6. It stops at capture: encoding to a shareable file and playback live in sibling projects.
- Who is it for?
- NextLevel suits teams building an Instagram, TikTok or Snapchat style capture screen where multi-clip recording, custom processing and device control are the point rather than an afterthought.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 126 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One shared object owns the camera, the preview and every delegate
The first thing to understand about NextLevel is that it is a singleton with a wide surface. NextLevel.shared exposes the preview layer you add to your view hierarchy, four separate delegates (delegate, deviceDelegate, videoDelegate, photoDelegate), the video and audio configuration objects, and start, stop, record and show methods. Everything the README demonstrates goes through that one instance.
That design is why the setup code reads the way it does. The preview layer is fetched off the shared object and added as a sublayer of a plain UIView:
let screenBounds = UIScreen.main.bounds
self.previewView = UIView(frame: screenBounds)
if let previewView = self.previewView {
previewView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
previewView.backgroundColor = UIColor.black
NextLevel.shared.previewLayer.frame = previewView.bounds
previewView.layer.addSublayer(NextLevel.shared.previewLayer)
self.view.addSubview(previewView)
}It is also the design's cost. A second capture screen, a picture-in-picture feed, or a camera running alongside an existing one all have to share one object with one preview layer and one delegate set. If your app needs two independent sessions, this library is asking you to fight it rather than helping you.
Configuring bitrate, preset and capture duration before start
Configuration happens on the shared object before the session runs, split into a videoConfiguration and an audioConfiguration. The README sets a 6 Mbps video bitrate at 1920x1080 with a ten second maximum capture duration, and 128 kbps for audio:
NextLevel.shared.videoConfiguration.bitRate = 6_000_000 // 6 Mbps
NextLevel.shared.videoConfiguration.preset = .hd1920x1080
NextLevel.shared.videoConfiguration.maximumCaptureDuration = CMTime(seconds: 10, preferredTimescale: 600)
NextLevel.shared.audioConfiguration.bitRate = 128_000 // 128 kbpsDelegates are assigned in the same phase, before start, which is the natural point because the session does not exist yet:
NextLevel.shared.delegate = self
NextLevel.shared.deviceDelegate = self
NextLevel.shared.videoDelegate = self
NextLevel.shared.photoDelegate = selfThe session then follows the view lifecycle. viewWillAppear calls start and viewWillDisappear calls stop, and recording is a separate call on the same object. Note what is not in the README: the example section ends mid-comment at a pause line, so the resume and pause semantics, and whether maximumCaptureDuration is enforced by the library or by the delegate, are not something this document answers. Test that yourself against the source before you rely on a hard clip limit.
Why USE_ARKIT and USE_TRUE_DEPTH need a compiler flag
ARKit and True Depth support are compiled in behind flags, and the reason is unusual enough to be worth reading twice. Apple rejects apps that link against ARKit or the True Depth Camera API and do not use them, so an unconditional link would get a binary rejected for merely carrying the dependency. The library makes the choice explicit at build time instead.
With CocoaPods, the flag goes into OTHER_SWIFT_FLAGS for the NextLevel target specifically:
installer.pods_project.targets.each do |target|
# setup NextLevel for ARKit use
if target.name == 'NextLevel'
target.build_configurations.each do |config|
config.build_settings['OTHER_SWIFT_FLAGS'] = ['$(inherited)', '-DUSE_ARKIT']
end
end
endThe same treatment applies to USE_TRUE_DEPTH, and with Swift Package Manager the flag goes in your own build settings. The pattern is per-target, which means a project with several targets needs the flag applied to each one that links the library, and a target without it silently gets a smaller feature set with no compile error to explain why.
Depth capture and portrait effects matte are on the feature list behind this same door, as is video zoom and the true depth camera itself. If depth data is your product, verify the flag actually landed in the target that links NextLevel rather than the app target.
Adding the package: SwiftPM, CocoaPods or dropped-in sources
Swift Package Manager is the route the README recommends, with a version floor at 0.19.1:
dependencies: [
.package(url: "https://github.com/NextLevel/NextLevel", from: "0.19.1")
]CocoaPods uses the same version line, which is worth noticing because a mismatch between the two is the kind of thing that produces confusing behaviour when a pod and a package end up in the same workspace:
pod "NextLevel", "~> 0.19.1"There is a third option the README mentions briefly: drop the files from the Sources directory into an Xcode project. That is the only way this library can be inspected and patched locally, which has real value given that the published documentation stops mid-sentence in the recording section. It also means you own the merge.
Permissions are ordinary iOS usage descriptions, and the README gives the exact keys:
<key>NSCameraUsageDescription</key>
<string>Allowing access to the camera lets you take photos and videos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allowing access to the microphone lets you record audio.</string>Note that this library does not request permissions for you. Nothing in the README describes a permission check, so your app owns that step before the first start call.
What Swift 6 changed: AsyncStream events, Sendable and OSLog
The current release targets iOS 16, Swift 6 and Xcode 16, and the change list is more than cosmetic. Native async/await support and AsyncStream events for camera state changes sit alongside Swift 6 strict concurrency, Sendable conformance and actor isolation, which is a large amount of surface for a library whose state is a shared mutable singleton. Those two things pull in opposite directions, and the project is candid that the legacy delegate API still works alongside the modern one.
Other entries in that list are worth more to an adopter than the concurrency headline. The list credits fixes for an AudioChannelLayout crash, a photo capture crash, audio interruption handling and video timing issues, and separate fixes for timestamp offset bugs when merging multi-clip recordings. Clip merging is the feature this library is known for, so timestamp bugs there were the worst possible place to have them.
Two smaller items change daily behaviour. Errors now carry contextual descriptions through LocalizedError with recovery suggestions, so a failed capture can tell the user what to do. And shouldOptimizeForNetworkUse on the video configuration lets you trade local recording speed against network write behaviour, which matters most for the streaming path.
Structured logging through OSLog means the library's internal state is filterable in Console rather than only visible in a debugger attached to your process.
Capture is the product; export and playback are separate packages
The feature table is long, and it is worth sorting it into what the library owns and what it borrows from the platform. Vine-like multi-clip recording and editing, photo capture in raw, JPEG and video frame form, custom buffer processing, animated GIF output, face recognition and QR and barcode recognition are all listed as library features. White balance, focus, exposure, flash, torch, mirroring, low light boost, zoom and frame-rate control including slow motion are device controls passed through to the capture device.
The honest boundary is the end of the capture session. The README points at NextLevelSessionExporter for exporting a video and at Player for playback, both separate repositories. So the promise of a Vine-style clip editor is a promise about buffers and session data, not about handing you a file on the camera roll with the right orientation and metadata.
For an existing app this is usually the right split. If you already have an exporter, or you write to your own AVAsset pipeline, NextLevel saves you the part that is genuinely hard to build well, which is multi-clip session handling and buffer hooks for image processing. If you are starting from nothing, you are assembling three repositories.
Where NextLevel is the wrong choice
Three cases stand out. The first is a deployment target below iOS 16. The requirements are stated as iOS 16.0 or later, Swift 6.0 and Xcode 16.0 or later, so an app still supporting older iOS versions cannot adopt the current line without raising its floor, and the library offers no documented older branch for that.
The second is cross-platform. This is an iOS library by construction: ARKit, True Depth, UIScreen and UIView are all in the first example. There is nothing here for Android or for a server-side pipeline, and the Augmentable topics in the repository reflect an iOS-only tool.
The third is a single-shot photo app. Every capability arrives with a delegate surface, a configuration object and a session lifecycle that assumes video is the main event. A team that just wants to call something like UIImagePickerController and receive a UIImage is paying for a session manager it will never use.
The documentation gap is the practical concern on top of all three. The README example is cut off in the middle of the recording section, the exporter is a separate project, and the feature list is a table rather than an explanation of when each control applies. The repository does carry a SwiftLint configuration and a .swift-version file, so the project is disciplined, but the published answer to "how do I pause and resume" is not in the README.
Editorial conclusion
NextLevel suits teams building an Instagram, TikTok or Snapchat style capture screen where multi-clip recording, custom processing and device control are the point rather than an afterthought. It is a poor fit if you want a drop-in capture and export pipeline, because the library ends at the session and NextLevelSessionExporter and Player are separate additions, and it is the wrong base for anything that needs two independent cameras in one app, since everything hangs off the NextLevel.shared singleton. Verify three things before committing. First, that your app genuinely needs iOS 16, Swift 6 and Xcode 16 as the requirements state, which rules the library out of any deployment target still in the 15 range. Second, whether you need ARKit or True Depth, because both compile out unless you add -DUSE_ARKIT or -DUSE_TRUE_DEPTH by hand, and linking them without using the API is a documented App Store rejection. Third, how your build manages those flags, since the CocoaPods route needs a Podfile loop that rewrites OTHER_SWIFT_FLAGS for the NextLevel target specifically. Pin 0.19.1 and read the pause and resume section, because the README example is cut off mid-line there.
Frequently asked questions
How do I install NextLevel in an iOS project?
Swift Package Manager is the recommended route, adding .package(url: "https://github.com/NextLevel/NextLevel", from: "0.19.1") to your Package.swift dependencies. CocoaPods uses pod "NextLevel", "~> 0.19.1", and you can also drop the Sources files into the Xcode project directly.
How do I enable ARKit support in NextLevel?
Compile with the flag USE_ARKIT, added as -DUSE_ARKIT to OTHER_SWIFT_FLAGS for the NextLevel target, or as USE_TRUE_DEPTH for the depth camera. The flag is required because Apple rejects apps that link these APIs without using them.
Does NextLevel export the recorded video file?
Not on its own. The README points to NextLevelSessionExporter for video exporting and to Player for playback, both separate projects. NextLevel covers capture sessions, multi-clip recording and buffer processing.
What are the minimum requirements for NextLevel?
The README states iOS 16.0 or later, Swift 6.0 and Xcode 16.0 or later. The async and await APIs and Task.sleep(for:) are tied to that iOS 16 floor.
Does NextLevel handle camera and microphone permissions itself?
The README only instructs you to add NSCameraUsageDescription and NSMicrophoneUsageDescription to Info.plist, and does not document a permission request inside the library. Requesting access is left to the host app.