1

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.

lacefarin
  • 1,018
  • 2
  • 13
  • 18
  • 1
    Note that the file saved on to a temporary location. As soon as the method finishes the file is deleted. You need to move/copy the file to a permanent location. – Leo Dabus Sep 10 '21 at 20:31
  • 1
    @LeoDabus moving the file is what I needed to do. Thanks for your clue. – lacefarin Sep 11 '21 at 09:24

1 Answers1

0

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)
    }
}
lacefarin
  • 1,018
  • 2
  • 13
  • 18
  • 1
    Tried it, didn't work for me.. :( For reference, `previewController(didSaveEditedCopyOf:)` is called correctly once (the first time) either after de-selecting edit mode or tapping "Done" while editing. As soon as I de-select and re-select edit mode it's only called when tapping "Done" and then the file at URL doesn't exist – thisIsTheFoxe Mar 14 '22 at 11:45
  • @lacefarin tried for PDF with more than 10 pages, the first 5 pages were saved correctly, and changes were not present after dismiss. Have you tried with 10 pages? – arunit21 Jul 11 '22 at 15:23