Skip to main content
PESDK/iOS/Getting Started

Migration from v10

Migration guide for PhotoEditor SDK v10.

Update dependencies#

PhotoEditor SDK requires iOS 13+ and Xcode 13.3+ with Swift 5.6+. If you have to use older versions of Swift or support older versions of iOS, please have a look at previous versions.

Swift Package Manager#

Go to your Xcode project Package Dependencies section, make sure that the version policy for the pesdk-ios-build package includes v11:

SPM Version

Then select File > Packages > Update to Latest Package Versions.

For more details about using Swift Package Manager see the official guide.

CocoaPods#

Your Podfile should already contain the proper entry:

pod 'PhotoEditorSDK'

To perform the actual update, run the following command:

pod update PhotoEditorSDK --repo-update

During a successful update to v11, you should get a similar output:

Installing PhotoEditorSDK 11.0.0 (was 10.30.0)
Installing imglyKit 11.0.0 (was 10.30.0)

For more details about using CocoaPods see the official guide.

Manually#

Download the latest version of the SDK here, then replace ImglyKit.framework as well as PhotoEditorSDK.framework in the Frameworks, Libraries, and Embedded Content section of your target and make sure that the Embed & Sign option is selected for both of them:

Embedded Binaries

For more details about manual integration see the getting started section.

UIKit#

CameraViewController#

The CameraViewController now returns its results within a single completionBlock:

Swift
- cameraViewController.completionBlock = { image, url in
- // ...
- }
- cameraViewController.dataCompletionBlock = { data in
- // ...
- }
+ cameraViewController.completionBlock = { result in
+ // ...
+ }
Objective-C
- [cameraViewController setCompletionBlock:^(UIImage * _Nullable image, NSURL * _Nullable url) {
- // ...
- }];
- [cameraViewController setDataCompletionBlock:^(NSData * _Nullable imageData) {
- // ...
- }];
+ [cameraViewController setCompletionBlock:^(PESDKCameraResult * _Nonnull result) {
+ // ...
+ }];

The new CameraResult parameter optionally contains either:

  • JPEG data along with EXIF information or
  • the url of the recorded video.

It also adds the model property to be able to pass, e.g., the filter state stored in PhotoEditModel between the camera and the editor.

There is no UIImage property in the result. You can still create it from data using UIImage(data:).

To create a Photo and pass it to the editor, use Photo(data:) accordingly:

let cameraViewController = CameraViewController()
cameraViewController.completionBlock = { [unowned cameraViewController] result in
guard let data = result.data else { return }
let photo = Photo(data: data)
let photoEditViewController = PhotoEditViewController(photoAsset: photo)
photoEditViewController.delegate = self
cameraViewController.present(photoEditViewController, animated: true, completion: nil)
}
present(cameraViewController, animated: true, completion: nil)

PhotoEditViewControllerDelegate#

The PhotoEditViewControllerDelegate uses extensible return types. See the examples below to integrate the improved result API with your existing code.

Getting the image#

- func photoEditViewController(_ photoEditViewController: PhotoEditViewController, didSave image: UIImage, and data: Data)
+ func photoEditViewControllerDidFinish(_ photoEditViewController: PhotoEditViewController, result: PhotoEditorResult)

The first thing you may notice is that the UIImage parameter was removed. Using UIImage directly is discouraged as it does not contain EXIF metadata. Please see the export section for more details. You can still consume the output image directly in UIKit using UIImage(data:).

The PhotoEditorResult parameter has three inner properties:

  • output - stores the data parameter with additional metadata (such as the type identifier),
  • status - indicates which exact path was chosen by the user, whether the input image was processed by our rendering pipeline or just passed without changes. This extends the photoEditViewController.hasChanges property.
  • task - encapsulates the input data (context) passed to the editor.

In most cases, you will store or upload your output image directly from Data. Then, you will need to replace the previously used data with result.output.data inside your delegate methods.

While saving to the filesystem, you can leverage the additional property result.output.uti to create a file extension:

func photoEditViewControllerDidFinish(_ photoEditViewController: PhotoEditViewController, result: PhotoEditorResult) {
guard let uti = result.output.uti, let type = UTType(uti as String) else { return }
// Get a reference to the temporary directory and append the filename and extension.
let temporaryDirectoryURL = FileManager.default.temporaryDirectory
// Append a random filename with an extension matching the output format's UTI.
let localURL = temporaryDirectoryURL.appendingPathComponent(UUID().uuidString, conformingTo: type)
if FileManager.default.fileExists(atPath: localURL.path) {
// Remove the file at the destination if it already exists.
try? FileManager.default.removeItem(at: localURL)
}
// Write image data to `localURL`.
try? result.output.data.write(to: localURL)
presentingViewController?.dismiss(animated: true, completion: nil)
}

