Native iOS/Android file choosers

Support for native iOS and Android file choosers is now on develop with commit 7e23bf2. This also adds support for the asynchronous invocation of native file choosers. You can preview the new native file choosers in the JUCE Demo (except on iOS - see notes below on how to enable native file choosers in the JUCE Demo on iOS).

iOS: There are a few steps required for your app to be able to invoke the native file chooser on iOS. First of all you’ll need to rebuild the Projucer. You then need to enable “iCloud Permissions” and code-signing (“Development Team ID”) in the Projucer:

39
31
When enabling code-signing you need to be sure to use the correct team id. After this, you need to add iCloud containers to your app in your iCloud developer console. You can also have Xcode do this for you, by disabling and enabling the “iCloud Documents” tick-box in the Capabilities tab in your project settings:
23

You only have to do this once - after the iCloud container was added to your app, it’s stored online with your apple developer account. You can then safely delete your xcode project and regenerate it with the Projucer and you should not need to do any additional manual steps.

Furthermore, the user needs to install the iCloud app on their device and have used it at least once. In certain situations you may want to have a different application user flow depending on if the native iOS file chooser can be shown or not (for example, if the user does not have iCloud installed or has not logged into it yet). For this, you can use the static method FileChooser::isPlatformDialogAvailable() to check the availability of the native file chooser at runtime.

As the JUCE Demo is not code signed, by default, the JUCE Demo will not show native file choosers on iOS. You’ll need to enable iCloud permissions and add your own code signing identity to the JUCE Demo to preview it.

Android: Using a native file chooser on Android is much easier than on iOS. All you need to do is rebuild the Projucer and then re-save your project. Note, that Android does not support modal loops! Therefore, you must use the asynchronous methods in the FileChooser class to open any FileChooser (native or non-native) on Android.

10 Likes

WOW! So it looks like we’ll practically get iCloud document integration basically “free” now?!

I apologize for my naïveté in the request thread. My “quick way” suggestion would have been very half baked. This is awesome!

No, this is not iCloud document integration. You just need iCloud permissions to show any type of native file chooser on iOS.

1 Like

Thank you!! :slight_smile:

Looking good so far on iOS! Does this require iOS 11 or will it also run on older? I know the new Files app is iOS11 only, but not sure if this utilises that or lower level APIs. thx

Hi, found a slight difference in navigation options. If I navigate from a top level, e.g. Dropbox i get a pointer to the previous directory at the top, as shown here:

image

If I then cancel out of that browser and re-open it, the pointer to the previous directory has gone and has been replaced by a window expansion icon:

image

I can’t appear to get back to the previous display at this point and need to navigate again from the top level.

Additionally, whether the menu bar (i.e. sort options, new folder icon etc) bar is appearing is somewhat random.

For saving, browseForFileToSave() is bringing up a move dialog. I can’t seem to enter a filename anywhere - clicking on the “Untitled” does nothing.

image

Clicking on Move brings up the following:

image

I’ve assumed that all my existing OSX/Windows code should work unaltered?

Hmmm that’s super odd. I can’t see what we are doing differently for the file chooser to react like that.

A native iOS save dialog box where the user can enter a filename does not exist on iOS. The filename “Untitled” will be the default filename if you do not specify a default filename yourself via the initialFileOrDirectory parameter in the FileChooser constructor. For example:

fc = new FileChooser ("Choose a file to save...",
                      File::createFileWithoutCheckingPath ("JUCE-goes-native-filechooser.jpg"),
                      "*",
                      useNativeVersion);

When saving to iCloud or to your device, you can use the returned path FileChooser path just as you would on the desktop.

However, I did now notice that DropBox does not seem to work (I also get the error that you are getting). The following workaround seem to work for me though: before opening the save dialog, write the file you want to export to a temporary location. Then supply the path to this temporary file via the initialFileOrDirectory. iOS will then move your temporary file to the correct location. I’ll update the API to make this clearer.

ah ok, so I still need to provide some way of entering a file name

thx for the updates - will make the changes and report back - I’ll see if the juce demo behaves the same as my with regards to navigation

Confirmed that this happens on the demo also. thx

initialFileOrDirectory the file or directory that should be selected when the dialog box opens. If this parameter is set to File(), a sensible default directory will be used instead.

Should probably make clear that this needs to provide a filename on iOS (and Android? Not tried yet), whereas providing a directory for desktop is fine.

1 Like

Hi, looks like there might be an issue reading from Dropbox also - whilst the filename returned is valid, when checking file length it is always 0.

Edit: In fact, iCloud Drive comes back as file length 0 also, but in that case the exists() call also returns 0.

OK. The saving to DropBox issue is now fixed on develop with commit df8fc9b by adding a new parameter called fileWhichShouldBeSaved to the save file choosers (search for fileWhichShouldBeSaved to find more documentation).

I’ve added the documentation for this in the above commit.

Hmmmm, I’ve just tested this and it seems to work here. Are you sure that you have iCloud permissions and containers enabled as described above?

I’ll double check but I think so - without it, apps won’t even compile as XCode can’t get the permissions

So if we’ve saved a file in preparation for being moved, are we responsible for deleting it if the users hits cancel or will this be done for us? thx

Ahhhhh good point! I’ll fix that.

thx.

It looks as if the dialog is now displaying the temp file passed in as the file to write, rather than the initialFileOrDirectory parameter

image

Yes, this is expected. See the new documentation for initialFileOrDirectory (-> it’s ignored on iOS). It uses fileWhichShouldBeSaved. This needs to be like this because I noticed that iOS will preview the file if it is an image, for example. So the file chooser needs a path to the actual file.

For cross platform compatibility, you should invoke the FileChooser like the JuceDemo does:

fc = new FileChooser ("Choose a file to save...",
                      File::getCurrentWorkingDirectory().getChildFile (fileWhichShouldBeSaved.getFileName()),
                      "*",
                      useNativeVersion);

This ensures that the suggested file name is consistent on all platforms.

Also look at the complete JuceDemo code from line DialogsDemo.cpp:320. It creates a tmp directory and puts the file with the correct name inside that tmp directory. Maybe we should put that code snippet into the documentation of the FileChooser class.

Hi - yes, was just coming to the conclusion that’s what I needed to do…

So, tested the load/save on OSX, Dropbox and iCloud (iOS), creating a temp file in the temp directory as returned from the special location and providing that file to browseForFileToSave, with the following results:

OSX can read and write the file
iCloud: write file but not read it (file shows as not existing with length 0).
Google: write file but not read it (file shows as not existing with length 0).
Dropbox: I cannot read or write a file (file shows as existing with length 0).

I’m assuming that I can at least write to iCloud drive/Google means that perms are setup correctly?