I am creating a file/document provider in iOS 9.2 and have gotten the Import and Export operations working fine. However, the Open operation is giving me trouble.
My document provider writes a sample file to its file provider storage, specifically at this location:
/private/var/mobile/Containers/Shared/AppGroup/41EDED34-B449-4FE0-94BA-4046CC573544/File Provider Storage/test.txt
I have been able to read it back from within the document provider and validated the data is correct. However, when I try to read back the URL that was passed to the host app, I get a permissions error:
Error Domain=NSCocoaErrorDomain Code=257 "The file “test.txt” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/private/var/mobile/Containers/Shared/AppGroup/41EDED34-B449-4FE0-94BA-4046CC573544/File Provider Storage/test.txt, NSUnderlyingError=0x15eda7700 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
I am using fairly simple code for the reading and writing, but will include it here in case it is relevant:
File Write: (Called in the document provider before I call dismissGrantingAccessToURL:)
NSString *contents = @"this is a dynamically created text file";
NSLog(@"write to file = %@", fileName);
NSError *err;
[contents writeToFile:fileName
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:&err];
File Read: (called inside the host app in documentPicker: didPickDocumentAtURL:)
NSString *readback = [[NSString alloc] initWithContentsOfFile:[url path]
usedEncoding:nil
error:&err];
I started with Apple's sample code and have read through their documentation, but no luck explaining why this is failing.
For my file provider, only the init: function is being called. I am not sure if this is normal or if the other methods should be called for the Open operation. Here is my code for init:
- (instancetype)init {
self = [super init];
if (self) {
[self.fileCoordinator coordinateWritingItemAtURL:[self documentStorageURL] options:0 error:nil byAccessor:^(NSURL *newURL) {
// ensure the documentStorageURL actually exists
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtURL:newURL withIntermediateDirectories:YES attributes:nil error:&error];
}];
}
return self;
}
I've verified the error above is nil, and the newURL is as follows:
file:///private/var/mobile/Containers/Shared/AppGroup/41EDED34-B449-4FE0-94BA-4046CC573544/File%20Provider%20Storage/
Update: I'm starting to understand why the host app can't read the file. I think it's because I don't have permissions for that app group in the hosted app. However, I don't think it would make sense to try and add this permission since the host app should work with any document providers that support the proper extensions.
I guess the reason my file provider isn't being called (except for it's init:) is because the file returned by the 'open' is detected to be local, so there is no copying required. To pass a local file the documentation says you have to pass a URL which is inside the documentStorageURL, and yet (by definition?) that is going to be mapped to the app group which isn't accessible by the host app.
So I'm not sure how this will ever work. If someone can clarify what I should be doing here, I'd appreciate it.
After doing some more digging through the documentation, I finally found the problem: I needed to call "startAccessingSecurityScopedResource" on the URL before I tried to access it.
Refer to the "requirements" section of "Accessing files outside your sandbox" in this document:
https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/DocumentPickerProgrammingGuide/AccessingDocuments/AccessingDocuments.html
The accepted answer is right, here's just an instant code:
guard filePath.startAccessingSecurityScopedResource(),
let data = try? Data(contentsOf: filePath) else { return }
filePath.stopAccessingSecurityScopedResource()
// Do your business
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With