Auv3 / iOS / native file browser issues / bug?

Hi all,

Has anyone seen this behavior for JUCE auv3 plugins?

  1. Save an xml file from within auv3 using the native iOS file browser (with app groups etc enabled). File saves fine (on ‘On my iPad’). File appears in Files.app as well.
  2. Change something in the plugin.
  3. Load the same xml file again; all settings are perfectly restored. All good so far.

Now close the host (Cubasis in my case) and restart it. Load your auv3 plugin, and try to load the same xml file as in step 3) above. Now all of a sudden it will not load… std::unique_ptr wi(url.createInputStream()) simply returns a null pointer.

Strangely enough, I can load that same file with the stand-alone version of the same plugin. I am using the following code snippets for loading and saving:

To load an xml file:

FileChooser myChooser("Select file:",
     File::getSpecialLocation(File::SpecialLocationType::userApplicationDataDirectory),
			"*", true, true, parentComponent);

		if (myChooser.browseForFileToOpen())
		{
            auto url = myChooser.getResult();
            std::unique_ptr<InputStream> wi(url.createInputStream());
            if (wi != nullptr)
            {
                const String s = wi->readEntireStreamAsString();
                if (s.length() > 0)
                {
                    std::unique_ptr<XmlElement> xml(XmlDocument::parse(s));
                    if (xml != nullptr)
                    {
                        // Do something sensible here
                    }
                }
            }
        }

For saving, I’m using something very similar:

FileChooser myChooser("Set file name:", File::getSpecialLocation(File::SpecialLocationType::userApplicationDataDirectory).getFullPathName() + File::getSeparatorChar() + initialFileName, "*", true, true, parentComponent);
        
        if (myChooser.browseForFileToSave(true))
        {
            auto url = myChooser.getURLResult();
            std::unique_ptr<OutputStream> wo(url.createOutputStream());
            if (wo != nullptr)
            {
                XmlElement xml("tpb");
                return wo->writeString(xml.toString());
            }
        }

Does this mean we can’t use the native file browser for auv3? If I switch back to the JUCE file browser, and always read/write the xml file from within the documents folder of the auv3, it does work consistently.

BTW the same happens if I change the initial location to SpecialLocationType::userDocumentsDirectory.

Now, if on the other hand, I use the JUCE file browser, I can read and write xml files across multiple instances of the auv3 plugin, even after closing and re-opening the host.

However, in that particular case, those files written by the auv3 are not visible in Files app, nor are they visible to the stand-alone version.

Perhaps there is something I am overlooking, or there is something not quite right with how JUCE interacts with the native file browser for auv3 plugins?

In summary it doesn’t seem possible to:

  1. save a file using the native file chooser from within an auv3; file appears in Files app, and is perfectly OK (e.g. all xml code is in there).
  2. Close and re-open the host and the included juce auv3 plugin;
  3. try to load the same file from within the auv3 using the native file chooser.

To re-iterate, when I omit step 2), it all does work as expected. Also if the plugin is running in stand-alone mode, it all works perfectly fine including closing/re-opening the app.

AUv3 plugins have their own directory structure that is sandboxed; if you’re using the Files trigger (which is what the native chooser is) you’re saving to the standalone’s directory structure. The AUv3 can’t actually see that; hence the nullptr.

You have to save to and load from the security group, which is a third (!!!) tree that both the standalone and AUv3 can see.

(As to why it persists after the save, but not the re-open, no idea. Strange are the ways of the URL.)

Ah ok, that helps a lot, thanks!

It would be really great if JUCE could have an easy way to read and write from/to that security group. I know you’ve posted an approach for that on this forum but it’s still a bit cumbersome…

+1 Let’s just keep on suggesting that the API supports this :wink:

Yes; even just a simple function that would return the path to the folder of the security group (based on properties set in projucer) would already be a big help so we know where to read and write files for iOS. @crandall1 has already supplied the code to get that path; as far as I can see it only needs integration in the JUCE framework to get that to work in an easy way?

@crandall1 I think I get it but this confuses me quite a bit.

So is it true then that the auv3 can write to the standalone’s directory structure (my tests indicate that this works perfectly fine, and the files written can be opened by the standalone app), but the auv3 cannot read from what it as written itself in that structure?

To the best of my knowledge, that is correct. If you write to the standalone’s “Documents” folder from the AUv3, you can not read it from the AUv3 later. If App Groups are active for the pair, the AUv3 can read from the Standalone’s bundle, but not write to it.

The AUv3 can open a Files instance (which is the native browser in iOS) and read and write to the main system file tree (assuming you have a minimum of iOS 11) and with App Groups, you can read and write to the Security group, which is the file tree that the .appex and .app share. But the AUv3 has no other file writing permissions outside of that; AUv3 are heavily sandboxed, and the system tree (via the Files app), Security Group, and its own tree (which nothing else can see) are the only places it has read and write access to.

So, for presets, best to use the security group, because both have permissions to it and both can see it at all times. You’ll need to do my Obj-C song and dance from the other thread for that. For loading and saving audio files, best to use the native browser and Files instance, because Apple doesn’t like it when you save large blobs of data in the app groups, and you run the risk of rejection, or having your folder cleaned out or not backing up.

because Apple doesn’t like it when you save large blobs of data in the app groups, and you run the risk of rejection, or having your folder cleaned out or not backing up.

Really? I mean I’m only planning to use the Security Group to save/load presets about less than 1kB but is your recommendation officially documented?

There’s no hard and fast “this is too big” situation, but I’ve been rejected in the past for storing audio recordings in locations that are backed up to iCloud.

The entirety of Apple’s official direction in this regard is here: https://developer.apple.com/icloud/documentation/data-storage/index.html

You can read further at the links at the bottom of the screen, depending on how interested you are in learning about CoreData and iCloud. The short answer is audio recordings should not be backed up. You should only store data that the user creates and that the app can’t recreate, and which isn’t gigantic, in the backup locations.

Hi Im new today, and actually it’s reading/writing this kind of data that’s the first thing I want to get working. Please could you provide a link to your thread on the security group?

I’m hoping to migrate an app from cycling74 Max. My .json presets are about 400KB per bank, and Im hoping that’s not too much.

Here you go.

Make sure to read the whole thread. There was some confusion from Fabian as to what I was asking for, but we got it figured out.