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
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.