Skip to content

Daniele-rolli/capacitor-scoped-storage

Repository files navigation

πŸ—‚οΈ Capacitor Scoped Storage

A Capacitor plugin for secure, user-approved file and folder access on iOS and Android, built on top of platform-native Security Scoped Bookmarks (iOS) and Storage Access Framework (SAF) (Android).

@daniele-rolli/capacitor-scoped-storage

This plugin lets your app request a folder from the user and then perform safe file operations (read, write, append, delete, move, copy, stat, etc.) within that scope, with persistent access across app restarts.

πŸ‘‰ For a real-world setup, check out the example-app with a full implementation.

πŸ“‘ Table of Contents

✨ Features

  • πŸ”’ Scoped access: Operates only within the folder the user selects

  • πŸ“‚ Cross-platform:

    • iOS β†’ Security-scoped bookmarks
    • Android β†’ Storage Access Framework (SAF)
  • πŸ“‘ File operations: readFile, writeFile, appendFile, deleteFile

  • πŸ“¦ Directory management: mkdir, rmdir, readdir

  • πŸ” Metadata utilities: stat, exists

  • πŸ“Ž Path management: move, copy, getUriForPath

  • Persistent folder permissions (reusable bookmarks/tree URIs)

  • UTF-8 and Base64 encoding support

πŸ“¦ Installation

npm install @daniele-rolli/capacitor-scoped-storage
npx cap sync

Requirements:

  • iOS deployment target: 14.0+
  • Android: API 21+

πŸ›  Usage

Pick a Folder

const { folder } = await ScopedStorage.pickFolder();
// { folder: { id: "...", name: "Documents" } }

Write & Read a File

await ScopedStorage.writeFile({
  folder,
  path: 'notes/today.txt',
  data: 'Hello world!',
});

const res = await ScopedStorage.readFile({
  folder,
  path: 'notes/today.txt',
});

console.log(res.data); // "Hello world!"

List Directory Contents

const { entries } = await ScopedStorage.readdir({ folder, path: 'notes' });

console.log(entries);
// [{ name: "today.txt", isDir: false, size: 123, mtime: 1710000000 }]

Move & Copy

await ScopedStorage.move({
  folder,
  from: 'notes/today.txt',
  to: 'archive/today.txt',
  overwrite: true,
});

await ScopedStorage.copy({
  folder,
  from: 'archive/today.txt',
  to: 'backup/today.txt',
});

πŸ“š API Reference

Plugin Methods

  • pickFolder(): Promise<PickFolderResult>
  • writeFile(options: WriteOptions): Promise<void>
  • appendFile(options: AppendOptions): Promise<void>
  • readFile(options: ReadOptions): Promise<{ data: string }>
  • mkdir(options: MkdirOptions): Promise<void>
  • rmdir(options: RmdirOptions): Promise<void>
  • readdir(options: ReaddirOptions): Promise<ReaddirResult>
  • stat(options: StatOptions): Promise<StatResult>
  • exists(options: ExistsOptions): Promise<ExistsResult>
  • deleteFile(options: DeleteOptions): Promise<void>
  • move(options: MoveCopyOptions): Promise<void>
  • copy(options: MoveCopyOptions): Promise<void>
  • getUriForPath(options: UriForPathOptions): Promise<UriForPathResult>

πŸ“ API Types

Folder & Folder Selection

export interface FolderRef {
  /** Android: tree URI; iOS: base64 security-scoped bookmark */
  id: string;
  name?: string;
}

export interface PickFolderResult {
  folder: FolderRef;
}

File Operations

export interface WriteOptions {
  folder: FolderRef;
  path: string; // relative to folder
  data: string; // utf8 or base64
  encoding?: 'utf8' | 'base64';
}

export interface AppendOptions extends WriteOptions {}

export interface ReadOptions {
  folder: FolderRef;
  path: string;
  encoding?: 'utf8' | 'base64';
}

Directory Operations

export interface MkdirOptions {
  folder: FolderRef;
  path: string;
  recursive?: boolean;
}

export interface RmdirOptions {
  folder: FolderRef;
  path: string;
  recursive?: boolean;
}

export interface ReaddirOptions {
  folder: FolderRef;
  path?: string;
}

export interface ReaddirResult {
  entries: {
    name: string;
    isDir: boolean;
    size?: number | null;
    mtime?: number | null;
  }[];
}

File Metadata & Existence

export interface StatOptions {
  folder: FolderRef;
  path: string;
}

export interface StatResult {
  uri?: string;
  size?: number | null;
  mtime?: number | null;
  type: 'file' | 'directory' | 'unknown';
}

export interface ExistsOptions {
  folder: FolderRef;
  path: string;
}

export interface ExistsResult {
  exists: boolean;
  isDirectory: boolean;
}

File Management (Delete, Move, Copy)

export interface DeleteOptions {
  folder: FolderRef;
  path: string;
}

export interface MoveCopyOptions {
  folder: FolderRef;
  from: string;
  to: string;
  overwrite?: boolean;
}

Path Utility

export interface UriForPathOptions {
  folder: FolderRef;
  path: string;
}

export interface UriForPathResult {
  uri: string | null;
}

iOS

Add the following to your Info.plist:

<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>UISupportsDocumentBrowser</key>
<false/>

πŸ“„ License

MIT Author: Daniele Rolli

About

A Capacitor plugin to add support for scoped storage on Android and iOS.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published