iOS AUv3 save/load custom presets

Hi all, I have started porting my plugins to iOS/iPad and I would like to keep my current desktop preset system.
As far as I understand, there are two ways to access the sandboxed area to save/load your own files.

Using App Group ID, something like:
juce::File::getContainerForSecurityApplicationGroupIdentifier ("group.audiothing");
in my case returns:

juce::File::getSpecialLocation (File::userDocumentsDirectory);

I am able to save presets in both locations, and they apparently end up in the same spot regardless of the method used. However, not matter what I try, I can’t access the files in any way.

Any clue on what should I do?

1 Like

I’m using:

presetsDirectory = File::getSpecialLocation(File::userApplicationDataDirectory)
                            .getChildFile("Application Support")

I can access them fine in a standalone app, but haven’t tried as an AUv3 plugin yet.

1 Like

Thank you! So trying userApplicationDataDirectory then I get this path:
But now I double-checked the path for the saved file and it’s completely different from what I give to the FileChooser.
So, whatever path I give to the FileChooser (using each of the three methods outlined so far), it instead saves the file to:
/private/var/mobile/Containers/Shared/AppGroup/3BBD97CB-1852-4624-BC28-8FB5BDE9BC34/File Provider Storage/Filterjam/CustomPreset.atp
So yet another path, how am I supposed to get this path then? Or what should I provide to the FileChooser exactly?

This is what I’m testing right now:

File customPreset = File::getSpecialLocation (File::userApplicationDataDirectory)
    .getChildFile ("AudioThing")
    .getChildFile ("Filterjam")
    .getChildFile ("Presets")
    .getChildFile ("CustomPreset.atp"); // hardcoded just for this example

DBG ("customPreset: " << customPreset.getFullPathName());
// customPreset: /var/mobile/Containers/Data/PluginKitPlugin/60B27E2F-9F74-4672-B09B-AFBDE6109FCA/Library/AudioThing/Filterjam/Presets/CustomPreset.atp

fileChooser = std::make_unique<FileChooser> ("Save Preset", customPreset, "*.atp", true, false, this);
fileChooser->launchAsync (FileBrowserComponent::saveMode |
                          FileBrowserComponent::warnAboutOverwriting |
        [this](const FileChooser& fc)
            File saveFile (fc.getResult());
            DBG ("saveFile: " << saveFile.getFullPathName());
            // saveFile: /private/var/mobile/Containers/Shared/AppGroup/3BBD97CB-1852-4624-BC28-8FB5BDE9BC34/File Provider Storage/Filterjam/CustomPreset.atp

So the path I provide to FileChooser and the path the FileChooser uses to save the file are different.
The result is the same for AUv3 and Standalone.

1 Like

Interesting…and odd.

For whatever reason, I chose to eliminate using a FileChooser when porting my plugin to iOS. Instead, I’m simplifying that so there’s no real file system view to the user. I’m just hardwiring several folders to “banks” and just having the user pick a bank and a name and that’s it.

Ok, so bypassing the FileChooser works. So I can just prompt a small box to the user to get the preset name. That’s ok I guess…but now the problem is, which path should I use?

To recap, there are 3 methods I’ve found so far:
juce::File::getContainerForSecurityApplicationGroupIdentifier ("group.audiothing");
juce::File::getSpecialLocation (File::userDocumentsDirectory);
juce::File::getSpecialLocation (File::userApplicationDataDirectory)

I think that if you need to share files among multiple apps, the first option is the way to go.

1 Like

Hmmm. I haven’t gotten that far yet.

Btw, I’m glad to see this documented somewhere. Not sure why, but as this is my first port to iOS, it still seems like the wild west for lots of small details to get right.

The App Group container is the right choice if you want user files to be seamlessly usable from both your app and AUv3.

However, if you have some factory presets or bundled content, you have to take care of copying these in the App Group container yourself, e.g. upon first launch.

1 Like