Storage and Permissions Handling

Master storage and permissions handling for mobile apps. This tutorial covers core concepts, hands-on implementation, troubleshooting, and what to learn next in the Mobile App Development track.

Focus: add storage and permissions handling

Sponsored

Your app’s feature set is growing. You’ve built screens, wired up logic, and maybe even synced data to the cloud. But every real mobile app eventually hits the same wall: it needs to save files locally, read photos, or access the device’s storage — and the OS blocks you until you ask the right way. Ignore this and your app crashes on user devices with security exceptions, get rejected in app-store review, or silently fail to save data. This lesson is where you stop guessing and start implementing storage and permissions handling the way modern mobile platforms expect — deliberate, scoped, and transparent to the user.

The problem this lesson solves

Every time your app touches a file outside its private sandbox, the operating system acts as a gatekeeper. On Android, that means the Storage Access Framework and runtime permissions. On iOS, it’s privacy-protected resources like Photos or Files. Without proper handling, you get the classic failure modes:

  • The app crashes with SecurityException or a missing NSPhotoLibraryUsageDescription key in the info plist.
  • Data silently fails to save because you wrote to a path the system no longer allows.
  • Users uninstall because a permission dialog appears at a random moment with no explanation.

The core problem: you need a reliable way to request access to device storage, handle the user’s response, and save/load data without breaking on any OS version. This lesson gives you a concrete, step-by-step pattern that works across Python mobile frameworks like Kivy and BeeWare.

Core concept / mental model

Think of the OS as a security guard stationed outside a building. Your app is a visitor who can move freely inside its own office (the sandbox), but must check in before entering shared spaces (like the user’s photo library or external SD card). The guard’s rules are the permission system.

Key definitions

  • Sandbox: A private directory your app can always read/write without asking — usually the app’s own documents folder. On Android it’s getFilesDir(), on iOS it’s ~/Documents.
  • Runtime permission: A permission the user must approve while the app is running (Android 6+). Previously, all permissions were granted at install time.
  • Permission request flow: The sequence of asking the user, waiting for their answer, and handling both outcomes.
  • External storage: Shared storage that might be accessible via public folders like DCIM or Downloads, but often requires a permission or a system picker.

The mental model in action

Imagine your app wants to export a PDF report to the user’s Downloads folder. What happens inside:

  1. Check — does your app already have permission?
  2. Request — if not, show the system dialog.
  3. Handle response — if granted, write the file; if denied, show a fallback (e.g., save to app sandbox).

This sequence is the same on every platform — only the APIs differ. Python frameworks give you a unified way to trigger it.

How it works step by step

Implementing storage and permissions handling is a repeatable pipeline. Follow these logical steps:

  1. Decide what you actually need. Most apps only need access to their own sandbox, which requires zero permissions. Only request external storage or photo access if your feature truly demands it.
  2. Declare the permission in the platform manifest. For Android, add the <uses-permission> tag with the correct string (e.g., READ_EXTERNAL_STORAGE). For iOS, add the usage description key to Info.plist.
  3. Check if the permission is already granted. In Python, you’ll usually delegate to a platform-specific library or a Python wrapper.
  4. Request the permission at the right moment. Ask only when the user triggers the feature that needs it, not on app launch. This drastically improves approval rates.
  5. Write or read the file using the appropriate API. For the sandbox, use plain file paths. For shared storage, use a system picker or the storage access framework.
  6. Handle the user’s “No”. Always provide a fallback — say, save to the app’s private storage and inform the user.

Hands-on walkthrough

We’ll build a small utility module for storage and permission handling that works with Kivy on Android and can be adapted to BeeWare. We’ll use plyer to call native APIs — the de-facto standard for cross-platform Python mobile.

Step 1: Install the library

pip install plyer

Step 2: Request permission before writing

from plyer import storagepath
from plyer import permissions
from android.permissions import request_permissions, Permission
import os

# Android-specific permission list
ANDROID_PERMISSIONS = [
    Permission.WRITE_EXTERNAL_STORAGE,
    Permission.READ_EXTERNAL_STORAGE
]

def ensure_storage_permission() -> bool:
    """Request storage permissions and return True if granted."""
    if not hasattr(permissions, 'check_permission'):
        # On iOS, permission for photo library is requested via separate API
        return True  # fallback: assume granted or use platform-specific flow

    granted = [
        permissions.check_permission(p) for p in ANDROID_PERMISSIONS
    ]
    if all(granted):
        return True

    # This blocks until user responds
    request_permissions(ANDROID_PERMISSIONS)
    return all(permissions.check_permission(p) for p in ANDROID_PERMISSIONS)

Expected output: If the user taps “Allow”, the function returns True. If denied, it returns False.

Step 3: Write a file to the app sandbox

The sandbox is always safe — no permission needed. Here’s how to save JSON data:

import json
import os
from plyer import storagepath

def save_to_app_storage(filename: str, data: dict):
    """Save data to the app's private documents directory."""
    # Get a writable base directory (works on both Android and iOS via plyer)
    base_dir = storagepath.get_documents_dir()
    # For Android, prefer internal storage; on iOS, this is already sandboxed
    app_dir = os.path.join(base_dir, 'MyAppData')
    os.makedirs(app_dir, exist_ok=True)

    file_path = os.path.join(app_dir, filename)
    with open(file_path, 'w') as f:
        json.dump(data, f)
    return file_path

# Example usage
result = save_to_app_storage('user_prefs.json', {'theme': 'dark'})
print(f'Saved to {result}')

