tauri icon indicating copy to clipboard operation
tauri copied to clipboard

[bug] App filesystem paths do not resolve correctly on iOS and Android

Open velocitysystems opened this issue 11 months ago • 19 comments

Describe the bug

appDataDir does not resolve filesystem paths correctly (or consistently) on iOS and Android.

OS Expected Actual Comments
macOS .../Library/Application Support/com.company.appname .../Library/Application Support/com.company.appname
Android /data/user/0/com.company.appname /data/user/0/com.company.appname
iOS .../Data/Application/5537853B-26D5-4B30-B99E-373146D19848 .../Data/Application/5537853B-26D5-4B30-B99E-373146D19848/Library/Application Support/com.company.appname

Reproduction

Use the appDataDir API from @tauri-apps/api.

Expected behavior

macOS

  • Unsigned applications typically store application data under: ~/Library/Application Support/[App Name]
  • Signed (sandboxed) macOS applications have directories located under the ~/Library/Containers folder. E.g. ~/Library/Containers/[Bundle Identifier]/Data/[Subfolder]

iOS

  • Sandbox paths are managed by the system and vary for each app. Each app gets its unique directory under /var/mobile/Containers E.g. /var/mobile/Containers/Data/Application/[UUID]/[Subfolder]

Full tauri info output

[✔] Environment
    - OS: Mac OS 15.2.0 arm64 (X64)
    ✔ Xcode Command Line Tools: installed
    ✔ rustc: 1.83.0 (90b35a623 2024-11-26)
    ✔ cargo: 1.83.0 (5ffbef321 2024-10-29)
    ✔ rustup: 1.27.1 (54dd3d00f 2024-04-24)
    ✔ Rust toolchain: stable-aarch64-apple-darwin (default)
    - node: 22.12.0
    - pnpm: 9.15.2
    - npm: 10.9.0

[-] Packages
    - tauri 🦀: 2.1.1
    - tauri-build 🦀: 2.0.3
    - wry 🦀: 0.47.2
    - tao 🦀: 0.30.8
    - @tauri-apps/api : 2.1.1 (outdated, latest: 2.2.0)
    - @tauri-apps/cli : 2.1.0 (outdated, latest: 2.2.2)

[-] Plugins
    - tauri-plugin-http 🦀: 2.2.0
    - @tauri-apps/plugin-http : 2.2.0
    - tauri-plugin-sql 🦀: 2.2.0
    - @tauri-apps/plugin-sql : 2.2.0
    - tauri-plugin-os 🦀: 2.2.0
    - @tauri-apps/plugin-os : 2.0.0 (outdated, latest: 2.2.0)
    - tauri-plugin-fs 🦀: 2.2.0
    - @tauri-apps/plugin-fs : 2.2.0
    - tauri-plugin-upload 🦀: 2.2.1
    - @tauri-apps/plugin-upload : 2.2.1
    - tauri-plugin-log 🦀: 2.2.0
    - @tauri-apps/plugin-log : 2.0.1 (outdated, latest: 2.2.0)

[-] App
    - build-type: bundle
    - CSP: default-src 'self' ipc: http://ipc.localhost; img-src 'self' asset: http://asset.localhost
    - frontendDist: ../dist
    - devUrl: http://localhost:5173/
    - framework: Vue.js
    - bundler: Rollup

Stack trace

No response

Additional context

#5263

velocitysystems avatar Jan 06 '25 16:01 velocitysystems

The resolved path should exist without having to create it.

This is not how Tauri handled paths ever. It never creates paths it doesn't need itself.

Unsigned applications typically store application data under: ~/Library/Application Support/[App Name]

Our choice to use the id is indeed a bit weird, same on windows for example but it's not been a real issue either. (still wanna change it at some point ig)

Signed (sandboxed) macOS applications have directories located under the ~/Library/Containers folder. E.g. ~/Library/Containers/[Bundle Identifier]/Data/[Subfolder]

Our sandbox support is a bit lackluster but this may already work, at least i didn't hear about issues with it before. Did you test this as well?

iOS /Users/johndoe/Library/Developer/CoreSimulator/Devices/6C6435C0-2956-46E4-B7EE-006D5C14770A/data/Containers/Data/Application/5537853B-26D5-4B30-B99E-373146D19848/Library/Application Support/com.company.appname

Everything up to /Library/Application Support/com.company.appname comes from iOS directly. The rest comes from the dirs crate applying macOS logic on iOS (of course we could just do it ourselves). A bit ugly but still working afaik.

