Version 1.73 introduces a major iOS UI architecture change:
PhotoEditor,DesignEditor,VideoEditor,PostcardEditorandApparelEditorare no longer available.- A single
Editorview is now the foundation for all solutions. - The former solution UIs are replaced by Starter Kits that you copy into your app and customize per your business logic.
This guide covers the iOS migration path and lists the public API changes with before/after examples.
Choose Your Migration Path#
Path A: Adopt a Starter Kit (Recommended)#
Use the starter kit that matches your previous solution:
| Legacy View | Starter Kit Guide | GitHub Repo |
|---|---|---|
PhotoEditor | Photo Editor | imgly/starterkit-photo-editor-ios |
DesignEditor | Design Editor | imgly/starterkit-design-editor-ios |
VideoEditor | Video Editor | imgly/starterkit-video-editor-ios |
PostcardEditor | Postcard Editor | imgly/starterkit-postcard-editor-ios |
ApparelEditor | T-Shirt Designer | imgly/starterkit-apparel-editor-ios |
Each starter kit includes:
- A demo app target to run the starter kit.
- A reusable
StarterKit/module with the configuration class. - A configuration class (for example
PhotoEditorConfiguration) as the starting point. - Encapsulated callback and component setup that you can modify directly in your app codebase per your business logic.
Path B: Build a Fully Custom Editor#
We recommend using one of our starter kits, as they help with editor setup and provide a proven structure and architecture. However, you can still use the Editor view without any configuration class and provide your own scene creation, assets, and UI component configuration inline.
Note that by default Editor launches an empty editor, meaning you have to configure all the components manually:
Editor(settings) .imgly.configuration { EditorConfiguration { builder in builder.onCreate { engine, _ in ... } builder.onExport { engine, eventHandler, _ in ... } builder.dock { dockBuilder in ... } builder.navigationBar { navBuilder in ... } // ... } }Public API Changes#
1. Editor Entry Point#
Before:#
let settings = EngineSettings(license: "<license>", userID: "<user-id>")
DesignEditor(settings) .imgly.onCreate { engine in // Your configuration } .imgly.onExport { engine, eventHandler in // Your configuration }Now:#
let settings = EngineSettings(license: "<license>", userID: "<user-id>")
Editor(settings) .imgly.configuration { // DesignEditorConfiguration comes with the starter kit and is not available otherwise DesignEditorConfiguration() }See Section 2 for all the ways to customize the configuration.
What changed#
- Added:
Editorview as the single entry point for all editor types. - Removed:
PhotoEditor,DesignEditor,VideoEditor,PostcardEditor,ApparelEditorviews (marked as@available(*, unavailable)). - Removed:
.imgly.onCreate,.imgly.onExport,.imgly.onLoaded,.imgly.onChangedSwiftUI environment modifiers. All callbacks are now configured throughEditorConfiguration. - Removed:
.imgly.dockItems,.imgly.navigationBarItemsSwiftUI environment modifiers. All UI components are now configured throughEditorConfiguration.
2. Configuration Changes#
Before v1.73, configuration was done through SwiftUI environment modifiers on solution-specific views. Each view had built-in defaults for its use case.
From v1.73, configuration is unified through EditorConfiguration applied via .imgly.configuration { ... }.
Below you can find the migration of all configuration options:
Before:#
DesignEditor(settings) .imgly.onCreate { engine in ... } .imgly.onExport { engine, eventHandler in ... } .imgly.onLoaded { context in ... } .imgly.onChanged { update, context in ... } .imgly.onClose { engine, eventHandler in ... } .imgly.onError { error, eventHandler in ... } .imgly.onUpload { engine, sourceID, asset in ... } .imgly.colorPalette([...]) .imgly.assetLibrary { DefaultAssetLibrary() } .imgly.dockItems { context in ... } .imgly.modifyDockItems { modifier in ... } .imgly.navigationBarItems { context in ... } .imgly.modifyNavigationBarItems { modifier in ... } .imgly.inspectorBarItems { context in ... } .imgly.modifyInspectorBarItems { modifier in ... } .imgly.canvasMenuItems { context in ... } .imgly.modifyCanvasMenuItems { modifier in ... }Now:#
Editor(settings) .imgly.configuration { EditorConfiguration { builder in builder.onCreate { engine, _ in ... } builder.onExport { engine, eventHandler, _ in ... } builder.onLoaded { context, _ in ... } builder.onChanged { update, context, _ in ... } builder.onClose { engine, eventHandler, _ in ... } builder.onError { error, eventHandler, _ in ... } builder.onUpload { engine, sourceID, asset, _ in ... } builder.colorPalette([...]) builder.assetLibrary { libBuilder in ... } builder.dock { dockBuilder in ... } builder.navigationBar { navBuilder in ... } builder.inspectorBar { inspectorBuilder in ... } builder.canvasMenu { canvasMenuBuilder in ... } builder.bottomPanel { bottomPanelBuilder in ... } } }What changed#
- All callbacks and UI components are now configured through a single
EditorConfigurationbuilder instead of individual SwiftUI environment modifiers. - Callback signatures changed: each handler now receives an
existingparameter for chaining with the default implementation (see Section 3).
3. Callback Signatures#
Callback signatures have changed from closures to Handler types that include an existing parameter for chaining:
Before:#
// OnCreate.imgly.onCreate { engine in // Your configuration}
// OnExport.imgly.onExport { engine, eventHandler in // Your configuration}Now:#
// OnCreate.Handler — receives engine + existing (previous handler for chaining)builder.onCreate { engine, existing in // Your configuration}
// OnExport.Handler — receives engine + eventHandler + existingbuilder.onExport { engine, eventHandler, existing in // Your configuration}The existing parameter allows you to call the default implementation from the starter kit configuration class and extend it, rather than replacing it entirely.
All callback types#
| Callback | Handler Signature | Purpose |
|---|---|---|
OnCreate | (Engine, existing) async throws -> Void | Scene creation and asset loading |
OnExport | (Engine, EditorEventHandler, existing) async throws -> Void | Export and share |
OnLoaded | (OnLoaded.Context, existing) async throws -> Void | Post-load setup (register tasks, set video constraints) |
OnChanged | (EditorStateChange, OnChanged.Context, existing) throws -> Void | React to editor state changes (page, view mode, gestures) |
OnClose | (Engine, EditorEventHandler, existing) -> Void | Editor dismissal |
OnError | (Error, EditorEventHandler, existing) -> Void | Error handling |
OnUpload | (Engine, String, AssetDefinition, existing) async throws -> AssetDefinition | Asset upload processing |
4. Configuration Class Pattern#
Starter kit configuration classes extend EditorConfiguration and provide solution-specific defaults:
final class PhotoEditorConfiguration: EditorConfiguration { // Settings override var zoomPadding: CGFloat? { 0 }
// Callbacks — each provides a default implementation override var onCreate: OnCreate.Handler? { Self.defaultOnCreateHandler } override var onLoaded: OnLoaded.Handler? { Self.defaultOnLoadedHandler } override var onChanged: OnChanged.Handler? { Self.defaultOnChangedHandler } override var onExport: OnExport.Handler? { Self.defaultOnExportHandler }
// UI Components — each provides default buttons/items override var navigationBar: NavigationBar.Configuration? { Self.defaultNavigationBar } override var dock: Dock.Configuration? { Self.defaultDock } override var inspectorBar: InspectorBar.Configuration? { Self.defaultInspectorBar } override var canvasMenu: CanvasMenu.Configuration? { Self.defaultCanvasMenu }}Each starter kit overrides a different subset of properties depending on its use case. For example, VideoEditorConfiguration also overrides bottomPanel to include a timeline, while ApparelEditorConfiguration and PostcardEditorConfiguration override onChanged for view mode handling.
Phased onCreate#
The onCreate callback in each starter kit is split into independently overridable phases. Each phase handles a specific part of the initialization:
extension PhotoEditorConfiguration { static func defaultOnCreate( preCreateScene: @escaping OnCreate.Callback = defaultPreCreateScene, createScene: @escaping OnCreate.Callback = defaultCreateScene, loadAssetSources: @escaping OnCreate.Callback = defaultLoadAssetSources, postCreateScene: @escaping OnCreate.Callback = defaultPostCreateScene, ) -> OnCreate.Callback { { engine in try await preCreateScene(engine) try await createScene(engine) try await loadAssetSources(engine) try await postCreateScene(engine) } }}To load a custom scene, edit the defaultCreateScene property in OnCreate+Photo.swift:
static let defaultCreateScene: OnCreate.Callback = { engine in try await engine.scene.load(from: mySceneURL)}When you download a starter kit, you own the code. Edit the files directly to take full control of the solution.
5. Editor Defaults#
Before:#
- When using
PhotoEditor, callbacks had built-in defaults: the editor loaded a default image, registered default asset sources, and configured photo-specific settings automatically. - UI components had built-in defaults: the dock showed adjustments, filters, effects, blur, crop, text, shapes, and stickers buttons.
- The navigation bar showed close, undo, redo, preview, and export buttons.
Now:#
- The
Editorview itself has no defaults. If you do not provide a configuration, the editor creates a blank scene only. - Default
onExportdoes nothing. - All components (
Dock,NavigationBar,InspectorBar,CanvasMenu) are empty by default. - To get the previous behavior, use a starter kit configuration class (for example
PhotoEditorConfiguration) which bundles all the appropriate defaults. You can also use the baseEditorConfigurationdirectly with the builder to build a fully custom configuration without a starter kit:
Editor(settings) .imgly.configuration { EditorConfiguration { builder in builder.onCreate { engine, _ in // Must be implemented — create or load a scene } builder.onExport { engine, eventHandler, _ in // Must be implemented — export and share } builder.dock { dockBuilder in dockBuilder.items { _ in Dock.Buttons.adjustments() Dock.Buttons.filter() Dock.Buttons.effect() } } builder.navigationBar { navBuilder in navBuilder.items { _ in NavigationBar.ItemGroup(placement: .topBarLeading) { NavigationBar.Buttons.closeEditor() } NavigationBar.ItemGroup(placement: .topBarTrailing) { NavigationBar.Buttons.undo() NavigationBar.Buttons.redo() NavigationBar.Buttons.export() } } } } }6. UI Component Configuration#
UI components are now configured through builder closures on the configuration class instead of SwiftUI environment modifiers.
Dock#
builder.dock { dockBuilder in dockBuilder.items { _ in Dock.Buttons.adjustments() Dock.Buttons.filter() Dock.Buttons.effect() Dock.Buttons.blur() Dock.Buttons.crop() Dock.Buttons.textLibrary() Dock.Buttons.shapesLibrary() Dock.Buttons.stickersLibrary() }}Navigation Bar#
builder.navigationBar { navBuilder in navBuilder.items { _ in NavigationBar.ItemGroup(placement: .topBarLeading) { NavigationBar.Buttons.closeEditor() } NavigationBar.ItemGroup(placement: .topBarTrailing) { NavigationBar.Buttons.undo() NavigationBar.Buttons.redo() NavigationBar.Buttons.togglePreviewMode() NavigationBar.Buttons.export() } }}Modifying Existing Components#
You can modify default components from a starter kit using the modify method or directly change the starter kit callback.
PhotoEditorConfiguration { builder in builder.navigationBar { navBuilder in navBuilder.modify { _, items in items.replace(id: NavigationBar.Buttons.ID.closeEditor) { NavigationBar.Buttons.closeEditor(label: { _ in Label("Home", systemImage: "house") }) } } }}Bottom Panel#
A new bottomPanel configuration option allows rendering content above the dock. The Video starter kit uses this for the timeline:
static var defaultBottomPanel: BottomPanel.Configuration { BottomPanel.Configuration { builder in builder.content { context in DefaultTimelineComponent(context: context) } }}Color Palette#
Set a custom color palette through the builder:
EditorConfiguration { builder in builder.colorPalette([ NamedColor("Primary", CGColor(sRGBRed: 0, green: 0, blue: 0, alpha: 1)), NamedColor("Secondary", CGColor(sRGBRed: 1, green: 1, blue: 1, alpha: 1)), ])}7. Asset Library#
The asset library is now configured through the builder:
Before:#
PhotoEditor(settings) // Asset library was configured through the solution view's built-in defaults // or via custom environment modifiersNow:#
Editor(settings) .imgly.configuration { EditorConfiguration { builder in builder.assetLibrary { libBuilder in libBuilder.modify { categories in categories.modifySections(of: AssetLibraryCategory.ID.images) { sections in sections.addFirst(.image( id: "unsplash", title: "Unsplash", source: .init(id: "unsplash"), )) } } } } }Quick Migration Summary#
| Before (v1.72) | After (v1.73) |
|---|---|
PhotoEditor(settings) | Editor(settings).imgly.configuration { PhotoEditorConfiguration() } |
DesignEditor(settings) | Editor(settings).imgly.configuration { DesignEditorConfiguration() } |
VideoEditor(settings) | Editor(settings).imgly.configuration { VideoEditorConfiguration() } |
PostcardEditor(settings) | Editor(settings).imgly.configuration { PostcardEditorConfiguration() } |
ApparelEditor(settings) | Editor(settings).imgly.configuration { ApparelEditorConfiguration() } |
.imgly.onCreate { engine in } | builder.onCreate { engine, existing in } |
.imgly.onExport { engine, handler in } | builder.onExport { engine, handler, existing in } |
.imgly.onLoaded { context in } | builder.onLoaded { context, existing in } |
.imgly.onChanged { update, context in } | builder.onChanged { update, context, existing in } |
.imgly.onClose { engine, handler in } | builder.onClose { engine, handler, existing in } |
.imgly.onError { error, handler in } | builder.onError { error, handler, existing in } |
.imgly.onUpload { engine, sourceID, asset in } | builder.onUpload { engine, sourceID, asset, existing in } |
.imgly.dockItems { context in } | builder.dock { dockBuilder in } |
.imgly.navigationBarItems { context in } | builder.navigationBar { navBuilder in } |
.imgly.inspectorBarItems { context in } | builder.inspectorBar { inspectorBuilder in } |
.imgly.canvasMenuItems { context in } | builder.canvasMenu { canvasMenuBuilder in } |
.imgly.assetLibrary { ... } | builder.assetLibrary { libBuilder in } |
.imgly.colorPalette([...]) | builder.colorPalette([...]) |
.imgly.bottomPanel { ... } | builder.bottomPanel { bottomPanelBuilder in } |