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.
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
1. Which API should be used to store an OAuth access token securely on device?
2. What attribute combination marks a property as a SQLite table's auto-incrementing primary key in sqlite-net-pcl?
3. Why shouldn't a critical SQLite database file be stored in FileSystem.Current.CacheDirectory?
4. What is Preferences.Set/Get best suited for?
5. Which method call sets up a SQLite table for a model class T for the first time?
Was this page helpful?
You May Also Like
Data Binding Basics
Learn how MAUI connects UI controls to data using the BindingContext and the {Binding} markup extension, including binding modes and INotifyPropertyChanged.
The MVVM Pattern
Understand how Model-View-ViewModel separates UI from business logic in .NET MAUI, using CommunityToolkit.Mvvm's ObservableObject and RelayCommand to keep code testable.
Collections and CollectionView
Learn how CollectionView renders scrollable lists and grids in .NET MAUI, binding to ObservableCollection and customizing item layout with DataTemplate.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics