0

I am creating a UIDocumentPicker extension on iOS for the first time. I have populated the UIDocumentPickerExtensionViewController subclass with its appropriate methods. In my UI when the user selects the document from my extension, I am able to successfully copy the image in question to the document directory. Relevant code:

NSURL *sourceURL = [item imageUrlWithPath:image.fullSizeImagePath];
NSString *filename = [NSString stringWithFormat:@"%@.jpg", item.title ?: NSLocalizedString(@"NO_TITLE", nil)];
NSURL *targetURL = [self.documentStorageURL URLByAppendingPathComponent:filename];

NSError *copyError = nil;
BOOL success = [fm copyItemAtURL:sourceURL toURL:targetURL error:&copyError];

if (success) {
    [self dismissGrantingAccessToURL:targetURL];
} else {
    NSLog(@"Error! %@", copyError);
}

This all goes fine according to the code path above, however, after the selection is made and the call is made to dismissGrantingAccessToURL:, the UI gets dismissed, but I get an alert from the host application (in this case, Pages) saying that the image could not be inserted because "This image is of an unsupported type."

Things I have tried:

  • Try the file name with a different file extension
  • Used variations of different thumbnails of my image
  • Recompressed the image to a new file.

All to no avail. How come my file cannot be read by other applications?

Wayne Hartman
  • 18,369
  • 7
  • 84
  • 116

1 Answers1

0

When you create a new document provider extension (at least in Xcode 8), Apple create an additional extension called a File Provider. This is a separate extension independent of the UI to fetch the file from a remote service, such as a server. This is useful in instances where the file is not physically on-device, but you still want the user to be able to import it into the host application.

The unfortunate side effect of this is that Apple have also written some stub code, a subclass of NSFileProviderExtension. If you find this class (defaults to FileProvider), there is a method startProvidingItemAtURL:completionHandler:. This method has some precanned code that Apple have put in there:

- (void)startProvidingItemAtURL:(NSURL *)url completionHandler:(void (^)(NSError *))completionHandler {

    // Should ensure that the actual file is in the position returned by URLForItemWithIdentifier:, then call the completion handler
    NSError *fileError = nil;

    // TODO: get the contents of file at <url> from model
    NSData *fileData = [NSData data];

    [fileData writeToURL:url options:0 error:&fileError];

    if (completionHandler) {
        completionHandler(nil);
    }
}

As is readily seen in this code, they completely overwrite the file you may have already written, if the file was in fact already present from the previous picker extension. Sample code to the rescue...until it silently wipes out your file. One solution to this problem is to comment out all the lines of code in this method, except for the call on the completion handler:

- (void)startProvidingItemAtURL:(NSURL *)url completionHandler:(void (^)(NSError *))completionHandler {
    if (completionHandler) {
        completionHandler(nil);
    }
}

With that, your document provider is right as rain.

Wayne Hartman
  • 18,369
  • 7
  • 84
  • 116