How can I download to a persistent folder via the Web component on AI2 for iOS?

When I use the Web component to download audio files (mp4) from my server via an app with AI2 for iOS, I get the following path, among others:
/private/var/mobile/Containers/Data/Application/77E6D664-136A-4501-BDE4-97596C422A1E/tmp/Music-1.m4a.

I then save this path in a TinyDB and pass it to the Player component (Player.Source), which then plays it. This works with Companion and the compiled app (IPA). However, if I restart the app, this path no longer seems to be correct (no longer exists).

Apparently, these are temporary files (in the sandbox) that are no longer there after restarting the app. Is there a way to write (download) to a persistent folder using the Web component?
@ewpatton

Is this happening in both companion and compiled apps? Do you want to PM me the project so I can test it?

Yes.

Of course. Done.

Sorry, I checked it again. It works with Companion also after reloading, but definitively not with the IPA (after restarting the app).

Error 702: Unable to prepare.

Setting a ResponseFileName has no effect on iOS.

I repeated my tests with the IPA today on three test devices. These tests actually differ from my tests of the past few months. In fact, the previous paths seemed to disappear after restarting the app. At least, the music from there could no longer be played ("unable to prepare").

My tests today actually showed that it still works even after restarting the app.

My tests with the IPA on:

  • iPhone 15 Pro Max (iOS 26.0.2),
  • iPad Pro 12.9 (iOS 18.5) -> Upgrade to iOS 26 coming...
  • iPhone 6s (iOS 15.8.2)

No idea why it seems to be working since today. I'll wait and see how long these paths will work...

An AI response is:

What option do I have in AI2 for iOS to check whether such a path still exists? Unfortunately, the File.Exists method doesn't work on iOS. I can only see one (cumbersome) method: adding another Player component, setting the volume to 0, setting the path as Player.Source, playing the music, and checking whether Player.IsPlaying returns "true" (then stopping this test player). If not, the download must be repeated. This check would have to be performed before each app restart (i.e., in the Screen.Initialize event and via a timer).

Barely two hours later, it stopped working on my iPhone 15 Pro Max.
(Error 702: Unable to prepare)

Should a persistent path not look like this:
/private/var/mobile/Containers/Data/Application/<UUID>/Documents/Music-1.m4a ?

I'm working on a few improvements and will ping you once they're up on TestFlight.

1 Like

Previously, I received paths like this after downloading:
/private/var/mobile/Containers/Data/Application/46F94671-EBF8-4517-9062-319C6094B344/tmp/Musik-1.m4a.

Now I get this:
/var/mobile/Containers/Data/Application/36944084-4654-4F9B-86CE-833BA7E85E28/Library/Application Support/AppInventor/Downloads/app_inventor_1761121313402.m4a,

where the file name is no longer "Music-1.m4a", but "app_inventor_1761121313402.m4a" (i.e., app_inventor_<SystemTime>.m4a). This may be manageable with very few downloads/files, but definitely not with dozens/hundreds. Additional logic would have to be implemented for assigning paths for the Player.Source. Furthermore, with each subsequent (re)download, new files are created.

ResponseFileName was and remains ineffective, which wouldn't be a problem if the filename of the file to be downloaded were also part of the filename of the downloaded file.

The very long and ugly path is already unpleasant enough, but please don't add a file name like "app_inventor_SystemTime." The downloaded file must still be recognizable by the path name.

Okay, I've found a way to deal with it. But it would be helpful in general if we had a method to check whether a file exists.

Is there a good reason to add the SystemTime to the file name? And is this path/file (var/mobile/.../Downloads/app_inventor_...) deleted if the app is uninstalled?

Yes I still need to track down why the ResponseFileName isn't being respected. I had been working on improving the File support so that the Android and iOS versions are in alignment, but ran into some issues and had to back it out.

1 Like

I'm pretty sure, yes.

Yes, AFAIK nothing persists across the uninstall/reinstall of an application on iOS. Any data you'd want to persist has to be stored somewhere else.

1 Like

However, the paths should persist if the app is only updated (reinstalled). Of course, TinyDB "survives" a reinstallation, but unfortunately the paths (files) do not.

Furthermore, I don't know how to query a reinstallation within the app to then repeat the download. Besides, the user certainly won't be amused that the download has to be repeated (after every update).

I found a way to handle this problem. I start a dummy Player in Screen.Initialize (volume = 0) and use a timer to check if Player.IsPlaying is true. If not, the download must be repeated. The error message "unable to prepare" is overlaid with a notification prompting the user to download again.

It's not nice, but I could live with it.


But I would like to know, why these (download) paths (files) are no longer persistent after an update/reinstallation. @ewpatton

I'll have to investigate, but with the latest change the Web downloads should not be going into a temp path and should survive an update of the app. A complete reinstall will likely result in a new UUID for the app container and so the best course of action would be to not store the absolute path.

Separately, I'm working on implementing the missing blocks for the File component, which should give you more tools to track the state of things, particularly via the Exists block.

1 Like