Error handling#

- func photoEditViewControllerDidFailToGeneratePhoto(_ photoEditViewController: PhotoEditViewController)
+ func photoEditViewControllerDidFail(_ photoEditViewController: PhotoEditViewController, error: PhotoEditorError)

The PhotoEditorError is passed to provide different fallback paths, as well as debugging possibilities. You can query the error object for its reason and optional underlyingError properties:

func photoEditViewControllerDidFail(_ photoEditViewController: PhotoEditViewController, error: PhotoEditorError) {
// There was an error generating the photo.
print(error.localizedDescription)
switch error.reason {
case .inputImageNotFound:
()
case .renderingFailed:
()
default:
()
}
// Dismissing the editor.
presentingViewController?.dismiss(animated: true, completion: nil)
}

Starting the rendering (optional)#

There is also a new optional method that you can implement to perform additional validation of the input task and interrupt the rendering process if necessary:

func photoEditViewControllerShouldStart(_ photoEditViewController: PhotoEditViewController, task: PhotoEditorTask) -> Bool {
true
}

SwiftUI#

Working with SwiftUI PhotoEditor you are already familiar with PhotoEditorResult as well as with the onDidSave and onDidFail modifiers. The new version provides a consistent API with its UIKit counterpart so that you can reuse the same straightforward patterns in your closures.

Camera#

There is no UIImage property in the result, you can still create it from data using UIImage(data:).