Expected output: Saved to /data/user/0/org.example.myapp/files/MyAppData/user_prefs.json (path may vary).

Step 4: Write to shared storage (needs permission)

Now we attempt to save to Downloads, but only after asking:

from plyer import storagepath
from os import path

def save_to_downloads(filename: str, content: bytes):
    if not ensure_storage_permission():
        print('Permission denied — falling back to app sandbox.')
        save_to_app_storage(filename, content)
        return False

    downloads_dir = storagepath.get_downloads_dir()
    file_path = path.join(downloads_dir, filename)
    with open(file_path, 'wb') as f:
        f.write(content)
    print(f'Saved to {file_path}')
    return True

# Example with binary data
save_to_downloads('report.pdf', b'%PDF-1.4 fake content')

Expected output: Either Saved to /storage/emulated/0/Download/report.pdf or Permission denied — falling back to app sandbox.

Pro tip: Always call permission requests inside an event handler (like a button click), never in __init__ or on_start. This is a hard requirement on Android.

Compare options / when to choose what

Different storage and permission strategies trade convenience against user trust and platform compatibility. Here’s how to choose:

Option Use when Pros Cons
App sandbox (always allowed) Internal data, preferences, temporary files Zero code, works offline, no dialogs Data lost if user clears cache; not visible to user
External storage (via permission) Exporting files the user expects to find, like PDFs User can access via file manager Requires permission, risk of denial, more complex
System file picker (no permission) Importing user-selected files No permission needed, user controls access One file at a time, more UI code
Cloud storage Large datasets, sync across devices No local permission issues Requires network, account setup

When to choose what? For most internals, use the sandbox. For sharing files, prefer a system picker or a share sheet. Only request broad storage permission when you must automatically access a directory (e.g., a file manager app).

Troubleshooting & edge cases

Here are the most common issues and how to fix them:

1. Permission dialog never appears

  • Error: No dialog shows when you call request_permissions.
  • Fix: Verify that the permission is declared in build.gradle (or the AndroidManifest.xml in your template). If missing, the OS ignores the request.

2. Crash on startup because of missing usage description (iOS)

  • Error: App crashes immediately with This app has crashed because it attempted to access privacy-sensitive data.
  • Fix: Add the appropriate key to Info.plist, e.g. NSPhotoLibraryUsageDescription with a user-friendly explanation like “We need access to your photo library to save images.”

3. Permission denied but you expected it to be granted

  • Error: Permission denied, but the user swears they allowed it.
  • Fix: On Android 11+, the system may automatically reset permissions if you don’t request them immediately. Re-request after a user action. Also, note that READ_EXTERNAL_STORAGE is not needed if you only use MediaStore — always test on the target OS version.

4. Files written to sandbox disappear after app update

  • Error: User reports data loss after an update.
  • Fix: If you store data in getCacheDir() it can be cleared. Use the documents directory (getFilesDir()) and back up to a cloud service if needed.

What you learned & what's next

You now know how to add storage and permissions handling: you can distinguish between the always-available app sandbox and the guarded external storage, request permissions correctly, and gracefully handle denial. You completed a hands-on exercise that writes both to private and shared storage, and you can troubleshoot the most common pitfalls.

Next lesson

In the next step of the Mobile App Development track, we’ll build on this to sync local data with a backend service. You’ll take your saved files and use REST APIs to upload them — using the same permission-aware pattern to ensure network calls only happen after user consent. You’ll also learn about background sync and push notifications, which rely on similar permission flows.

Keep practicing: modify the example to save user photos (with permission) or export a database file, then test on a real device to see the dialogs in action.

Practice recap

Extend the example above to save a list of user tasks as JSON in sandbox storage, then add a button that exports the same data to the Downloads folder. Run it on a real Android device, deny the permission once, and verify the app falls back gracefully. Next, prepare for the upcoming lesson by investigating how to upload your locally saved file to a REST API.

Common mistakes

  • Declaring permissions only in the manifest but forgetting to request them at runtime — newer Android versions silently deny.
  • Asking for storage permission at app launch instead of when the feature is used — this decreases user trust and approval rates.
  • Assuming the app sandbox path is externally accessible by the user — it’s private; use external storage or a share sheet for user-facing files.
  • Testing only on the emulator where permissions are often auto-granted — always test on a physical device with Android 10+ or iOS 14+.

Variations

  1. Use the Android Storage Access Framework (ACTION_CREATE_DOCUMENT) to write to any user-chosen directory without requiring storage permission.
  2. For cross-platform app development with KivyMD or BeeWare, you can wrap permissions in a custom decorator to reuse across screens.
  3. Store user preferences in a secure key-value store (like SQLite or encrypted storage) instead of raw files for sensitive data.

Real-world use cases

  • A note-taking app that auto-saves drafts to internal storage and exports user notes as PDFs to Downloads (requires runtime permission).
  • A photo editing app that imports images from the gallery using a system picker (no permission needed) and saves edited results to the app sandbox or share sheet.
  • An enterprise file manager that must read/write multiple files in external storage, using broad READ/WRITE permissions and a permission request flow with fallback to SAF.

Key takeaways

  • The app sandbox is always writable without permissions — prefer it for internal data.
  • External storage and photo library access require runtime permission requests after an explicit user action.
  • Always declare permissions in the manifest/Info.plist and request them in an event handler, not at startup.
  • Handle denial gracefully by falling back to sandbox storage and informing the user.
  • Use Android Storage Access Framework or a file picker to avoid broad permissions when possible.
  • Test on real devices—permission dialogs and OS behavior differ from emulators.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.