# Maui File Handling

> >

- **Type:** Skill
- **Install:** `agentstack add skill-davidortinau-maui-skills-maui-file-handling`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [davidortinau](https://agentstack.voostack.com/s/davidortinau)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [davidortinau](https://github.com/davidortinau)
- **Source:** https://github.com/davidortinau/maui-skills/tree/main/plugins/maui-skills/skills/maui-file-handling

## Install

```sh
agentstack add skill-davidortinau-maui-skills-maui-file-handling
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# .NET MAUI File Handling

## Critical: Always Use `OpenReadAsync()`, Not `FullPath`

Some platforms (especially Android) return **content URIs**, not file system
paths. Reading `FullPath` directly will throw or return empty data.

```csharp
// ❌ Breaks on Android — FullPath may be a content:// URI
var result = await FilePicker.Default.PickAsync();
var bytes = File.ReadAllBytes(result.FullPath);

// ✅ Works on all platforms
var result = await FilePicker.Default.PickAsync();
if (result is not null)
{
    using var stream = await result.OpenReadAsync();
    // process stream
}
```

---

## Platform File-Type Format Gotcha

Each platform uses a **different format** for custom `FilePickerFileType`.
Mixing them up causes the picker to show no files or crash.

| Platform | Format | Example |
|---|---|---|
| Android | MIME types | `"application/json"` |
| iOS / macOS | UTType identifiers | `"public.json"` |
| Windows | Dot-prefixed extensions | `".json"` |

```csharp
// ❌ Using file extensions for Android — picker shows nothing
{ DevicePlatform.Android, new[] { ".json", ".txt" } }

// ✅ Correct MIME types for Android
{ DevicePlatform.Android, new[] { "application/json", "text/plain" } }
```

---

## Common Pitfalls

### Bundled files are read-only

`Resources/Raw` assets cannot be modified at runtime. Attempting to write
throws an exception (or silently fails on some platforms).

```csharp
// ❌ Trying to write to a bundled file
var path = "data.json"; // inside Resources/Raw
File.WriteAllText(path, newContent); // fails

// ✅ Copy to AppDataDirectory first, then modify
string targetPath = Path.Combine(FileSystem.Current.AppDataDirectory, "data.json");
if (!File.Exists(targetPath))
{
    using var source = await FileSystem.Current.OpenAppPackageFileAsync("data.json");
    using var dest = File.Create(targetPath);
    await source.CopyToAsync(dest);
}
// Now safe to read/write targetPath
```

### Bundled subdirectories are flattened

On some platforms, `Resources/Raw/subdir/file.txt` becomes just `file.txt`.
Use **unique file names** regardless of subdirectory structure.

### iOS sandbox path changes on rebuild

The iOS sandbox path includes an app GUID that changes across clean builds.
Hard-coded absolute paths break silently.

```csharp
// ❌ Hard-coded path — breaks after clean rebuild
var path = "/var/mobile/.../Documents/data.json";

// ✅ Always use the FileSystem helper
var path = Path.Combine(FileSystem.Current.AppDataDirectory, "data.json");
```

### Android: bundled stream has no `Length`

`OpenAppPackageFileAsync` may return a stream where `.Length` throws
`NotSupportedException`. Copy to a `MemoryStream` if you need the size.

```csharp
// ❌ Throws on Android
using var stream = await FileSystem.Current.OpenAppPackageFileAsync("data.json");
var size = stream.Length; // NotSupportedException

// ✅ Copy first if you need the length
using var stream = await FileSystem.Current.OpenAppPackageFileAsync("data.json");
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
var size = ms.Length;
```

### Windows: virtualized file system

Packaged apps silently redirect writes to classic paths like `%AppData%`.
Always use `AppDataDirectory` and `CacheDirectory` for reliable
cross-platform paths.

### FilePicker returns `null` on cancellation

```csharp
// ❌ NullReferenceException when user cancels
var result = await FilePicker.Default.PickAsync();
using var stream = await result.OpenReadAsync();

// ✅ Always null-check
var result = await FilePicker.Default.PickAsync();
if (result is null) return;
using var stream = await result.OpenReadAsync();
```

---

## Android Permissions (API 33+ change)

Android 13 replaced `READ_EXTERNAL_STORAGE` with granular media permissions.
Using the old permission on API 33+ silently grants nothing.

| Android version | Required permission |
|---|---|
| ≤ 12 (API 32) | `READ_EXTERNAL_STORAGE` |
| ≥ 13 (API 33) | `READ_MEDIA_IMAGES`, `READ_MEDIA_VIDEO`, `READ_MEDIA_AUDIO` |

---

## Checklist

- [ ] Use `OpenReadAsync()` — never read `FullPath` directly
- [ ] Null-check `FilePicker` result before accessing properties
- [ ] Custom `FilePickerFileType` uses correct format per platform (MIME / UTType / extension)
- [ ] Bundled files copied to `AppDataDirectory` before modification
- [ ] Unique file names in `Resources/Raw` (subdirectories are flattened)
- [ ] Android manifest declares correct permission for target API level
- [ ] macOS: App Sandbox entitlement includes file access

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [davidortinau](https://github.com/davidortinau)
- **Source:** [davidortinau/maui-skills](https://github.com/davidortinau/maui-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-davidortinau-maui-skills-maui-file-handling
- Seller: https://agentstack.voostack.com/s/davidortinau
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
