
Skill
maui-networking-offline-data
build MAUI networking and offline features
Description
Build .NET MAUI networking and offline data features with HttpClient DI, JSON serialization, emulator/simulator networking, debug cleartext exceptions, SQLite persistence, sync boundaries, retries, cancellation, and error handling. USE FOR: typed HttpClient clients, Android emulator 10.0.2.2, iOS simulator localhost, physical-device LAN/dev-tunnel fallback, offline-first screens, SQLiteAsyncConnection/EF Core sync metadata, sync queues, sensitive offline data encryption decisions, retry/backoff, and background batch/chunk sync. DO NOT USE FOR: auth redirects, Aspire service discovery, or UI layout.
SKILL.md
MAUI Networking and Offline Data
Use this skill when a MAUI feature calls backend APIs, works against local services during development, persists local data, or must behave well offline.
Workflow
- Inspect
MauiProgram.cs, existing API clients, model serialization, and data storage packages. - Register API clients through DI. Prefer typed clients or named clients over
constructing
HttpClientin pages. - Define request/response DTOs and serialization options once. Prefer
System.Text.Jsonsource generation for trimmed or NativeAOT-sensitive apps. - Decide the local development address by runtime platform and environment.
- Keep cleartext HTTP exceptions debug-only and platform-scoped.
- Define offline boundaries before schema/code: name the read-only cached reference data, the entities users can edit while offline, and the fields that remain server-authoritative.
- For SQLite/offline sync answers, show the storage initialization API before
the entity schema: explicitly name
SQLiteAsyncConnectionplusCreateTableAsync<T>for sqlite-net, or an EF CoreDbContextsetup. Do not only show POCO models, sqlite-net attributes, ordb.Table<T>()queries. - In every offline-sync design, include a security line: if cached data is
sensitive, regulated, or contains customer/business PII, encrypt the local
SQLite store, for example with SQLCipher/provider encryption, and keep the
database key in
SecureStoragerather than source code orPreferences. - In every offline-sync design, include a background performance line: drain
queued sync work in bounded batches/chunks off the UI thread, and marshal only
UI updates through
MainThreadwhen needed. - Store local data in SQLite or app data files behind a repository/service abstraction.
- Add cancellation, timeout, retry, and user-facing error states.
HttpClient DI Pattern
builder.Services.AddHttpClient<IProductsApi, ProductsApi>(client =>
{
client.BaseAddress = new Uri("https://api.contoso.dev/");
});
Keep auth headers in a delegating handler or typed client boundary. Do not add bearer tokens in every page or ViewModel.
Local Development Networking
| Runtime | Local backend address |
|---|---|
| Android emulator | Use 10.0.2.2 to reach the host machine. |
| iOS simulator | localhost usually reaches the Mac host. |
| Windows/Mac Catalyst app | localhost reaches the same machine. |
| Physical device | Use a LAN-reachable hostname/IP, reverse tunnel, dev proxy, or deployed endpoint. |
For cleartext HTTP during development:
- Android needs a debug-only network security config such as
networkSecurityConfigorUsesCleartextTraffic. Gate it with#if DEBUG,IsDevelopment, or an MSBuildConditionon the Debug configuration so it is never present in Release builds. - iOS/Mac Catalyst need an App Transport Security (
NSAppTransportSecurity) exception for debug HTTP, also guarded by a Debug-only condition. - Release builds should use HTTPS and remove broad cleartext exceptions.
Connectivity Check
Use MAUI connectivity APIs to decide when to show offline UI and when to drain sync queues:
public sealed class SyncService(IConnectivity connectivity)
{
public bool IsOnline => connectivity.NetworkAccess == NetworkAccess.Internet;
}
Subscribe to ConnectivityChanged from a long-lived service when the app should
resume sync on reconnect, and unsubscribe when the owner is disposed. Only
NetworkAccess.Internet indicates a routable connection. In migrated apps,
replace Xamarin.Essentials.Connectivity with Microsoft.Maui.Networking
APIs or injected IConnectivity.
JSON Guidance
- Use one
JsonSerializerOptionsinstance for casing, enum conversion, and null handling. - Prefer source-generated
JsonSerializerContextwhen the app is trimmed, NativeAOT-sensitive, or serializes many known DTOs. - Separate DTOs from database entities when local schema and API shape evolve at different speeds.
SQLite and Offline Sync
Use SQLite for structured local state, offline queues, and cached server data. Start offline-sync guidance with an explicit boundary list or table so the answer does not imply every local row is editable. For example:
| Boundary | Examples | Sync behavior |
|---|---|---|
| Read-only cached reference data | product catalog, feature flags, lookup values | pull/refresh from server; do not enqueue local edits |
| Offline-editable user data | draft orders, notes, inspection forms | local writes get dirty state and outbox entries |
| Server-authoritative data | payment status, inventory counts, account roles | display cached values but require online server confirmation before mutation |
For sqlite-net, this usually means a SQLiteAsyncConnection and
CreateTableAsync<T> setup before any db.Table<T>() queries. For EF Core, keep
the local schema behind a DbContext.
Always include a short initialization snippet in offline-sync guidance so the
storage layer is concrete, not just the row shape or sqlite-net attributes.
Immediately after the boundary table or storage initialization, include a
one-sentence security decision for sensitive offline data, naming encryption
such as SQLCipher and a SecureStorage-protected key when applicable.
Also include a one-sentence sync performance decision using the words
background and batch or chunk, for example: "Drain the outbox on a
background worker in small batches/chunks and use MainThread only to update
progress UI."
var db = new SQLiteAsyncConnection(databasePath);
await db.CreateTableAsync<Order>();
await db.CreateTableAsync<SyncOperation>();
Common fields for syncable rows:
- Server ID and local ID.
UpdatedAt,ETag, row version, or another concurrency token.- Dirty state such as
PendingCreate,PendingUpdate, orPendingDelete. - Tombstone/deleted marker when deletes must sync.
Keep sync boundaries explicit:
- Cache read-only reference data separately from editable offline data and say which local tables are never enqueued for upload.
- Retry idempotent operations automatically; ask the user before replaying non-idempotent actions.
- Resolve conflicts with a documented policy: server wins, client wins, field merge, or user decision.
- Persist queued operations before sending them so app termination does not lose offline edits.
- Encrypt the local SQLite database when it stores sensitive business data.
Options include SQLCipher-based packages or provider-specific SQLite
encryption. Store the database key in
SecureStorage, not in source code orPreferences. - Page or chunk large sync operations and write in bounded batches so the app stays responsive and avoids mobile memory spikes.
- Drain sync queues on background work, not the UI thread; marshal only progress
or completion updates back with
MainThread.BeginInvokeOnMainThreadwhen UI needs to change.
Retry and Error Handling
- Pass
CancellationTokenfrom UI commands and lifecycle shutdown paths. - Use short connection timeouts and clear user messages for no network, unauthorized, validation, and server errors.
- Retry only transient failures such as timeouts and 5xx responses.
- Use exponential backoff with jitter for background sync.
- Do not silently swallow sync failures; surface pending state and retry status.
Validation Checklist
- API clients are registered in DI and are not created directly in UI code.
- Local dev base addresses are platform-aware.
- Cleartext HTTP exceptions are debug-only.
- SQLite state has an explicit sync/conflict boundary and names the concrete
storage API, such as
SQLiteAsyncConnectionwithCreateTableAsync<T>or an EF CoreDbContext. - Sensitive local data has an encryption decision, such as SQLCipher plus a key
stored in
SecureStorage. - Requests support cancellation and distinguish transient from permanent errors.
More skills from the maui-labs repository
View all 32 skillsandroid-slim-bindings
create Android slim bindings for .NET
Jul 12.NETAndroidJavaKotlin +1devflow-automation
automate .NET MAUI app state
Jul 12.NETAutomationMAUIMobiledevflow-connect
diagnose DevFlow agent connectivity issues
Jul 12DebuggingMAUINetworkingdotnet-workload-info
discover MAUI workload metadata
Jul 12C#CLIEngineeringMAUIios-slim-bindings
create iOS slim bindings for MAUI
Jul 12.NETiOSMAUISwift +1maui-accessibility
implement accessibility in .NET MAUI apps
Jul 12.NETAccessibilityMAUIMobile +1
More from .NET (Microsoft)
View publishermultithreaded-task-migration
migrate MSBuild tasks to multithreaded mode
msbuild
Jul 22.NETEngineeringPerformanceanalyzing-dotnet-performance
analyze .NET code for performance anti-patterns
skills
Jul 12.NETCode AnalysisDebuggingPerformanceandroid-tombstone-symbolication
symbolicate .NET runtime frames in Android tombstones
skills
Jul 12.NETAndroidDebuggingMicrosoftapple-crash-symbolication
symbolicate .NET runtime frames in crash logs
skills
Jul 12.NETDebuggingiOSmacOS +1assertion-quality
evaluate assertion quality in test suites
skills
Jul 12Code AnalysisQATestingauthor-component
create and review Blazor components
skills
Jul 15.NETBlazorC#UI Components +1