FabianLars avatar Jan 06 '25 17:01 FabianLars

Thanks @FabianLars. It’s true that while this works, this approach is not ideal.

Ideally appDataDir should resolve the application root path i.e. On iOS: /var/mobile/Containers/Data/Application/[UUID]. This path is guaranteed to exist since it is the root of the application sandbox.

If the dirs crate is automatically appending “/Library/Application Support/com.company.appname” — this is a bug. Appending subfolders should be at the developer’s discretion and not automatically imposed by the framework itself. It should simply resolve the root application folder for each platform.

velocitysystems avatar Jan 09 '25 23:01 velocitysystems

@FabianLars I agree with @velocitysystems on this one. I'm currently prototyping a new Tauri-based app that will (hopefully) replace a native app that was built for iOS, Android, and Windows. So, on (eventual) upgrade to the new Tauri-based version of my app, I need to retain the user's previous data and files, which can be many GB worth of files since this app includes offline audio and video playback. Thus, it would be ideal for us if the root directory returned by Tauri was truly the root directory of the app, according to each platform - not a contrived app root from Tauri. What's the likelihood of getting this fixed / changed?

jthomerson avatar Jan 10 '25 21:01 jthomerson

If the dirs crate is automatically appending “/Library/Application Support/com.company.appname” — this is a bug.

The id is from us, the rest from them: https://github.com/dirs-dev/dirs-rs/blob/main/src/mac.rs#L7 (file is called mac but it's used on ios as well https://github.com/dirs-dev/dirs-rs/blob/main/src/lib.rs#L23). Like i said above, we could easily work around this in Tauri, but i'd still consider this a bug on their side.

Appending subfolders should be at the developer’s discretion and not automatically imposed by the framework itself. It should simply resolve the root application folder for each platform.

The each platform part doesn't make sense unless you confuse tauri's appDataDir function with something like a Windows AppData dir getter. On desktop the method's current behavior makes sense. On mobile it's not adapted to the sandboxed-by-default architecture.

What's the likelihood of getting this fixed / changed?

🤷

A pr would for sure raise the chances and i think mobile support is still young enough that we can consider this a bug fix and not something we need a new major or some config for.

FabianLars avatar Jan 10 '25 22:01 FabianLars

Thanks @FabianLars @jthomerson.

Rather than taking a custom approach, the best path forward may simply be for Tauri to emulate how other cross-platform frameworks already handle filesystem access e.g. React Native (Expo) and MAUI. Will review these implementations and follow-up shortly with a proposal for review.

velocitysystems avatar Jan 15 '25 21:01 velocitysystems

@lucasfernog See below table for standard file paths on iOS and Android.

iOS Tested with iPhone 16 simulator (iOS 18.4).

Type Source Output
Root NSHomeDirectory() /Users/user/Library/Developer/CoreSimulator/Devices/414F89E8-571D-422A-8130-70F994169E1D/data/Containers/Data/Application/ADD573AD-F239-4E67-A688-D680114DCCD7/
Documents FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! {Root}/Documents/
Library FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first! {Root}/Library/
Caches FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! {Root}/Library/Caches/
Temporary URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) {Root}/tmp/

Android Tested with Android 15 emulator (API 35).

Type Source Output
Root context.filesDir.parentFile /data/user/0/com.example.app/
Documents context.getExternalFilesDir(null) /storage/emulated/0/Android/data/com.example.app/files/
Library context.filesDir {Root}/files/
Caches context.cacheDir {Root}/cache/
Temporary context.cacheDir {Root}/cache/

velocitysystems avatar Apr 24 '25 10:04 velocitysystems

@FabianLars @lucasfernog I'd like to move forward on a PR for this issue. Can I please have your feedback on the proposed mappings above? Desktop behavior would be unchanged; changes would just be for iOS and Android to support the "sandboxed-by-default" architecture.

velocitysystems avatar Aug 19 '25 16:08 velocitysystems

most of it looks good to me. what i don't like is the Documents type - both what it maps to on Android and the tauri path api you specified. I don't have better suggestions right now but i feel like that's something we could also discuss when we're looking at the PR but maybe Lucas has better feedback than me already.

FabianLars avatar Aug 19 '25 17:08 FabianLars

@lucasfernog @FabianLars See revised suggested mappings for directory types to Tauri Path API:

Mappings (V2)