Swift
- .onDidSaveData { data in

The onDidSaveData modifier was removed and results are returned with the single onDidSave modifier.

.onDidSave { result in
// ...
}

The onDidSave modifier type did not change. The current CameraResult parameter optionally contains either JPEG data along with EXIF information or a URL of the recorded video.

PhotoEditor#

Getting the image#

.onDidSave { result in
// ...
}

The onDidSave modifier type did not change, but the PhotoEditorResult itself is extended. In most cases, you will store or upload your output image directly from Data. Then, you will need to replace result.data with result.output.data inside your closures.

For more details about consuming the results, see the getting the image section for UIKit.

Error handling#

Swift
- .onDidFail {
+ .onDidFail { error in

The onDidFail modifier passes the actual PhotoEditorError that can be handled accordingly:

.onDidFail { error in
// There was an error generating the photo.
print("Editor finished with error: \(error.localizedDescription)")
switch error.reason {
case .inputImageNotFound:
()
case .renderingFailed:
()
default:
()
}
}

Starting the rendering (optional)#

There is also a new optional modifier that you can implement to perform additional validation of the input task and interrupt the rendering process if necessary:

.onShouldStart { task in
true
}

Explicit use of AssetCatalog#

We deprecated the static .all properties to configure the assets used in the editor, e.g., StickerCategory.all, Frame.all, Effect.all, and Overlay.all. With this gentle change, we are continuing the transition towards the adoption of the AssetCatalog API introduced in version 10.3.0. We encourage you to explicitly use the .assetCatalog property of your Configuration/ConfigurationBuilder object from now on to configure your assets. Future versions of the SDK will remove the old .all API and change the default of ConfigurationBuilder.assetCatalog from AssetCatalog.shared to AssetCatalog.defaultItems while also removing the now deprecated AssetCatalog.shared instance. This will avoid unexpected side effects and allow you to conveniently append assets to the catalog without explicitly creating a new one, or caring about one-time initialization code for global variables.

In the following, we highlight the required changes to modernize the asset configuration of your codebase for the overlay tool. The same concept translates to the other asset types.

Replace default asset items#

If you are completely replacing our predefined items with your custom assets you'll need to move the assignment to your configuration closure as shown in the example below.

Swift
let customOverlays: [Overlay] = [ /* ...*/ ]
- Overlay.all = customOverlays
+ let configuration = Configuration { builder in
+ builder.assetCatalog.overlays = customOverlays
+ }
Objective-C
NSArray<PESDKOverlay *> *customOverlays = @[ /* ... */ ];
- PESDKOverlay.all = customOverlays;
+ PESDKConfiguration *configuration = [[PESDKConfiguration alloc] initWithBuilder:^(PESDKConfigurationBuilder * _Nonnull builder) {
+ builder.assetCatalog.overlays = customOverlays;
+ }];

Extend default asset items#

If you mix and match your custom assets with our predefined items you'll also need to move the modifications to your configuration closure as demonstrated below for appending your assets to our predefined ones. Make sure to explicitly use the defaultItems if your code runs more than once so that you won't accumulate more and more assets for every editor invocation.

Swift
let customOverlays: [Overlay] = [ /* ...*/ ]
- Overlay.all = Overlay.defaultItems
- Overlay.all.append(customOverlays)
+ let configuration = Configuration { builder in
+ builder.assetCatalog = AssetCatalog.defaultItems // This line won't be necessary in future versions as it will be the default.
+ builder.assetCatalog.overlays.append(contentsOf: overlays)
+ }
Objective-C
NSArray<PESDKOverlay *> *customOverlays = @[ /* ... */ ];
- PESDKOverlay.all = PESDKOverlay.defaultItems;
- PESDKOverlay.all = [PESDKOverlay.all arrayByAddingObjectsFromArray:customOverlays];
+ PESDKConfiguration *configuration = [[PESDKConfiguration alloc] initWithBuilder:^(PESDKConfigurationBuilder * _Nonnull builder) {
+ builder.assetCatalog = PESDKAssetCatalog.defaultItems; // This line won't be necessary in future versions as it will be the default.
+ builder.assetCatalog.overlays = [builder.assetCatalog.overlays arrayByAddingObjectsFromArray:customOverlays];
+ }];

IMGLY extension namespace#

We've moved all public extensions of UIKit, Foundation, and other iOS SDK types to the .imgly namespace for Swift and changed the prefix from pesdk_ to imgly_ for Objective-C, and deprecated the old ones. The reason for deprecation is that ImglyKit is a framework used in a lot of different scenarios, where we can expect that the application is using other frameworks as well which can lead to method and property name collisions in public extensions. In the future, non-namespaced methods will be removed. Affected public extensions are UIImage, Bundle, and Notification.Name.

Swift
- let overlay = Overlay(identifier: "imgly_overlay_golden", displayName: "Golden", url: Bundle.imglyBundle.url(forResource: "imgly_overlay_golden", withExtension: "jpg"), thumbnailURL: Bundle.imglyBundle.url(forResource: "imgly_overlay_golden_thumb", withExtension: "jpg"), initialBlendMode: .lighten)
+ let overlay = Overlay(identifier: "imgly_overlay_golden", displayName: "Golden", url: Bundle.imgly.resourceBundle.url(forResource: "imgly_overlay_golden", withExtension: "jpg"), thumbnailURL: Bundle.imgly.resourceBundle.url(forResource: "imgly_overlay_golden_thumb", withExtension: "jpg"), initialBlendMode: .lighten)
Swift
var sampleImage = UIImage(named: "sample_image")
- sampleImage = sampleImage?.image(withTint: .black)
+ sampleImage = sampleImage?.imgly.image(withTint: .black)
Objective-C
- PESDKOverlay *overlay = [[PESDKOverlay alloc] initWithIdentifier:@"imgly_overlay_golden" displayName:@"Golden" url:[NSBundle.imglyBundle URLForResource:@"imgly_overlay_golden" withExtension:@"jpg"] thumbnailURL:[NSBundle.imglyBundle URLForResource:@"imgly_overlay_golden_thumb" withExtension:@"jpg"] initialBlendMode:PESDKBlendModeLighten];
+ PESDKOverlay *overlay = [[PESDKOverlay alloc] initWithIdentifier:@"imgly_overlay_golden" displayName:@"Golden" url:[NSBundle.imgly_resourceBundle URLForResource:@"imgly_overlay_golden" withExtension:@"jpg"] thumbnailURL:[NSBundle.imgly_resourceBundle URLForResource:@"imgly_overlay_golden_thumb" withExtension:@"jpg"] initialBlendMode:PESDKBlendModeLighten];
Objective-C
UIImage* sampleImage = [UIImage imageNamed:@"sample_image"];
- sampleImage = [sampleImage pesdk_imageWithTintColor:UIColor.blackColor];
+ sampleImage = [sampleImage imgly_imageWithTintColor:UIColor.blackColor];