Search Docs
Loading...
Skip to content

Video Editor in iOS

Professional video editing for your iOS app—edit clips, add effects, trim footage, and export to MP4. Runs entirely on the mobile device with no server dependencies.

Video Editor starter kit screenshot

10 mins
estimated time
Download
GitHub

Pre-Requisites#

This guide assumes basic familiarity with iOS and Swift. You will need:

  • Latest Xcode
  • Swift 5.9 or later
  • iOS 16.0+

Get Started#

Start with a complete, runnable iOS starter kit project.

Step 1: Clone the Repository#

Terminal window
git clone -b v1.73.1 https://github.com/imgly/starterkit-video-editor-ios.git
cd starterkit-video-editor-ios

Step 2: Open and Run#

Open the project in Xcode and run on a simulator or connected device:

  1. Open starterkit-video-editor-ios.xcodeproj in Xcode
  2. Select your target device or simulator
  3. Press ⌘R to build and run

The sample app shows a “Launch Editor” button. Tapping it presents the video editor:

var body: some View {
Editor(settings)
.imgly.configuration {
VideoEditorConfiguration()
}
}

The full implementation of the starter kit lives in the starter-kit/ folder:

starter-kit/
├── VideoEditorStarterKit.swift # SwiftUI view that launches the editor
├── VideoEditorConfiguration.swift # Editor configuration (callbacks + UI components)
├── callbacks/
│ ├── OnCreate+Video.swift # Editor initialization logic
│ └── OnExport+Video.swift # Export flow and handling (MP4)
└── components/
├── BottomPanel+Video.swift # Timeline component configuration
├── CanvasMenu+Video.swift # Canvas menu configuration
├── Dock+Video.swift # Dock configuration
├── InspectorBar+Video.swift # Inspector bar configuration
└── NavigationBar+Video.swift # Navigation bar configuration

Configuring the Starter Kit#

The starter kit provides a generic structure and behavior that you can customize to match your needs. Since the implementation is part of your codebase, you can add, remove, or modify functionality as you wish.

The entry point is VideoEditorStarterKit.swift, which creates the Editor view and applies the configuration:

Editor(settings)
.imgly.configuration {
VideoEditorConfiguration()
}

You can configure the editor based on your business logic — for example, loading a scene from a previous editing session or displaying different UI for different users. Add properties to VideoEditorStarterKit and use them in the .imgly.onCreate callback to customize scene loading or other behavior.

Set Up a Scene#

The scene setup logic is located in OnCreate+Video.swift as part of the defaultCreateScene callback:

let sceneURL = Bundle(for: VideoEditorConfiguration.self).url(forResource: "video-empty", withExtension: "scene")!
try await engine.scene.load(from: sceneURL)

CE.SDK offers multiple ways to load a scene into the editor — from a video URL, a template archive, a blank video canvas, or a .scene file.

Video Duration Constraints#

Enforce minimum and maximum clip durations in the video editor. Send the setVideoDurationConstraints event in an onLoaded callback:

/// Example: Apply video duration constraints in the `onLoaded` callback.
/// Add this logic before or after the existing `onLoaded` handler in `VideoEditorConfiguration`.
extension VideoEditorConfiguration {
static var durationConstraintsOnLoadedHandler: OnLoaded.Handler {
{ context, existing in
// Enforce all videos to be between 10 and 20 seconds
context.eventHandler.send(
.setVideoDurationConstraints(
minimumVideoDuration: 10,
maximumVideoDuration: 20,
),
)
// Continue with the existing onLoaded logic
try await existing()
}
}
}

Enable IMG.LY Camera#

Instead of the system camera, you can use the camera feature provided by IMG.LY. Replace Dock.Buttons.systemCamera() with Dock.Buttons.imglyCamera() in Dock+Video.swift:

Dock.Buttons.imglyCamera(icon: { _ in Image.imgly.addCameraBackground }) // Camera capture

In addition, add the IMG.LY camera dependency to your project via SPM by adding the IMGLYCamera product from the same package URL.

Customize Assets#

The asset source setup is located in OnCreate+Video.swift as part of the defaultLoadAssetSources callback. Enable or disable individual sources:

