Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No permission to view document passed back from iOS Document Provider on Open Operation

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.

like image 749
Locksleyu Avatar asked Jan 06 '16 14:01

Locksleyu


2 Answers

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

like image 183
Locksleyu Avatar answered Sep 28 '22 08:09

Locksleyu


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
like image 42
Display Name Avatar answered Sep 28 '22 10:09

Display Name