Search code examples
iosquicklook

Quick Look Preview Extension iOS preparePreviewOfFile(at:completionHandler:)


I'm trying to write a simple Quick Look Preview Extension for my UIDocument-based iOS app.

The problem is that in my implementation of preparePreviewOfFile(at:completionHandler:) my attempt to open the UIDocument based on the URL I'm being handed is failing. I instantiate my document with the file URL and call open(completionHandler:) but I'm not getting any data, and I'm seeing a console message that the file coordinator has crashed.

All of this works fine in my actual app; it's just the Quick Look Preview Extension implementation that's having trouble. Is there something special I have to do to open a UIDocument from inside a Quick Look Preview Extension? Apple doesn't provide any sample code; in WWDC 2017 video 229 they just gloss over the whole thing.

EDIT: Curiouser and curiouser. I created a simplified testbed app that displays a Quick Look preview with UIDocumentInteractionController, along with my custom Quick Look Preview Extension. On the Simulator, the preview works! On the device, it doesn't. It looks like, when I tell my document to open, its load(fromContents:ofType) is never even called; instead, we are getting a pair of error messages like this:

The connection to service named com.apple.FileCoordination was invalidated.

A process invoked one of the -[NSFileCoordinator coordinate...] methods but filecoordinationd crashed. Returning an error.


Solution

  • I was able to work around the issue by not calling open on my UIDocument. Instead, I call read directly, on a background thread, like this:

    func preparePreviewOfFile(at url: URL, completionHandler handler: @escaping (Error?) -> Void) {
        DispatchQueue.global(qos: .background).async {
            let doc = MyDocument(fileURL: url)
            do {
                try doc.read(from: url)
                DispatchQueue.main.async {
                    // update interface here!
                }
                handler(nil)
            } catch {
                handler(error)
            }
        }
    }
    


    I have no idea if that's even legal. You'd think that just reading the document straight in, without the use of a file coordinator, would be Bad. But it does seem to work!


    I found yet another workaround, using NSFileCoordinator and calling load manually to get the UIDocument to process the data:

        let fc = NSFileCoordinator()
        let intent = NSFileAccessIntent.readingIntent(with: url)
        fc.coordinate(with: [intent], queue: .main) { err in
            do {
                let data = try Data(contentsOf: intent.url)
                let doc = MyDocument(fileURL: url)
                try doc.load(fromContents: data, ofType: nil)
                self.lab.text = doc.string
                handler(nil)
            } catch {
                handler(error)
            }
        }
    


    Again, whether that's legal, I have no idea, but I feel better about it than calling read directly, because at least I'm passing through a file coordinator.