let assetSources: [String: URL] = [
Engine.DefaultAssetSource.sticker.rawValue: Engine.assetBaseURL,
Engine.DefaultAssetSource.vectorPath.rawValue: Engine.assetBaseURL,
Engine.DefaultAssetSource.filterLut.rawValue: Engine.assetBaseURL,
Engine.DefaultAssetSource.filterDuotone.rawValue: Engine.assetBaseURL,
Engine.DefaultAssetSource.colorsDefaultPalette.rawValue: Engine.assetBaseURL,
Engine.DefaultAssetSource.effect.rawValue: Engine.assetBaseURL,
Engine.DefaultAssetSource.blur.rawValue: Engine.assetBaseURL,
Engine.DefaultAssetSource.typeface.rawValue: Engine.assetBaseURL,
Engine.DefaultAssetSource.cropPresets.rawValue: Engine.assetBaseURL,
Engine.DefaultAssetSource.pagePresets.rawValue: Engine.assetBaseURL,
Engine.DemoAssetSource.image.rawValue: Engine.assetBaseURL,
Engine.DemoAssetSource.video.rawValue: Engine.assetBaseURL,
Engine.DemoAssetSource.audio.rawValue: Engine.assetBaseURL,
Engine.DemoAssetSource.textComponents.rawValue: Engine.assetBaseURL,
]
try await withThrowingTaskGroup(of: Void.self) { group in
for assetSource in assetSources {
group.addTask {
try await engine.populateAssetSource(id: assetSource.key, baseURL: assetSource.value)
}
}
try await group.waitForAll()
}
let localAssetSources: [Engine.DemoAssetSource] = [
.imageUpload,
.audioUpload,
.videoUpload,
]
for localAssetSource in localAssetSources {
try engine.asset.addLocalSource(
sourceID: localAssetSource.rawValue,
supportedMimeTypes: localAssetSource.mimeTypes,
)
}
try await engine.asset.addSource(TextAssetSource(engine: engine))
try engine.asset.addSource(PhotoRollAssetSource(engine: engine))

For production deployments, self-hosting assets is required—the IMG.LY CDN is intended for development only. See Serve Assets for downloading assets, configuring baseURL and excluding unused sources to optimize load times.

Customize Export Functionality#

Export handling logic is located in OnExport+Video.swift as part of the onExport callback.

The default implementation exports the scene as MP4 and opens the system share sheet:

guard let page = try engine.scene.getCurrentPage() else {
throw EditorError("No page was found.")
}
eventHandler.send(.exportProgress(.relative(0)))
let mimeType: MIMEType = .mp4
let stream = try await engine.block.exportVideo(page, mimeType: mimeType) { _ in }
var lastReportedProgress = 0
for try await export in stream {
try Task.checkCancellation()
switch export {
case let .progress(_, encodedFrames, totalFrames):
let progress = Int((Float(encodedFrames) / Float(totalFrames)) * 100)
if progress > lastReportedProgress {
lastReportedProgress = progress
eventHandler.send(.exportProgress(.relative(Float(progress) / 100)))
}
case let .finished(video: videoData):
let url = FileManager.default.temporaryDirectory.appendingPathComponent(
"Export",
conformingTo: mimeType.uniformType,
)
try videoData.write(to: url, options: [.atomic])
eventHandler.send(.exportCompleted { eventHandler.send(.shareFile(url)) })
return
}
}
try Task.checkCancellation()
throw EditorError("Failed to export the content.")

Customize (Optional)#

Base URL#

The starter kit uses Engine.assetBaseURL by default, which points to https://cdn.img.ly/packages/imgly/cesdk-engine/1.73.1/assets. To self-host assets, download the asset zip file and pass a custom baseURL to your EngineSettings.

Color Scheme#

CE.SDK supports light and dark modes out of the box, plus automatic system preference detection. Apply SwiftUI’s .preferredColorScheme modifier to the Editor view to switch themes.

See Theming for more details.

Localization#

See Localization for supported languages, adding support for new languages, and replacing existing keys.

UI Layout#

All configurable components are located in the components/ folder:

  • BottomPanel+Video.swift — Timeline component configuration
  • CanvasMenu+Video.swift — see Canvas Menu for full configuration options
  • Dock+Video.swift — see Dock for full configuration options
  • InspectorBar+Video.swift — see Inspector Bar for full configuration options
  • NavigationBar+Video.swift — see Navigation Bar for full configuration options

Troubleshooting#

Editor doesn’t load#

  • Check onCreate: Ensure the onCreate callback loads a scene successfully
  • Verify the baseURL: Assets must be accessible from the CDN or your self-hosted location
  • Check Xcode console: Look for errors in the Xcode debug console

Assets don’t appear#

  • Check network requests: Make sure the device or simulator is connected to the internet
  • Self-host assets for production: See Serve Assets to host assets on your infrastructure
  • Check Xcode console: Look for errors in the Xcode debug console

Export fails or produces blank video#

  • Wait for content to load: Ensure video clips are fully loaded before exporting
  • Check Xcode console: Look for errors in the Xcode debug console

Watermark appears in production#

  • Add your license key: Set the license property in your EngineSettings
  • Sign up for a trial: Get a free trial license at img.ly/forms/free-trial

Next Steps#