Type Tauri Path API Comments
Root appDataDir() Resolves to the root sandbox path (iOS, Android)
Documents appDocumentDir()* Resolves to the documents path (iOS), external files path (Android)
Library appConfigDirectory() Resolves to the library path (iOS), files path (Android)
Caches appCacheDir() Resolves to the cache(s) path (iOS, Android)
Temporary appTempDir()* Resolves to the temp path (iOS), cache path (Android)

*Note: These are new methods. These will resolve to mobile-specific paths (described here) and fall back to existing desktop implementations: documentDir() and tempDir().

velocitysystems avatar Oct 13 '25 16:10 velocitysystems

My only concern is breaking APIs. A change in the existing path APIs will break for anyone using them to store data.

lucasfernog avatar Oct 16 '25 16:10 lucasfernog

I understand @lucasfernog. To avoid breaking changes what if we make this change entirely additive? Ideally we'd also align appConfigDirectory() and appCacheDir() but this could be done in a separate PR.

Mappings (V3)

Type Tauri Path API Comments
Root appRootDir()* Resolves to the root sandbox path (iOS, Android)
Documents appDocumentDir()* Resolves to the documents path (iOS), external files path (Android)
Temporary appTempDir()* Resolves to the temp path (iOS), cache path (Android)

*Note: These are new methods. These will resolve to mobile-specific paths (described here) and fall back to existing desktop implementations: documentDir() and tempDir(). appRootDir() will fall back to appDataDir() for desktop platforms.

velocitysystems avatar Oct 16 '25 17:10 velocitysystems

@velocitysystems I like the idea of additive changes. I'd personally favor appDocumentsDir() (plural) since the filesystem folder is also called "Documents". One option could be to mark appDataDir as deprecated in a future version and remove it entirely in a future major release.

jfahrenkrug avatar Oct 21 '25 14:10 jfahrenkrug

Sorry this will be a bit all over the place. I'm typing as i'm thinking and researching.

The more i look into this the more i want to have something like iosDocuments, androidCacheDir whatever. I just find it really hard to abstract over the platform differences between android, ios, and desktop. Looking at what other frameworks do:

  • capacitor: android & ios only - falls back to ios documents dir for almost all predefined paths
  • flutter: Very few APIs but all 5 platforms. On first glance i liked this the most (it's also what made me think more about simply having platform specific apis).
  • MAUI (because you mentioned it): Seems like it only has 2 apis? cache and appdata. That's perhaps a bit too extreme but i wouldn't mind having only 2 shared apis with the rest(?) being platform specific (could be added on demand too ig).
  • expo: confuses me so much with their new and legacy plugins but it looks like they also only have 3 apis, the ones from your last comment, the app install/bundle dir, documents, and cache

So the easy one here is the cache dir. Considering its nature imo we can just "break" the existing APIs - i assume we're aiming for appCacheDir and cacheDir (both returning the same path on mobile). Not sure if it requires any changes though, lost the overview a bit.

About that iOS temp dir, did you ever check the existing tempDir which uses https://doc.rust-lang.org/std/env/fn.temp_dir.html ? As far as i understand it, this should either match what you're proposing or hopefully be close enough to not introduce yet another temp api.

I think context.getExternalFilesDir(null) should get its own function like it is in flutter, if it gets one at all at this point in time.

It also looks like NSHomeDirectory should never be used on iOS, i didn't read a single positive comment about it. I also couldn't find a single mention of context.filesDir.parentFile anywhere, including alternative syntax of course. Do you have an example for that? So, i don't see any value in appRootDir right now (the name may also be misleading but that's besides the point).

how does the current documentDir() function behave on iOS and android? Did you check that? i may be open to "break" that one as well depending on that and considering that in theory app devs shouldn't rely on this being persistent (eg when the app is re-installed).


I'd personally favor appDocumentsDir() (plural) since the filesystem folder is also called "Documents".

Same but we already have documentDir so we should match that for now imo but can rename it in v3.

FabianLars avatar Oct 22 '25 19:10 FabianLars

Thanks @FabianLars. Discussed with @lucasfernog and appreciate all your comments and research.

I agree with your comments about appRootDir; I think we can safely avoid the need for that if we have APIs which can resolve documents, library, caches and temporary paths on all platforms. While NSHomeDirectory isn't officially deprecated by Apple, the recommended approach is to use FileManager APIs.

Let me do some further checks around the tempDir API and behaviour of documentDir() on iOS and Android. Good thought about adding a separate API for resolving external files directory on Android; let me take a look at this one too.

velocitysystems avatar Oct 24 '25 10:10 velocitysystems

