nsurlappstore-sandboxsandboxnspathcontrolsecurity-scoped-bookmarks

How to bind NSURL in plist to NSPathControl in sandboxed app?


My OS X app needs persistent access to a directory the user chooses. Before I turned on app sandbox the UI had a NSPathControl with a value binding to the data model. The data type in the model is NSData and the binding used a NSKeyedUnarchiveFromData value transformer. It worked great.

With app sandboxing turned on this obviously fails because the NSURL is not security scoped. To remedy this, I replaced the NSKeyedUnarchiveFromData transformer in the binding with my own transformer, shown below.

Unfortunately it does not always work correctly. Sometimes when I (acting as user) select a file from the NSPathControl, transformedValue: returns nil even though the NSData argument passed into it is non-nil. In other words, NSURL doesn't resolve the bookmark data. Anyone know what's going on here? When it fails, the error returned by URLByResolvingBookmarkData:options:relativeToURL:bookmarkDataIsStale:error: is

Error Domain=NSCocoaErrorDomain Code=259 "The file couldn’t be opened because it isn’t in the correct format."

@implementation URLFromBookmarkDataTransformer

+ (BOOL)allowsReverseTransformation {
    return YES;
}

+ (Class)transformedValueClass {
    return [NSURL class];
}

- (id)transformedValue:(id)value {
    if (value == nil) {
        return nil;
    }
    NSAssert([value isKindOfClass:[NSData class]], @"value must be NSData");
    return [NSURL URLByResolvingBookmarkData:value options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:NULL error:NULL];
}

- (id)reverseTransformedValue:(id)value {
    if (value == nil) {
        return nil;
    }
    NSAssert([value isKindOfClass:[NSURL class]], @"value must be NSURL");
    return [value bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:NULL];
}

@end

Solution

  • The cause of the behavior I was seeing was (surprise...) pilot error. I should note that the value transformer subclass I have posted above does smoothly convert between NSURL and bookmark NSData. Note that com.apple.security.files.bookmarks.app-scope must be declared in entitlements file.