Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling application: openURL: sourceApplication: to open files in iOS app

I have an app that uses some known large file formats and has supported the "Open In..." feature of iOS since the iOS4 days.

Up until recently has worked fine, certain apps, like the iOS built-in Mail app would open the app by making a copy inside the ~/Documents/Inbox directory inside my app and life was good.

I have recently been made aware of the fact that "Open In..." is no longer working for my app, at least when files come from iCloud/Dropbox/GoogleDrive via the iOS11+ built-in Files app, and I'm wondering if I'm missing anything.

After checking the code I'm starting to think that I must be missing something really obvious because the code seems to be correct to not open the files as the paths given are either invalid or my app does not have the necessary permissions to read them.

This is my code:

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)urlToOpen sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    if([urlToOpen isFileURL])  // <<< this goes through
    {
        if([[NSFileManager defaultManager] isReadableFileAtPath:urlToOpen.path])
        {
            // <<< this doesn't
        }
   }
}

Example working paths:

/private/var/mobile/Containers/Data/Application/F99A3EA4-457C-4043-AEB3-A9D961184360/Documents/plane.obj
/private/var/mobile/Containers/Data/Application/F99A3EA4-457C-4043-AEB3-A9D961184360/Documents/Inbox/cube.obj

Examples of paths that don't work:

/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/somedir/somefile.ext
/private/var/mobile/Containers/Shared/AppGroup/A7FA40A0-2C7D-4FC1-BF21-A8E88B106FF9/File Provider Storage/7791941/local-storage/L3dzX3JhZmFfemFiYWxhL2Jhc2VvcmNfbWVzaC5vYmo=/somefile.ext

As you can see both files that did work had their data copied onto my local apps directory in the device.

How can I open these files? Please note that I mentioned that the app uses large files because it does, so I can't get an in-memory copy of them, that wouldn't help very much, I need to get a file path so I can do memory mapping and load what's needed at the right time.

I have read a bit about open in place etc, but it doesn't apply to the type of app I'm developing since it doesn't operate on a single "element" the large files I mention are just one of the possible sources and thus things like what I've seen mentioned regarding opening-in-place doesn't apply to my case.

Is there a way to get the app to be able to access these files directly or at the very least get them copied over to my apps sandboxed temporary directory so I can memory map them directly?

Cheers!

like image 923
Exaberri Tokugawa Avatar asked Nov 29 '18 11:11

Exaberri Tokugawa


1 Answers

I contacted Apple about this as it wasn't making much sense, the docs mention the methods to use but you really need to dig deep and it's not entirely obvious as it's mentioned in a different non-logical place.

It turns out that the NSURL's you're given in the AppDelegate are protected, your safest bet is to either read them or copy them to your local drive.

I ended up with this:

NSString* urlPath = url.path;
if(![[NSFileManager defaultManager] isReadableFileAtPath:urlPath])
{
    if([url startAccessingSecurityScopedResource])
    {
        NSString* docsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString* destPath = [NSString stringWithFormat:@"%@/%@", docsPath, [url.path lastPathComponent]];
        urlPath = [FileHandler copyFileAtPath:url.path toPath:destPath increment:YES];
        [url stopAccessingSecurityScopedResource];
    }
}

Where copyFileAtPath:toPath:increment is a custom function that simply copies files and increments them if they exist to avoid overwriting, but could be anything really.

The key methods here are [NSURL startAccessingSecurityScopedResource] and [NSURL stopAccessingSecurityScopedResource], it's important to note that they must be called in pairs (start/stop) as failure to call stop can cause issues, thus the copying method works great, specially when those files are going to be memory mapped. (my case as they are large)

like image 145
Exaberri Tokugawa Avatar answered Nov 13 '22 08:11

Exaberri Tokugawa