I have zero idea on whether it's gonna be helpful, but I found this page on the Android developer website: https://developer.android.com/training/data-storage/

All these different types of directories refer to very specific ways to access them, and for example "App-specific files" are accessed via getFilesDir() or getCacheDir() functions in Android's API (probably in Kotlin, idk, I'm not an expert).

What I think is important to do is to, sort of, find a way to clarify how "directories" work on all platforms, which you seem to have been doing for some time now, but also a potential cross-version-compatible method to access them.

If a particular Android version says it's "getFilesDir()" but another version says you cannot, there's only two choices: either Tauri doesn't support it and has to expose all APIs, even the legacy ones, and "just" panic at compile-time if the OS's target version doesn't match anything Tauri is capable to do, or if the developer used an OS-specific function that doesn't exist in this target.

For instance, storing in a "Database" needs the "Room" Kotlin library (there's docs there on how to add it: https://developer.android.com/training/data-storage/room) , but so far I have no idea on what Tauri can do to help developers have access to the Room lib directly from Rust code. There's documentation on how to use "app/cache" data storages there: https://developer.android.com/training/data-storage/app-specific , but as said, I don't understand how to make Tauri call them from Rust code.

Pierstoval avatar Oct 25 '25 22:10 Pierstoval

@FabianLars I've been working with @velocitysystems on this issue, and I have a proposal for next steps with this issue.

Short version

3 proposed changes:

  1. A sample page in the examples/api/src/App.svelte to allow for testing directory resolutions
  2. A feature flag that will allow folder paths to be resolved using default OS implementations
  3. Addition of a Swift implementation of the PathResolver for use in iOS

Long version

In order to better understand the current state of these methods and where any changes would need to be implemented, I added a page to the examples/api/src/App.svelte to print out what we actually get for all the different path methods across platforms, and have placed the results in the table below.

There was found a lack of consistency (even between iOS and Android) for which directories we resolve to for each of these methods.

For example, the homeDir method on iOS returns us the path that we would initially expect to receive from appDataDir (the app sandbox), but on Android it provides a folder which is the root of the storage device, not the root of the app sandbox.

After discussing with @velocitysystems, we'd like to propose a feature flag that can be set to determine how paths are resolved on mobile platforms.

This would allow us to return the paths that are specifically used on mobile (such as we get from FileManager on iOS and Context on Android) from the methods we already have (such as appDataDir), without creating any more methods or breaking current usages.

When the time comes for V3 or some other major version update, this proposed implementation (using iOS FileManager and Android Context) would become the standard path for mobile.

This does bring up one other question: I noticed that there is no Swift implementation for the pathResolver, only a Kotlin implementation of PathPlugin.

My understanding is that iOS and MacOS share the same calls made directly from Rust (please correct me if I am wrong).

With that in mind, I would propose that these changes also include the introduction of a Swift implementation of the PathResolver so that calls made on iOS for directories will use the standard iOS libraries.

If this is acceptable, the above-mentioned feature flag could be set when initializing the PathPlugin on Android and iOS.

Thanks very much for considering; please let us know if these changes are acceptable, or if there are any concerns with this approach.

Output of all available path methods on Android, iOS, MacOS, and Windows PathResolutionResults.csv

onehumandev avatar Nov 20 '25 23:11 onehumandev

Addition of a Swift implementation of the PathResolver for use in iOS

hmm, i'd prefer not to do that unless we need to eg if we need the paths in swift to do XY or the objc bindings are missing something.

After discussing with @velocitysystems, we'd like to propose a feature flag that can be set to determine how paths are resolved on mobile platforms.

Makes sense to me. Wondering if we should re-use that flag for new desktop paths as well (for example using the bundle identifier on Windows while arguable a good idea is far from standard practice)

FabianLars avatar Nov 24 '25 13:11 FabianLars

Sounds good; I am away this week, but next week I'll implement the feature flag and see how far that gets us.

onehumandev avatar Nov 24 '25 14:11 onehumandev

@FabianLars @velocitysystems I've created a Draft MR for these changes. As it turned out, creating a feature flag ended up causing more issues than it solved, and there is a way for the needed functionality to be provided additively, with the only change being in the iOS implementation of appCacheDir.

A comparison of what we get from the path api currently and after the changes in the MR is attached in the MR description.

I don't know if I missed something in the process, or there is something else needed in the MR, but hopefully this is a good path for fleshing out the remaining needed changes and discussions. Thanks very much!

onehumandev avatar Dec 02 '25 22:12 onehumandev