MAUI Application Data Storage


Storing application data in .NET MAUI is necessary to maintaining state, saving user preferences or to manage data between application sessions. The type of data and the required persistence determines how it is stored. Below are several approaches to storing data in a .NET MAUI application:

.NET MAUI Data Storage Options

  • Key-value storage for simple settings and preferences, known as Preferences.
  • Saving data to a file of type text or JSON into a specific folder in the File System.
  • Secure data storage – Storing user password, token etc.
  • Local database storage of structured data (optimized for offline capabilities); SQLite Database.
  • Advanced data storage with ORM (Object-Relational Mapping) using Entity Framework Core – ORM in advanced data storage.

Preferences for simple key value storage

Preferences is used to store simple data (user settings, etc) and small pieces of state information that need to persist over application launches. It is used to store key value pairs and serves for small data.

Storing Data:

Preferences.Set("username", "JohnDoe");
Preferences.Set("loggedIn", true);
Preferences.Set("volume", 0.5);

Retrieving Data:

string username = Preferences.Get("username", "defaultUser");
bool isLoggedIn = Preferences.Get("loggedIn", false);
double volume = Preferences.Get("volume", 1.0);

Finally, we are saving data using Preferences.Set.

With Preferences.Get, we retrieve our stored data, with a default value if the key doesn’t exist.

File System for Storing Files

For storing files, such as JSON, text, or media, on the device’s file system, .NET MAUI lets you. There are many directories like AppDataDirectory for the storage of application dependent data.

Storing Data in a File:

string fileName = Path.Combine(FileSystem.AppDataDirectory, "mydata.txt");
File.WriteAllText(fileName, "Hello, File System!");

Reading Data from a File:

string fileName = Path.Combine(FileSystem.AppDataDirectory, "mydata.txt");
if (File.Exists(fileName))
{
    string content = File.ReadAllText(fileName);
}

File system provides app data directory through FileSystem.AppDataDirectory. This is the location where app specific data is stored.

File.WriteAllText and File.ReadAllText saves and reads from file.

Secure Storage for Sensitive Data

Used for storing sensitive information like Passwords or Tokens, you would like them secure. The solution it provides uses platform-specific mechanisms such as Keychain on iOS, or Keystore on Android.

Storing Data:

await SecureStorage.SetAsync("auth_token", "my_secure_token");

Retrieving Data:

string token = await SecureStorage.GetAsync("auth_token");

SecureStorage.SetAsync stores data securely.

SecureStorage.GetAsync retrieves the secure data.

NOTE: Exceptions could be thrown if the device doesn’t supports secure storage. This can be done using the use of exception handling.

Structured Data with SQLite Database

Structured data is stored locally using SQLite. It is a good fit when the data you need to persist is large or complex objects that have a relationship.

Setting Up SQLite in .NET MAUI
  1. Add SQLite NuGet Package:
    • dotnet add package sqlite-net-pcl.
  2. Define the Model:
    • Create a model representing your data.

Task Model(TaskItem.cs)

using SQLite;

namespace MauiAppExample
{
    public class TaskItem
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }

        public string Name { get; set; }
        public bool IsCompleted { get; set; }
    }
}

3. Set Up SQLite Database:

Create the database connection and CRUD operations.

Database Service (TaskDatabase.cs):

using SQLite;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace MauiAppExample
{
    public class TaskDatabase
    {
        private readonly SQLiteAsyncConnection _database;

        public TaskDatabase()
        {
            string dbPath = Path.Combine(FileSystem.AppDataDirectory, "tasks.db");
            _database = new SQLiteAsyncConnection(dbPath);
            _database.CreateTableAsync<TaskItem>().Wait();
        }

        public Task<List<TaskItem>> GetTasksAsync()
        {
            return _database.Table<TaskItem>().ToListAsync();
        }

        public Task<int> SaveTaskAsync(TaskItem task)
        {
            if (task.Id != 0)
            {
                return _database.UpdateAsync(task);
            }
            else
            {
                return _database.InsertAsync(task);
            }
        }

        public Task<int> DeleteTaskAsync(TaskItem task)
        {
            return _database.DeleteAsync(task);
        }
    }
}

SQLiteAsyncConnection: Manages the connection to the SQLite database.

CreateTableAsync<TaskItem>(): Creates the table if it doesn’t exist.

CRUD Methods:

  • GetTasksAsync(): Retrieves all tasks.
  • SaveTaskAsync(TaskItem task): Inserts or updates a task.
  • DeleteTaskAsync(TaskItem task): Deletes a task.

Advanced Data Handling using Entity Framework Core

However, if your app has more complex data relationship, you can use Entity Framework Core with SQLite. It acts as an ORM (Object Relation Mapping) so that we can easily work with the databases in objects.

Add EF Core and SQLite Provider:

dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Sqlite

Define a Data Context

using Microsoft.EntityFrameworkCore;

namespace MauiAppExample
{
    public class AppDbContext : DbContext
    {
        public DbSet<TaskItem> Tasks { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            string dbPath = Path.Combine(FileSystem.AppDataDirectory, "app.db");
            optionsBuilder.UseSqlite($"Filename={dbPath}");
        }
    }
}

DbContext: It represents an instance of the session with the database.

DbSet<TaskItem>: The collection of TaskItem entity in the database.

Inserting Data:

using (var db = new AppDbContext())
{
    db.Tasks.Add(new TaskItem { Name = "New Task", IsCompleted = false });
    db.SaveChanges();
}

Retrieving Data:

using (var db = new AppDbContext())
{
    var tasks = db.Tasks.ToList();
}

SaveChanges() commits the changes to the database.

DbSet<T> allows you to query and work with data.

Storage TypeDescriptionUse Cases
PreferencesKey-value storage for simple settings.User preferences, small state info.
File SystemStoring data in files.Text, JSON, local files.
Secure StorageSecurely storing sensitive data.Passwords, tokens.
SQLiteLocal database for structured data.Offline data, complex relationships.
Entity Framework CoreORM-based SQLite database handling.Complex data models, relationships.

Best Practices

  • Sensitive Data:
    • The one rule: You can always use Secure Storage for passwords, tokens, or other sensitive information.
  • Complex Data:
    • When dealing with structured data that is related to one another, use SQLite / Entity Framework Core.
  • Avoid Overuse of Preferences:
    • Lightweight data, such as user settings, is good for preferences, but they are not suited for larger or complex data.
  • Backup and Synchronization:
    • In case you need to avoid losing critical data, think about how to back up your data at the cloud level (data synchronization, for example).