Skip to content

5.3 Local Storage and Persistence

ankrypht edited this page Jul 16, 2025 · 1 revision

Page: Local Storage and Persistence

Local Storage and Persistence

Relevant source files

The following files were used as context for generating this wiki page:

This document covers the local storage and persistence mechanisms used by AudioScape to maintain user data, preferences, and downloaded content across app sessions. The system employs multiple storage strategies including MMKV for high-performance key-value storage and the file system for library data and downloaded media files.

For information about state management patterns and Redux integration, see State Management. For details about external API data flows, see External API Integration.

Storage Architecture Overview

AudioScape implements a multi-layered persistence strategy that separates different types of data based on their access patterns and requirements:

graph TB
    subgraph "AudioScape Storage Systems"
        subgraph "MMKV Storage"
            MMKV["MMKV Instance"]
            SETTINGS["App Settings"]
            PREFS["User Preferences"]
        end
        
        subgraph "File System Storage"
            LIBRARY_JSON["libraryData.json"]
            DOWNLOADS_DIR["Downloads Directory"]
            ARTWORK_DIR["Artwork Cache"]
        end
        
        subgraph "Redux State Management"
            LIBRARY_STORE["LibraryState"]
            FAVORITES["favoriteTracks[]"]
            PLAYLISTS["playlists{}"]
            DOWNLOADED["downloadedTracks[]"]
        end
        
        subgraph "Device File System"
            DOCUMENTS["DocumentDirectory"]
            MEDIA_LIB["MediaLibrary"]
        end
    end
    
    MMKV --> SETTINGS
    MMKV --> PREFS
    
    LIBRARY_STORE --> LIBRARY_JSON
    LIBRARY_JSON --> DOCUMENTS
    
    DOWNLOADS_DIR --> MEDIA_LIB
    ARTWORK_DIR --> DOCUMENTS
    
    FAVORITES --> LIBRARY_JSON
    PLAYLISTS --> LIBRARY_JSON
    DOWNLOADED --> LIBRARY_JSON
Loading

Sources: storage.ts:1-16, store/library.tsx:1-407

MMKV Key-Value Storage

The application uses MMKV for high-performance, synchronous key-value storage of preferences and settings that require frequent access.

MMKV Instance Configuration

The storage instance is initialized as a singleton and provides synchronous access to stored data:

graph LR
    APP["Application"] --> STORAGE["storage"]
    STORAGE --> MMKV_INSTANCE["MMKV()"]
    
    MMKV_INSTANCE --> SET["set(key, value)"]
    MMKV_INSTANCE --> GET["getString(key)"]
    MMKV_INSTANCE --> DELETE["delete(key)"]
    MMKV_INSTANCE --> CLEAR["clearAll()"]
Loading

The MMKV storage is encrypted by default and provides better performance than AsyncStorage for frequent read/write operations.

Sources: storage.ts:9-15

Library Data Persistence

The core library data including favorites, playlists, and downloaded track metadata is persisted using expo-file-system as JSON files.

Library State Structure

The LibraryState interface defines the complete structure of persisted library data:

Field Type Description
favoriteTracks Song[] Array of user's favorite songs
playlists Record<string, Song[]> User-created playlists mapped by name
downloadedTracks DownloadedSongMetadata[] Metadata for locally stored songs
activeDownloads Record<string, {song, progress}> Currently downloading songs with progress

Sources: store/library.tsx:16-21

File System Persistence Flow

graph TD
    subgraph "Redux Actions"
        TOGGLE_FAV["toggleFavorite"]
        ADD_PLAYLIST["addToPlaylist"]
        ADD_DOWNLOAD["addDownloadedTrack"]
    end
    
    subgraph "Persistence Layer"
        SAVE_TO_FILE["saveToFile()"]
        FILE_PATH["dataFilePath"]
        JSON_STRINGIFY["JSON.stringify()"]
        WRITE_FILE["FileSystem.writeAsStringAsync()"]
    end
    
    subgraph "File System"
        LIBRARY_JSON["libraryData.json"]
        DOCUMENT_DIR["DocumentDirectory"]
    end
    
    TOGGLE_FAV --> SAVE_TO_FILE
    ADD_PLAYLIST --> SAVE_TO_FILE
    ADD_DOWNLOAD --> SAVE_TO_FILE
    
    SAVE_TO_FILE --> JSON_STRINGIFY
    JSON_STRINGIFY --> WRITE_FILE
    WRITE_FILE --> FILE_PATH
    FILE_PATH --> LIBRARY_JSON
    LIBRARY_JSON --> DOCUMENT_DIR
Loading

The saveToFile function at store/library.tsx:55-62 automatically persists state changes to ${FileSystem.documentDirectory}libraryData.json whenever Redux actions modify the library state.

Sources: store/library.tsx:48-77, store/library.tsx:88-99, store/library.tsx:105-117

Data Loading and Initialization

