PSPDFKit 11.4 Migration Guide
This guide covers updating an iOS or Mac Catalyst project from PSPDFKit 11.3 for iOS to PSPDFKit 11.4 for iOS. We encourage you to update as soon as possible, in order to take advantage of future new features and fixes.
PSPDFKit 11.4 for iOS drops support for iOS 13 in preparation for the expected announcement of iOS 16, which means you must ensure your app’s deployment target is at least iOS 14. All devices that support iOS 13 also support iOS 14 and iOS 15. This version fully supports iOS 14 and 15. Xcode 13 or later is required to use this version of PSPDFKit. Learn more in our version support guide.
Removed Deprecated APIs
This version of PSPDFKit removes APIs that have been deprecated for more than a year. This includes deprecations from PSPDFKit 9.1 to PSPDFKit 10.4 for iOS. Here’s a list of all the APIs that have been removed in this release:
Removed Deprecated API | Migration Strategy | Deprecated In |
---|---|---|
AESCryptoInputStreamError.PSPDFErrorCodeAESCryptoInputStreamCloseExpectedInsteadOfRead |
Use .closeExpectedInsteadOfRead instead. |
9.4 |
AESCryptoInputStreamError.PSPDFErrorCodeAESCryptoInputStreamCryptorCreationFailed |
Use .cryptorCreationFailed instead. |
9.4 |
AESCryptoInputStreamError.PSPDFErrorCodeAESCryptoInputStreamCryptorFinalFailed |
Use .cryptorFinalFailed instead. |
9.4 |
AESCryptoInputStreamError.PSPDFErrorCodeAESCryptoInputStreamCryptorResetToIVFailed |
Use .cryptorResetToIVFailed instead. |
9.4 |
AESCryptoInputStreamError.PSPDFErrorCodeAESCryptoInputStreamErrorDecryptingBlock |
Use .decryptingBlockFailed instead. |
9.4 |
AESCryptoInputStreamError.PSPDFErrorCodeAESCryptoInputStreamFailedToAllocateMemory |
Use .memoryAllocationFailed instead. |
9.4 |
AESCryptoInputStreamError.PSPDFErrorCodeAESCryptoInputStreamHMACCheckFailed |
Use .hMACCheckFailed instead. |
9.4 |
AESCryptoInputStreamError.PSPDFErrorCodeAESCryptoInputStreamIncorrectHMACInFile |
Use .incorrectHMACInFile instead. |
9.4 |
AESCryptoInputStreamError.PSPDFErrorCodeAESCryptoInputStreamUnknownVersionInFileHeader |
Use .unknownVersionInFileHeader instead. |
9.4 |
AESCryptoInputStreamError.PSPDFErrorCodeAESCryptoInputStreamUnknown |
Use .unknown instead. |
9.4 |
AESCryptoInputStreamError.PSPDFErrorCodeAESCryptoInputStreamWrongCloseCalled |
Use .wrongCloseCalled instead. |
9.4 |
AESCryptoInputStreamErrorCode |
Use AESCryptoInputStreamError instead. |
9.4 |
AESCryptoOutputStreamError.PSPDFErrorCodeAESCryptoOutputStreamCryptorFinalFailed |
Use .cryptorFinalFailed instead. |
9.4 |
AESCryptoOutputStreamError.PSPDFErrorCodeAESCryptoOutputStreamEncryptionFailed |
Use .encryptionFailed instead. |
9.4 |
AESCryptoOutputStreamError.PSPDFErrorCodeAESCryptoOutputStreamFailedToAllocateMemory |
Use .memoryAllocationFailed instead. |
9.4 |
AESCryptoOutputStreamError.PSPDFErrorCodeAESCryptoOutputStreamUnknown |
Use .unknown instead. |
9.4 |
AESCryptoOutputStreamError.PSPDFErrorCodeAESCryptoOutputStreamWritingToParentStreamFailed |
Use .writingToParentStreamFailed instead. |
9.4 |
AESCryptoOutputStreamErrorCode |
Use AESCryptoOutputStreamError instead. |
9.4 |
Annotation.attachBinaryInstantJSONAttachment(fromDataProvider:mimeType:) |
Use attachBinaryInstantJSONAttachment(fromDataProvider:) . |
9.1 |
AnnotationManager.ChangeBehaviorKey.animateViewKey |
Use .animateView instead. |
9.3 |
AnnotationManager.ChangeBehaviorKey.suppressNotificationsKey |
Use .suppressNotifications instead. |
9.3 |
AnnotationSet.init(annotations:) |
Use init(annotations:copyAnnotations:) , with copyAnnotations as true . |
9.2 |
AnnotationStyleViewControllerDelegate.annotationStyleController(_:didEndChangingProperty:) |
Use annotationStyleController(_:didEndChangingProperty:affectedProperties:) instead. |
10.4 |
AnnotationStyleViewControllerDelegate.annotationStyleController(_:willStartChangingProperty:) |
Use annotationStyleController(_:didBeginChangingProperty:) instead. |
10.4 |
BackForwardActionList.requestBack() |
Use requestBack(animated:) instead. |
9.4 |
BackForwardActionList.requestBack(to:) |
Use requestBack(to:animated:) instead. |
9.4 |
BackForwardActionList.requestForward() |
Use requestForward(animated:) instead. |
9.4 |
BackForwardActionList.requestForward(to:) |
Use requestForward(to:animated:) instead. |
9.4 |
BackForwardActionListDelegate.backForwardList(_:requestedBackActionExecution:) |
Use backForwardList(_:requestedBackActionExecution:animated:) instead. |
9.4 |
BackForwardActionListDelegate.backForwardList(_:requestedForwardActionExecution:) |
Use backForwardList(_:requestedForwardActionExecution:animated:) instead. |
9.4 |
BackForwardButton.longPressRecognizer |
Replaced by internally handled context menu. | 9.4 |
ButtonFormElement.ButtonFlag.noToggleToOff |
Wasn’t used. | 10.4 |
ButtonFormElement.ButtonFlag.pushButton |
Wasn’t used. Use ButtonFormField.isPushButton instead. |
10.4 |
ButtonFormElement.ButtonFlag.radio |
Wasn’t used. Use ButtonFormField.isPushButton instead. |
10.4 |
ButtonFormElement.ButtonFlag.radiosInUnison |
Wasn’t used. | 10.4 |
ButtonFormElement.ButtonFlag |
Values of this enum weren’t used. Use the properties on ButtonFormField instead. |
10.4 |
ChoiceFormElement.ChoiceFlag.combo |
Wasn’t used. Use ChoiceFormField.isCombo instead. |
10.4 |
ChoiceFormElement.ChoiceFlag.commitOnSelChange |
Wasn’t used. Use ChoiceFormField.commitOnSelChange instead. |
10.4 |
ChoiceFormElement.ChoiceFlag.doNotSpellCheck |
Wasn’t used. Use ChoiceFormField.doNotSpellCheck instead. |
10.4 |
ChoiceFormElement.ChoiceFlag.edit |
Wasn’t used. Use ChoiceFormField.isEdit instead. |
10.4 |
ChoiceFormElement.ChoiceFlag.multiSelect |
Wasn’t used. Use ChoiceFormField.isMultiSelect instead. |
10.4 |
ChoiceFormElement.ChoiceFlag.sort |
Wasn’t used. | 10.4 |
ChoiceFormElement.ChoiceFlag |
Values of this enum weren’t used. Use the properties on ChoiceFormField instead. |
10.4 |
ColorButton.borderWidth |
Use outerBorderWidth instead. |
9.3 |
ColorButton.displayAsEllipse |
Use shape instead. |
9.3 |
ColorButton.indicatorSize |
Use contentInset instead. |
9.3 |
Cryptor.ErrorCode |
Use CryptorError instead. |
9.4 |
DrawingPoint.isValid |
Use DrawingPoint.isFinite instead. |
10.0 |
FreeTextAnnotation.convert(toIntentType:) |
This method has been renamed to convertIntent(to:) |
9.3 |
ImagePickerController.ImageQuality.high |
Use ImagePickerController.ImageQuality.best instead |
9.2 |
PDFCache.invalidateImage(from:pageIndex:) |
Use invalidateImages(from:pageIndex:) instead. |
10.1 |
PDFConfiguration.showAnnotationMenuAfterCreation |
Observe the PSPDFAnnotationsAdded notification and programmatically select the new annotation and show the menu. |
10.4 |
PDFConfigurationBuilder.showAnnotationMenuAfterCreation |
Observe the PSPDFAnnotationsAdded notification and programmatically select the new annotation and show the menu. |
10.4 |
PDFContainerAnnotationProvider.prepareForRefresh() |
Nutrient handles refreshing internally now. | 9.1 |
PDFContainerAnnotationProvider.refreshAnnotationsForPages(at:) |
Nutrient handles refreshing internally now. | 9.1 |
PDFFileAnnotationPovider |
Use PDFFileAnnotationProvider instead. |
9.4 |
PDFLine |
Use [DrawingPoint] instead. An array of drawing points may be in a view coordinate space or a PDF page coordinate space. |
9.4 |
PDFPageView.longPress(_:) |
Add your own gesture recognizer instead. | 9.5 |
PDFPageView.showMenu(for:targetRect:allowPopovers:animated:) |
Call showMenu(for:targetRect:option:animated:) instead. |
9.1 |
PDFPageView.showMenuIfSelected(animated:allowPopovers:) |
Call showMenuIfSelected(with:animated:) instead. |
9.1 |
PDFPageView.singleTapped(_:) |
Add your own gesture recognizer instead. | 9.5 |
PDFPageView.singleTapped(atViewPoint:) |
Add your own gesture recognizer instead. | 9.5 |
PDFPageView.tappableAnnotations(at:) |
Use the interaction component activation conditions instead. | 9.5 |
PDFPageView.tappableAnnotationsForLongPress(at:) |
Use the interaction component activation conditions instead. | 9.5 |
PDFSigner.subFilter |
Use signatureType instead. |
10.1 |
PDFViewController.reloadPage(at:animated:) |
Use reloadPages(indexes:animated:) instead. |
10.1 |
PDFViewControllerDelegate.pdfViewController(_:didLongPressOn:at:gestureRecognizer:) |
Add your own gesture recognizer instead. | 9.5 |
PDFViewControllerDelegate.pdfViewController(_:didTapOn:at:) |
Add your own gesture recognizer instead. | 9.5 |
PSPDFAdditionalFontDirectories |
Use SDK.Setting.additionalFontDirectories instead. |
9.3 |
PSPDFAnnotationProviderRefreshing.performBlock(forWritingAndWait:) |
Nutrient now handles refreshing internally via PDFContainerAnnotationProvider . |
9.1 |
PSPDFAnnotationProviderRefreshing.prepareForRefresh() |
Nutrient now handles refreshing internally via PDFContainerAnnotationProvider . |
9.1 |
PSPDFAnnotationProviderRefreshing.refreshAnnotationsForPages(at:) |
Nutrient now handles refreshing internally via PDFContainerAnnotationProvider . |
9.1 |
PSPDFAnnotationProviderRefreshing |
Nutrient now handles refreshing internally via PDFContainerAnnotationProvider . |
9.1 |
PSPDFAnnotationRegisterOverrideClasses(_:_:) |
This functionality shouldn’t be needed. PDFFileAnnotationProvider already takes care of registering the appropriate overrides when reading an external annotation file. |
9.3 |
PSPDFDocumentCheckpointSavedNotificationSucessKey |
Use PSPDFDocumentCheckpointSavedNotificationSuccessKey instead. |
10.1 |
PSPDFError |
Use PSPDFKitError instead. |
9.4 |
PSPDFFeatureRequireSignedSource |
Compatibility constant. Migrate to Features.requireSignedSource . |
9.3 |
PSPDFFlexibleToolbarPositionsAll |
Use FlexibleToolbar.Position.all instead. |
9.3 |
PSPDFFlexibleToolbarPositionsVertical |
Use FlexibleToolbar.Position.vertical instead. |
9.3 |
PSPDFGalleryItemCaptionKey |
Use GalleryItem.Property.caption instead. |
9.3 |
PSPDFGalleryItemContentURLKey |
Use GalleryItem.Property.contentURL instead. |
9.3 |
PSPDFGalleryItemOptionsKey |
Use GalleryItem.Property.options instead. |
9.3 |
PSPDFGalleryItemTypeKey |
Use GalleryItem.Property.type instead. |
9.3 |
PSPDFInstantErrorCode |
Use InstantError instead. |
9.4 |
PSPDFLibraryExcludeAnnotationsKey |
Use PDFLibrary.Option.excludeAnnotations instead. |
9.4 |
PSPDFLibraryExcludeDocumentTextKey |
Use PDFLibrary.Option.excludeDocumentText instead. |
9.4 |
PSPDFLibraryMatchExactPhrasesOnlyKey |
Use PDFLibrary.Option.matchExactPhrasesOnly instead. |
9.4 |
PSPDFLibraryMatchExactWordsOnlyKey |
Use PDFLibrary.Option.matchExactWordsOnly instead. |
9.4 |
PSPDFLibraryMaximumPreviewResultsPerDocumentKey |
Use PDFLibrary.Option.maximumPreviewResultsPerDocument instead. |
9.4 |
PSPDFLibraryMaximumPreviewResultsTotalKey |
Use PDFLibrary.Option.maximumPreviewResultsTotal instead. |
9.4 |
PSPDFLibraryMaximumSearchResultsPerDocumentKey |
Use PDFLibrary.Option.maximumSearchResultsPerDocument instead. |
9.4 |
PSPDFLibraryMaximumSearchResultsTotalKey |
Use PDFLibrary.Option.maximumSearchResultsTotal instead. |
9.4 |
PSPDFLibraryPreviewRangeKey |
Use PDFLibrary.Option.previewRange instead. |
9.4 |
PSPDFObjectMinDiameterKey |
Use Document.ObjectFinderOption.minDiameter instead. |
9.4 |
PSPDFObjectsAnnotationIncludedGroupedKey |
Use Document.ObjectFinderOption.annotationIncludedGrouped instead. |
9.4 |
PSPDFObjectsAnnotationPageBoundsKey |
Use Document.ObjectFinderOption.annotationPageBounds instead. |
9.4 |
PSPDFObjectsAnnotationTypesKey |
Use Document.ObjectFinderOption.annotationTypes instead. |
9.4 |
PSPDFObjectsAnnotationsKey |
Use Document.ObjectFinderOption.extractAnnotations or Document.ObjectFinderType.annotations instead. |
9.4 |
PSPDFObjectsFindFirstOnlyKey |
Use Document.ObjectFinderOption.findFirstOnly instead. |
9.4 |
PSPDFObjectsGlyphsKey |
Use Document.ObjectFinderOption.extractGlyphs or Document.ObjectFinderType.glyphs instead. |
9.4 |
PSPDFObjectsIgnoreLargeTextBlocksKey |
Use Document.ObjectFinderOption.ignoreLargeTextBlocks instead. |
9.4 |
PSPDFObjectsImagesKey |
Use Document.ObjectFinderOption.extractImages or Document.ObjectFinderType.images instead. |
9.4 |
PSPDFObjectsPageZoomLevelKey |
Use Document.ObjectFinderOption.pageZoomLevel instead. |
9.4 |
PSPDFObjectsSmartSortKey |
Use Document.ObjectFinderOption.smartSort instead. |
9.4 |
PSPDFObjectsTestIntersectionFractionKey |
Use Document.ObjectFinderOption.testIntersectionFraction instead. |
9.4 |
PSPDFObjectsTestIntersectionKey |
Use Document.ObjectFinderOption.testIntersection instead. |
9.4 |
PSPDFObjectsTextBlocksKey |
Use Document.ObjectFinderOption.extractTextBlocks or Document.ObjectFinderType.textBlocks instead. |
9.4 |
PSPDFObjectsTextFlowKey |
Use Document.ObjectFinderOption.textFlow instead. |
9.4 |
PSPDFObjectsTextKey |
Use Document.ObjectFinderOption.extractText or Document.ObjectFinderType.text instead. |
9.4 |
PSPDFObjectsWordsKey |
Use Document.ObjectFinderOption.extractWords or Document.ObjectFinderType.words instead. |
9.4 |
PSPDFPresentationCloseButtonKey |
Use PresentationOption.closeButton instead. |
9.3 |
PSPDFPresentationContentSizeKey |
Use PresentationOption.contentSize instead. |
9.3 |
PSPDFPresentationHalfModalStyleKey |
Use PresentationOption.halfModalStyle instead. |
9.3 |
PSPDFPresentationInNavigationControllerKey |
Use PresentationOption.inNavigationController instead. |
9.3 |
PSPDFPresentationNonAdaptiveKey |
Use PresentationOption.halfModalStyle instead. |
9.3 |
PSPDFPresentationPopoverArrowDirectionsKey |
Use PresentationOption.popoverArrowDirections instead. |
9.3 |
PSPDFPresentationPopoverBackgroundColorKey |
Use PresentationOption.popoverBackgroundColor instead. |
9.3 |
PSPDFPresentationPopoverPassthroughViewsKey |
Use PresentationOption.popoverPassthroughViews instead. |
9.3 |
PSPDFPresentationRectBlockKey |
Use PresentationOption.sourceRectProvider instead. |
9.3 |
PSPDFPresentationRectKey |
Use PresentationOption.sourceRect instead. |
9.3 |
PSPDFPresentationReuseNavigationControllerKey |
Use PresentationOption.reuseNavigationController instead. |
9.3 |
PSPDFPresentationStyleKey |
Use PresentationOption.presentationStyle instead. |
9.3 |
PSPDFSafePreferredInterfaceOrientation(_:_:_:) |
Implement your own orientation handling. | 10.1 |
PSPDFSignerError |
Use PDFSignerError instead. |
9.4 |
PSPDFSpeechSynthesizerAutoDetectLanguage |
Use SpeechController.Option.autoDetectLanguage instead. |
9.4 |
PSPDFSpeechSynthesizerLanguageHintKey |
Use SpeechController.Option.languageHint instead. |
9.4 |
PSPDFSpeechSynthesizerLanguageKey |
Use SpeechController.Option.language instead. |
9.4 |
PSPDFViewControllerSearchHeadlessKey |
Use PresentationOption.searchHeadless instead. |
9.3 |
class Processor.cancellAllConversionOperations() |
Renamed to cancelAllConversionOperations() . |
9.5 |
Processor.Configuration.mergePage(from:password:sourcePageIndex:destinationPageIndex:transform:blendMode:) |
Use mergeAutoRotatedPage(from:password:sourcePageIndex:destinationPageIndex:transform:blendMode:) instead. |
9.4 |
SDK.Setting.PSPDFXCallbackURLStringKey |
Use .xCallbackURLString instead. |
9.3 |
SDK.Setting.applicationPolicyKey |
Use .applicationPolicy instead. |
9.3 |
SDK.Setting.coordinatedFileManagerKey |
Use .coordinatedFileManager instead. |
9.3 |
SDK.Setting.fileCoordinationEnabledKey |
Use .fileCoordinationEnabled instead. |
9.3 |
SDK.Setting.fileManagerKey |
Use .fileManager instead. |
9.3 |
SDK.Setting.honorDocumentPermissionsKey |
Use .honorDocumentPermissions instead. |
9.3 |
SDK.Setting.kitDebugModeKey |
Use .debugMode instead. |
9.3 |
SDK.Setting.libraryIndexingPriorityKey |
Use .libraryIndexingPriority instead. |
9.3 |
SegmentedControl.hitTestEdgeInsets |
Consider creating your own segmented control subclass. | 10.3 |
SegmentedControl |
Consider creating your own segmented control subclass. | 10.3 |
SignatureContainer.annotation |
Use signatureAnnotation instead. |
10.3 |
SignatureContainer.init(annotation:signer:biometricProperties:) |
Use init(signatureAnnotation:signer:biometricProperties:) instead. |
10.3 |
SignatureFormElement.overlappingInkSignature |
Use overlappingSignatureAnnotation instead. |
10.3 |
TextFieldFormElement.TextFieldFlag.comb |
Wasn’t used. Use TextFormField.isComb instead. |
10.4 |
TextFieldFormElement.TextFieldFlag.doNotScroll |
Wasn’t used. Use TextFormField.doNotScroll instead. |
10.4 |
TextFieldFormElement.TextFieldFlag.doNotSpellCheck |
Wasn’t used. Use TextFormField.doNotSpellCheck instead. |
10.4 |
TextFieldFormElement.TextFieldFlag.fileSelect |
Wasn’t used. Use TextFormField.fileSelect instead. |
10.4 |
TextFieldFormElement.TextFieldFlag.multiline |
Wasn’t used. Use TextFormField.isMultiLine instead. |
10.4 |
TextFieldFormElement.TextFieldFlag.password |
Wasn’t used. Use TextFormField.isPassword instead. |
10.4 |
TextFieldFormElement.TextFieldFlag.richText |
Wasn’t used. Use TextFormField.isRichText instead. |
10.4 |
TextFieldFormElement.TextFieldFlag |
Values of this enum weren’t used. Use the properties on TextFormField instead. |
10.4 |
ToolbarDualButton.longPressRecognizer |
Replaced by internally handled context menu. | 9.4 |
ToolbarGroupButton.longPressRecognizer |
Replaced by internally handled context menu. | 9.4 |
ViewLine |
Use [CGPoint] instead. An array of points may be in a view coordinate space or a PDF page coordinate space. |
9.4 |
-[PSPDFCache imageForRequest:imageSizeMatching:] |
Use version with error handling, image(for:imageSizeMatching:) throws , instead. |
9.1 |
-[PSPDFSoundAnnotation initWithRecorder:] |
Use init(recorderOptions:) with a nil parameter instead. |
9.3 |
Other Breaking Changes
This release changes the superclass of ThumbnailFilterSegmentedControl
from SegmentedControl
to UISegmentedControl
, since SegmentedControl
was deprecated and is now removed.
MutableRenderRequest.document
has been changed to a read-only property. If you need to request renders for many different documents, create a new request for each document instead.
This release also changes the type of ImageDocument.supportedContentTypes
and OfficeDocument.supportedContentTypes
from an array of String
to an array of UTType
, which has been the preferred way to describe type information of data since iOS 14. If you require a string representation of a supported content type, you can use the identifier
property of UTType
.
The LogLevelMask
option set changed to a LogLevel
enum. Refer to the logging guide for more details.
This release also removes some APIs that were implementation details and unintentionally ended up in our public API. This includes the removal of the following symbols, and if applicable, migration strategies in case you used any:
Removed API | Migration Strategy |
---|---|
KeyPathReferenceWritable |
Set value directly on the instance. |
UIHostingController.init(rootView:largeTitleDisplayMode:) |
Set the navigationItem.largeTitleDisplayMode yourself. |
View.customizeTextFieldOnAppear(customizeBlock:) |
Customize text fields yourself directly. |
Dictionary.valueForKey(_:) |
Use your own helper for accessing nested dictionary values. |
String.init(_: Annotation.Kind) |
Use String(describing:) instead. |
String.init(_: Annotation.Tool) |
Use String(describing:) instead. |
UnfairLock |
Use your own or system-provided locking mechanism instead. |
CGSize.* |
Use your own helper for geometry arithmetic. |
CGSize.*= |
Use your own helper for geometry arithmetic. |
CGSize./ |
Use your own helper for geometry arithmetic. |
CGSize./= |
Use your own helper for geometry arithmetic. |
Rotation: ExpressibleByIntegerLiteral |
Use the enum cases instead. |
PSPDFRenderDrawBlock |
Renamed to PDFRenderDrawBlock |
Changes to DrawView’s Undo Recording
When presented within PSPDFKit, the DrawView
API handles persisting changes to a document and recording the undo/redo commands of the annotations it’s displaying. However, if you’re using the DrawView
API outside of PSPDFKit, you’ll have to persist the changes to the document and record the undo/redo commands for the annotations yourself. This can be done by conforming to the new DrawViewDelegate
protocol and setting that instance as the DrawView.delegate
.
The modification and deletion of annotations can involve multiple user interactions, and each user interaction has to be recorded as a separate undo command. For example, a user erasing parts of multiple ink annotations in a single gesture is a single interaction, hence these modifications of multiple ink annotations should be recorded as a single undo command. However, if a user is erasing different parts of an ink annotation in two interactions, it should be recorded as a separate undo command. Likewise, adding multiple strokes to an ink annotation demands recording separate undo commands for each drawing stroke. To support all these interactions, you need to use a DetachedUndoRecorder
, which facilitates recording undoable actions over time until they’re manually committed.
Your DrawViewDelegate
implementation will require an optional property of type DetachedUndoRecorder
, which you can use to record the modification and deletion of annotations. The addition and deletion of annotations can be recorded directly to the document.
Below is an example snippet of code for illustrating the recording of undo commands for a DrawView
presented outside of PSPDFKit:
/// Document to which the annotations displayed in the `DrawView` belong. let document: Document = ... // The document to which the undo actions should be recorded to. /// Detached undo recorder for recording the modification and erasing of annotations. var pendingUndoRecorder: DetachedUndoRecorder? func drawView(_ drawView: DrawView, didBeginDrawingIn inputMode: DrawView.InputMode) { if inputMode == .erase { // Begin recording erasing of annotations. pendingUndoRecorder = document.undoController.beginRecordingCommand(named: "Erase") } } func drawView(_ drawView: DrawView, didEndDrawingIn inputMode: DrawView.InputMode, finished didFinish: Bool) { if inputMode == .erase { if didFinish { // Commit the changes only if the stroke was carried out successfully. pendingUndoRecorder?.commit() } pendingUndoRecorder = nil } } func drawView(_ drawView: DrawView, wantsToAdd annotation: Annotation) { // Add the annotation to the document recording its undo command. document.undoController.recordCommand(named: "Add", adding: [annotation]) { document.add(annotations: [annotation]) } } func drawView(_ drawView: DrawView, wantsToDelete annotation: Annotation) { // Remove the annotation from the document with an undo command. pendingUndoRecorder?.record(removing: [annotation], in: { document.remove(annotations: [annotation]) }) } func drawView(_ drawView: DrawView, wantsToModifyAnnotation annotation: Annotation, inScope scope: @escaping () -> Void) { if drawView.inputMode == .erase { // Use the detached pending recorder to record the undo command for erasing an annotation. pendingUndoRecorder?.record(changing: [annotation], in: scope) } else { // We're appending to an annotation, so this can be recorded manually. document.undoController.recordCommand(named: "Draw", changing: [annotation], in: scope) } }