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.​PSPDFErrorCodeAESCryptoInputStream​CloseExpectedInsteadOfRead Use .closeExpectedInsteadOfRead instead. 9.4
AESCryptoInputStreamError.​PSPDFErrorCodeAESCryptoInputStream​CryptorCreationFailed Use .cryptorCreationFailed instead. 9.4
AESCryptoInputStreamError.​PSPDFErrorCodeAESCryptoInputStream​CryptorFinalFailed Use .cryptorFinalFailed instead. 9.4
AESCryptoInputStreamError.​PSPDFErrorCodeAESCryptoInputStream​CryptorResetToIVFailed Use .cryptorResetToIVFailed instead. 9.4
AESCryptoInputStreamError.​PSPDFErrorCodeAESCryptoInputStream​ErrorDecryptingBlock Use .decryptingBlockFailed instead. 9.4
AESCryptoInputStreamError.​PSPDFErrorCodeAESCryptoInputStream​FailedToAllocateMemory Use .memoryAllocationFailed instead. 9.4
AESCryptoInputStreamError.​PSPDFErrorCodeAESCryptoInputStream​HMACCheckFailed Use .hMACCheckFailed instead. 9.4
AESCryptoInputStreamError.​PSPDFErrorCodeAESCryptoInputStream​IncorrectHMACInFile Use .incorrectHMACInFile instead. 9.4
AESCryptoInputStreamError.​PSPDFErrorCodeAESCryptoInputStream​UnknownVersionInFileHeader Use .unknownVersionInFileHeader instead. 9.4
AESCryptoInputStreamError.​PSPDFErrorCodeAESCryptoInputStream​Unknown Use .unknown instead. 9.4
AESCryptoInputStreamError.​PSPDFErrorCodeAESCryptoInputStream​WrongCloseCalled Use .wrongCloseCalled instead. 9.4
AESCryptoInputStreamErrorCode Use AESCryptoInputStreamError instead. 9.4
AESCryptoOutputStreamError.​PSPDFErrorCodeAESCryptoOutputStream​CryptorFinalFailed Use .cryptorFinalFailed instead. 9.4
AESCryptoOutputStreamError.​PSPDFErrorCodeAESCryptoOutputStream​EncryptionFailed Use .encryptionFailed instead. 9.4
AESCryptoOutputStreamError.​PSPDFErrorCodeAESCryptoOutputStream​FailedToAllocateMemory Use .memoryAllocationFailed instead. 9.4
AESCryptoOutputStreamError.​PSPDFErrorCodeAESCryptoOutputStream​Unknown Use .unknown instead. 9.4
AESCryptoOutputStreamError.​PSPDFErrorCodeAESCryptoOutputStream​WritingToParentStreamFailed 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
PSPDFAnnotationRegister​OverrideClasses​(_:_:) This functionality shouldn’t be needed. PDFFileAnnotationProvider already takes care of registering the appropriate overrides when reading an external annotation file. 9.3
PSPDFDocumentCheckpoint​SavedNotificationSucessKey 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
PSPDFLibrary​ExcludeAnnotationsKey Use PDFLibrary.Option.​excludeAnnotations instead. 9.4
PSPDFLibrary​ExcludeDocumentTextKey Use PDFLibrary.Option.​excludeDocumentText instead. 9.4
PSPDFLibrary​MatchExactPhrasesOnlyKey Use PDFLibrary.Option.​matchExactPhrasesOnly instead. 9.4
PSPDFLibrary​MatchExactWordsOnlyKey Use PDFLibrary.Option.​matchExactWordsOnly instead. 9.4
PSPDFLibrary​MaximumPreviewResultsPerDocumentKey Use PDFLibrary.Option.​maximumPreviewResultsPerDocument instead. 9.4
PSPDFLibrary​MaximumPreviewResultsTotalKey Use PDFLibrary.Option.​maximumPreviewResultsTotal instead. 9.4
PSPDFLibrary​MaximumSearchResultsPerDocumentKey Use PDFLibrary.Option.​maximumSearchResultsPerDocument instead. 9.4
PSPDFLibrary​MaximumSearchResultsTotalKey Use PDFLibrary.Option.​maximumSearchResultsTotal instead. 9.4
PSPDFLibrary​PreviewRangeKey Use PDFLibrary.Option.​previewRange instead. 9.4
PSPDFObjectMinDiameterKey Use Document.ObjectFinderOption.​minDiameter instead. 9.4
PSPDFObjects​AnnotationIncludedGroupedKey Use Document.ObjectFinderOption.​annotationIncludedGrouped instead. 9.4
PSPDFObjects​AnnotationPageBoundsKey Use Document.ObjectFinderOption.​annotationPageBounds instead. 9.4
PSPDFObjects​AnnotationTypesKey Use Document.ObjectFinderOption.​annotationTypes instead. 9.4
PSPDFObjects​AnnotationsKey Use Document.ObjectFinderOption.​extractAnnotations or Document.ObjectFinderType.annotations instead. 9.4
PSPDFObjects​FindFirstOnlyKey Use Document.ObjectFinderOption.​findFirstOnly instead. 9.4
PSPDFObjects​GlyphsKey Use Document.ObjectFinderOption.​extractGlyphs or Document.ObjectFinderType.glyphs instead. 9.4
PSPDFObjects​IgnoreLargeTextBlocksKey Use Document.ObjectFinderOption.​ignoreLargeTextBlocks instead. 9.4
PSPDFObjects​ImagesKey Use Document.ObjectFinderOption.​extractImages or Document.ObjectFinderType.images instead. 9.4
PSPDFObjects​PageZoomLevelKey Use Document.ObjectFinderOption.​pageZoomLevel instead. 9.4
PSPDFObjects​SmartSortKey Use Document.ObjectFinderOption.​smartSort instead. 9.4
PSPDFObjects​TestIntersectionFractionKey Use Document.ObjectFinderOption.​testIntersectionFraction instead. 9.4
PSPDFObjects​TestIntersectionKey Use Document.ObjectFinderOption.​testIntersection instead. 9.4
PSPDFObjects​TextBlocksKey Use Document.ObjectFinderOption.​extractTextBlocks or Document.ObjectFinderType.textBlocks instead. 9.4
PSPDFObjects​TextFlowKey Use Document.ObjectFinderOption.​textFlow instead. 9.4
PSPDFObjects​TextKey Use Document.ObjectFinderOption.​extractText or Document.ObjectFinderType.text instead. 9.4
PSPDFObjects​WordsKey Use Document.ObjectFinderOption.​extractWords or Document.ObjectFinderType.words instead. 9.4
PSPDFPresentation​CloseButtonKey Use PresentationOption.​closeButton instead. 9.3
PSPDFPresentation​ContentSizeKey Use PresentationOption.​contentSize instead. 9.3
PSPDFPresentation​HalfModalStyleKey Use PresentationOption.​halfModalStyle instead. 9.3
PSPDFPresentation​InNavigationControllerKey Use PresentationOption.​inNavigationController instead. 9.3
PSPDFPresentation​NonAdaptiveKey Use PresentationOption.​halfModalStyle instead. 9.3
PSPDFPresentation​PopoverArrowDirectionsKey Use PresentationOption.​popoverArrowDirections instead. 9.3
PSPDFPresentation​PopoverBackgroundColorKey Use PresentationOption.​popoverBackgroundColor instead. 9.3
PSPDFPresentation​PopoverPassthroughViewsKey Use PresentationOption.​popoverPassthroughViews instead. 9.3
PSPDFPresentation​RectBlockKey Use PresentationOption.​sourceRectProvider instead. 9.3
PSPDFPresentation​RectKey Use PresentationOption.​sourceRect instead. 9.3
PSPDFPresentation​ReuseNavigationControllerKey Use PresentationOption.​reuseNavigationController instead. 9.3
PSPDFPresentation​StyleKey Use PresentationOption.​presentationStyle instead. 9.3
PSPDFSafePreferredInterface​Orientation​(_:_:_:) Implement your own orientation handling. 10.1
PSPDFSignerError Use PDFSignerError instead. 9.4
PSPDFSpeechSynthesizer​AutoDetectLanguage Use SpeechController.Option.​autoDetectLanguage instead. 9.4
PSPDFSpeechSynthesizer​LanguageHintKey Use SpeechController.Option.​languageHint instead. 9.4
PSPDFSpeechSynthesizer​LanguageKey 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)
    }
}