graph TD
    subgraph "App Initialization"
        INIT["initializeLibrary()"]
        ENSURE_FILE["ensureFileExists()"]
        LOAD_DATA["loadStoredData()"]
    end
    
    subgraph "File Operations"
        CHECK_EXISTS["FileSystem.getInfoAsync()"]
        READ_FILE["FileSystem.readAsStringAsync()"]
        PARSE_JSON["JSON.parse()"]
    end
    
    subgraph "Redux Dispatch"
        SET_FAVORITES["setFavoriteTracks"]
        SET_PLAYLISTS["setPlaylists"]
        SET_DOWNLOADS["setDownloadedTracks"]
    end
    
    INIT --> ENSURE_FILE
    INIT --> LOAD_DATA
    
    ENSURE_FILE --> CHECK_EXISTS
    LOAD_DATA --> READ_FILE
    READ_FILE --> PARSE_JSON
    
    PARSE_JSON --> SET_FAVORITES
    PARSE_JSON --> SET_PLAYLISTS
    PARSE_JSON --> SET_DOWNLOADS
Loading

The initialization process at store/library.tsx:401-404 ensures data persistence across app restarts and handles file creation for new installations.

Sources: store/library.tsx:68-77, store/library.tsx:380-395, store/library.tsx:401-404

Downloaded Content Storage

Downloaded songs and their associated metadata are stored using a combination of the device's media library and file system storage.

Downloaded Song Metadata Structure

The DownloadedSongMetadata interface defines the structure for tracking downloaded content:

Field Type Description
id string Unique song identifier
title string Song title
artist string Artist name
duration number? Duration in seconds
localTrackUri string Local content:// URI for playback
mediaLibraryAssetId string MediaLibrary asset ID
localArtworkUri string? Local artwork URI
downloadDate string ISO date string

Sources: store/library.tsx:26-35

Download Storage Integration

graph TD
    subgraph "Download Process"
        DOWNLOAD_REQUEST["downloadAndSaveSong()"]
        FETCH_AUDIO["Fetch Audio Stream"]
        FETCH_ARTWORK["Fetch Artwork"]
    end
    
    subgraph "File System Storage"
        MEDIA_LIBRARY["MediaLibrary.createAssetAsync()"]
        ARTWORK_CACHE["Local Artwork Storage"]
        CONTENT_URI["content:// URI"]
    end
    
    subgraph "Redux State Update"
        ADD_DOWNLOADED["addDownloadedTrack"]
        METADATA["DownloadedSongMetadata"]
        LIBRARY_JSON["libraryData.json"]
    end
    
    subgraph "Playback Integration"
        TRACK_PLAYER["TrackPlayer.add()"]
        LOCAL_PLAYBACK["Local File Playback"]
    end
    
    DOWNLOAD_REQUEST --> FETCH_AUDIO
    DOWNLOAD_REQUEST --> FETCH_ARTWORK
    
    FETCH_AUDIO --> MEDIA_LIBRARY
    FETCH_ARTWORK --> ARTWORK_CACHE
    
    MEDIA_LIBRARY --> CONTENT_URI
    CONTENT_URI --> METADATA
    ARTWORK_CACHE --> METADATA
    
    METADATA --> ADD_DOWNLOADED
    ADD_DOWNLOADED --> LIBRARY_JSON
    
    METADATA --> TRACK_PLAYER
    TRACK_PLAYER --> LOCAL_PLAYBACK
Loading

The download system creates local content:// URIs that can be used directly by react-native-track-player for offline playback, as referenced in the MusicPlayerContext at components/MusicPlayerContext.tsx:637-644.

Sources: app/(modals)/menu.tsx:318-340, store/library.tsx:183-196, components/MusicPlayerContext.tsx:608-688

State Synchronization Patterns

The persistence layer ensures data consistency between Redux state and file system storage through automatic synchronization on state changes.

Redux Action Integration

graph LR
    subgraph "UI Components"
        FAVORITE_BTN["Favorite Button"]
        PLAYLIST_ACTION["Playlist Action"]
        DOWNLOAD_BTN["Download Button"]
    end
    
    subgraph "Redux Actions"
        TOGGLE_FAV["toggleFavorite"]
        ADD_TO_PLAYLIST["addToPlaylist"]
        ADD_DOWNLOAD["addDownloadedTrack"]
    end
    
    subgraph "Auto Persistence"
        SAVE_TO_FILE["saveToFile(state)"]
        FILE_WRITE["FileSystem.writeAsStringAsync()"]
    end
    
    FAVORITE_BTN --> TOGGLE_FAV
    PLAYLIST_ACTION --> ADD_TO_PLAYLIST
    DOWNLOAD_BTN --> ADD_DOWNLOAD
    
    TOGGLE_FAV --> SAVE_TO_FILE
    ADD_TO_PLAYLIST --> SAVE_TO_FILE
    ADD_DOWNLOAD --> SAVE_TO_FILE
    
    SAVE_TO_FILE --> FILE_WRITE
Loading

Each Redux reducer automatically triggers saveToFile() to ensure immediate persistence of state changes, eliminating the need for manual save operations.

Sources: store/library.tsx:88-99, store/library.tsx:105-117, store/library.tsx:183-196

Custom Hooks for Data Access

The library provides type-safe hooks for accessing persisted data:

Hook Purpose Return Type
useFavorites() Access favorite tracks {favoriteTracks, toggleFavoriteTrack}
usePlaylists() Manage playlists {playlists, addTrackToPlaylist, ...}
useDownloadedTracks() Access downloaded songs DownloadedSongMetadata[]
useIsSongDownloaded(id) Check download status boolean
useActiveDownloads() Track download progress Song[] with progress

These hooks automatically subscribe to Redux state changes and provide real-time updates when persisted data changes.

Sources: store/library.tsx:283-294, store/library.tsx:300-327, store/library.tsx:333-373

Clone this wiki locally