iosswiftquicklook

QLPreviewControllerDelegate's method didSaveEditedCopyOf gets called with URL that has no content


I'm using QLPreviewController for displaying different kind of documents. All this documents can be edited by returning QLPreviewItemEditingMode.createCopy or QLPreviewItemEditingMode.updateContents to previewController(_:editingModeFor:) instance method of QLPreviewControllerDelegate.

Everything works as expected when using QLPreviewItemEditingMode.updateContents. When user edits document previewController(_:didUpdateContentsOf:) method of QLPreviewControllerDelegate is getting called and updated content can be accessed with the url that was passed to data source of QLPreviewController.

Problem begins when I want to use QLPreviewItemEditingMode.createCopy. When user stops editing document, previewController(_:didSaveEditedCopyOf:at:) method of QLPreviewControllerDelegate is getting called with modifiedContentsURL parameter of type URL. The first time this method is getting called I read the data from modifiedContentsURL and I successfully retrieve it. But every other call after the first one gives back url that has no data to retrieve.

Object that conforms to QLPreviewControllerDelegate protocol looks like this:

final class CustomQLPreviewDelegate: NSObject, QLPreviewControllerDelegate {
    
    var onSave: ( (Data?) -> Void )?
    
    func previewController(_ controller: QLPreviewController, editingModeFor previewItem: QLPreviewItem) -> QLPreviewItemEditingMode {
        .createCopy
    }
    
    func previewController(_ controller: QLPreviewController, didSaveEditedCopyOf previewItem: QLPreviewItem, at modifiedContentsURL: URL) {
        
        // nil everytime after the first call
        let data = try? Data(contentsOf: modifiedContentsURL)
        
        onSave?(data)
    }
    
}

I also inspected edited document in Finder and its edited copy is generated after the first call, then removed after the second call and never created again in subsequent calls.

I just wanna make sure that I'm doing everything correct before reporting this as a bug to Apple.


Solution

  • As suggested from @Leo Dabus I tried to copy the file from modifiedContentsURL to some other directory. The item was copied the first time and then never again.

    Then I tried moving the file from modifiedContentsURL and this did the trick. File was moved successfully after every call to previewController(_:didSaveEditedCopyOf:at:). Here is the reworked code of my CustomQLPreviewDelegate class:

    final class CustomQLPreviewDelegate: NSObject, QLPreviewControllerDelegate {
        
        var onSave: ( (URL) -> Void )?
        let directoryUrlForEditedCopies: URL
        
        init(directoryUrlForEditedCopies: URL) {
            self.directoryUrlForEditedCopies = directoryUrlForEditedCopies
            super.init()
        }
        
        func previewController(_ controller: QLPreviewController, editingModeFor previewItem: QLPreviewItem) -> QLPreviewItemEditingMode {
            .createCopy
        }
        
        func previewController(_ controller: QLPreviewController, didSaveEditedCopyOf previewItem: QLPreviewItem, at modifiedContentsURL: URL) {
            
            // Get the extension of current edited file
            let fileExtension = modifiedContentsURL.pathExtension
            guard !fileExtension.isEmpty else { return }
            
            // Create destination url for moving the file
            let dstUrl = createUrl(withFileExtension: fileExtension)
            
            // Move edited copy from temporary location
            // and call onSave closure with destination url
            let error = moveEditedCopy(at: modifiedContentsURL, to: dstUrl)
            guard error == nil else { return }
            onSave?(dstUrl)
        }
        
        private func moveEditedCopy(at srcUrl: URL, to dstUrl: URL) -> Error? {
            do {
                try FileManager.default.moveItem(at: srcUrl, to: dstUrl)
                return nil
            } catch (let error) {
                return error
            }
        }
        
        func createUrl(withFileExtension fileExtension: String) -> URL {
            let randomFileName = UUID().uuidString
            return directoryUrlForEditedCopies.appendingPathComponent(randomFileName, isDirectory: false).appendingPathExtension(fileExtension)
        }
    }