100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Working with Local Data

Learn how .NET MAUI apps persist data locally using Preferences for simple settings, SQLite for structured storage, and FileSystem APIs for documents and cached files.

Data & BindingIntermediate10 min readJul 10, 2026
Analogies

Preferences for Simple Key-Value Settings

The Preferences API (Preferences.Set("key", value) and Preferences.Get("key", defaultValue)) stores small, primitive key-value pairs per app — things like a selected theme, a last-viewed tab index, or a login timestamp. It's backed by platform mechanisms like Android SharedPreferences and iOS NSUserDefaults, and it is not intended for large datasets or sensitive values like tokens.

🏏

Cricket analogy: Preferences is like a scorer jotting the toss result on a sticky note — quick, tiny facts like 'India won the toss', not meant for storing an entire match's ball-by-ball archive.

SQLite for Structured Local Storage

For structured, queryable data — a list of tasks, cached API results, offline records — MAUI apps typically use the sqlite-net-pcl NuGet package. You define plain C# classes annotated with [PrimaryKey, AutoIncrement], open a SQLiteAsyncConnection pointed at a file in FileSystem.AppDataDirectory, call CreateTableAsync<T>() once at startup, and then use InsertAsync, Table<T>().Where(...), and DeleteAsync for CRUD operations, all asynchronously.

🏏

Cricket analogy: SQLite tables are like a scorebook's structured columns for every ball bowled, batsman, runs, extras, queryable later to compute a bowler's economy rate, unlike a scattered pile of loose notes.

File System Access with FileSystem.AppDataDirectory

FileSystem.Current.AppDataDirectory gives you a path for files that should persist for the app's lifetime, such as the SQLite database file itself, exported PDFs, or downloaded documents. FileSystem.Current.CacheDirectory is for regenerable, disposable data like downloaded thumbnails; the operating system may clear it automatically under storage pressure, so nothing critical should live there exclusively.

🏏

Cricket analogy: AppDataDirectory is like the pavilion's permanent trophy cabinet holding items that stay season after season, while CacheDirectory is like the temporary practice-net equipment bin that groundstaff clear out regularly.

csharp
public class TaskRepository
{
    private readonly SQLiteAsyncConnection _db;

    public TaskRepository()
    {
        var dbPath = Path.Combine(FileSystem.Current.AppDataDirectory, "tasks.db3");
        _db = new SQLiteAsyncConnection(dbPath);
        _db.CreateTableAsync<TaskItem>().Wait();
    }

    public Task<List<TaskItem>> GetAllAsync() => _db.Table<TaskItem>().ToListAsync();

    public Task<int> SaveAsync(TaskItem item) =>
        item.Id == 0 ? _db.InsertAsync(item) : _db.UpdateAsync(item);

    public Task<int> DeleteAsync(TaskItem item) => _db.DeleteAsync(item);
}

public class TaskItem
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public string Title { get; set; }
    public bool IsDone { get; set; }
}

For sensitive values like OAuth tokens or passwords, use SecureStorage.Default.SetAsync/GetAsync instead of Preferences. SecureStorage encrypts values using the platform keystore (Android Keystore, iOS Keychain) rather than storing them as plain text.

Do not store your SQLite database file or any data you can't afford to lose inside FileSystem.Current.CacheDirectory. iOS and Android are both permitted to purge cache directories automatically when the device is low on storage, with no warning to the app.

  • Preferences.Set/Get store small primitive key-value settings like theme or last-viewed tab.
  • SecureStorage should be used instead of Preferences for tokens, passwords, or other sensitive values.
  • sqlite-net-pcl provides SQLiteAsyncConnection for structured, queryable local storage via plain C# model classes.
  • [PrimaryKey, AutoIncrement] and CreateTableAsync<T>() set up a SQLite table from a model class.
  • FileSystem.Current.AppDataDirectory holds files that must persist for the app's lifetime, like the database file.
  • FileSystem.Current.CacheDirectory holds regenerable data the OS may purge under storage pressure.
  • InsertAsync, UpdateAsync, DeleteAsync, and Table<T>().Where(...) form the core async CRUD API for sqlite-net-pcl.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#NETMAUIStudyNotes#WorkingWithLocalData#Local#Data#Preferences#Simple#StudyNotes#SkillVeris#ExamPrep