How do I properly implement a QLPreviewPanel datasource with security scoped URLs in mind?

There is a problem with poorly chosen abstractions in Cocoa that comes up when you try to combine the Quick Look and URL preview pane with the security scope.

I have a specific example:

Imagine we are trying to show a preview of some objects from MediaLibrary (MediaLibrary.framework allows applications to view iPhoto, Aperture ... and Photos libraries through a convenient API).

The simplest and easiest way is to adapt the "MLMediaObject" class (which represents a particular photo or video item) to implement the "QLPreviewItem" protocol (which can be passed to the QLPreviewPanel):

MLMediaObject + PreviewItem.h

#import <MediaLibrary/MLMediaObject.h>

#import <Quartz/Quartz.h>

@interface MLMediaObject (PreviewItem) <QLPreviewItem>



MLMediaObject + PreviewItem.m

#import "MLMediaObject+PreviewItem.h"

@implementation MLMediaObject (PreviewItem)

- (NSURL*) previewItemURL
  return self.URL;

- (NSString*) previewItemTitle



Simple. Now create the following QLPreviewPanel data source implementation:


- (NSInteger) numberOfPreviewItemsInPreviewPanel: (QLPreviewPanel*) panel
  // 'currentAlbum' property contains the currently-represented MLMediaGroup object.
  return self.currentAlbum.count;

- (id<QLPreviewItem>) previewPanel: (QLPreviewPanel*) panel previewItemAtIndex: (NSInteger) index
  return self.currentAlbum[index];


So far so good. But if we take a look at Apple's rare and usually misleading documentation, we can learn the following important details:

Location of the media object. (Read only)
This property is provided as a scoped URL. To access the file referenced by this url, the caller must call startAccessingSecurityScopedResource

before and stopAccessingSecurityScopedResource

after using the url to access the file.

So, obviously, the resource access must be parenthesized using the startAccessingSecurityScopedResource

/ pair stopAccessingSecurityScopedResource


The question is, where should I put these calls given the current protocol definition QLPreviewPanelDataSource

... You don't need a QLPreviewPanel to access the resource, not my code, but unfortunately I hardly ever assume that it was Apple that updated QL to work in a sandboxed environment.

How to handle cases where the call startAccessingSecurityScopedResource

returns NO

claiming to be unable to access?

It looks like when you try startAccessingSecurityScopedResource

on a URL that you are already accessing, you are returning a failure flag. For example, everything is fine, but you get an error flag. It seems that these start / stop ... calls have to be paired exactly, and even balanced nesting is prohibited. So how do you separate the two possibilities when you get NO

back when you return: a security-scoped URL that is already being accessed, and a secure URL that could not be "resolved"?

It has been experimentally proven that your application can only access a limited number of URLs protected by the security scope (you can take about 1500 URLs before they stop working). So how am I supposed to properly deny access to security-protected URLs after I have passed them to the QLPreviewPanel? When is the right time? It seems to me that this is a private implementation detail of the QLPreviewPanel class, and I cannot make any assumptions about its inner workings.


source to share

1 answer

You can use:

- (void)beginPreviewPanelControl:(QLPreviewPanel *)panel {
    [bookmarkURL startAccessingSecurityScopedResource];
    //... Your code




- (void)endPreviewPanelControl:(QLPreviewPanel *)panel {
    //... Your Code
    [bookmarkURL stopAccessingSecurityScopedResource];




All Articles