Hello! Today we will talk about a recurring headache for mobile developers: Persistent, secure folder access in .NET MAUI.
We’ve all been there—you use the FilePicker to let a user select a directory for your app to store data or scan for plugins. It works perfectly while the app is open. But the moment the user reboots their phone or the OS clears the app’s cache, your "saved" path becomes a "Permission Denied" error.
This happens because modern mobile operating systems use scoped storage and sandboxing. A file path isn't just a string anymore; it’s a temporary lease. Let’s look at how to build a Virtual Picker that secures persistent access.
The Problem: Why your string path is lying to you
On Android (especially 11+) and iOS, the path you get from a picker is often a "content URI" or a temporary virtual path.
iOS: Access to external folders (outside the app's sandbox) is revoked as soon as the app process ends unless you explicitly "start accessing" a security-scoped resource.
Android: The Storage Access Framework (SAF) requires you to "take" a persistent permission for the URI; otherwise, it expires.
1. The iOS Solution: Security-Scoped Bookmarks
On iOS, if you want to remember a folder, you must create a "Bookmark." This is a piece of data you can save to Preferences and later resolve back into a URL that the OS will respect.
When the app restarts, you resolve this byte array back into a path. This is the only way to ensure your "Plugin" folder stays accessible.
2. The Android Solution: Persistable URI Permissions
Android requires a different ritual. When you receive a URI from an Intent, you must tell the ContentResolver that you intend to keep it.
3. Architecting the "Virtual Picker" Wrapper
To keep your shared code clean, you shouldn't deal with these platform-specific "bookmarks" or "URIs" in your ViewModels. Instead, create a IVirtualStorageService.
Method | Purpose |
|---|---|
PickFolderAsync() | Opens the native picker and returns a "Virtual Handle." |
GetFolderStream(handle) | Resolves the handle (using the bookmark/URI) and opens a stream. |
EnsureAccess(handle) | Checks if the OS still grants us permission. |
4. Why this matters for Plugin Systems
If you are building a system that loads dynamic .dll files (like we discussed in Article #1), you can't just ask the user to "Pick the plugin folder" every time they open the app. By implementing persistent access, you can create a "Settings" page where the user picks a "Workspace" once, and your app remains a powerful, automated tool from that point forward.
Summary
Stop treating file paths as simple strings. In the world of .NET MAUI, a path is a contract between your app and the OS. By using Security-Scoped Bookmarks on iOS and Persistable Permissions on Android, you can build an app that truly feels like a desktop-class "Power User" tool.
Have you struggled with losing file permissions after an app update or reboot? Let’s compare our "Storage Wrapper" implementations in the comments!

