| MAUI-CommunityToolkit-8 :👈 | 👉:MAUI-CommunityToolkit-10 |
Hour 9 – REST APIs, HttpClient, JSON & Offline Sync |
Welcome to Hour 9 of your MAUI MVVM learning series. Today we cover how your MAUI app communicates with online services using:
In .NET MAUI, HttpClient should be created using Dependency Injection.
builder.Services.AddHttpClient(); builder.Services.AddTransient<ApiService>();
public class ApiService
{
private readonly HttpClient http;
public ApiService(HttpClient client)
{
http = client;
http.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
}
public async Task<List<TodoItem>> GetTodosAsync()
{
var json = await http.GetStringAsync("todos");
return JsonSerializer.Deserialize<List<TodoItem>>(json);
}
}
✔ HttpClient is reused efficiently by DI ✔ BaseAddress is configurable ✔ JSON is parsed using System.Text.Json
public class TodoItem
{
public int Id { get; set; }
public int UserId { get; set; }
public string Title { get; set; }
public bool Completed { get; set; }
}
✔ Properties match API JSON fields.
public partial class TodoViewModel : ObservableObject
{
private readonly ApiService api;
public TodoViewModel(ApiService service)
{
api = service;
}
[ObservableProperty]
private List<TodoItem> todos;
[RelayCommand]
private async Task LoadTodos()
{
Todos = await api.GetTodosAsync();
}
}
✔ Command-based loading ✔ Auto properties via source generators
<CollectionView ItemsSource="{Binding Todos}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10">
<Label Text="{Binding Title}" />
<Label Text="{Binding Completed}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
✔ Simple collection rendering ✔ Works cross-platform
public async Task<TodoItem> CreateTodoAsync(TodoItem item)
{
var json = JsonSerializer.Serialize(item);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await http.PostAsync("todos", content);
return JsonSerializer.Deserialize<TodoItem>(await response.Content.ReadAsStringAsync());
}
public async Task UpdateTodoAsync(TodoItem item)
{
var json = JsonSerializer.Serialize(item);
await http.PutAsync($"todos/{item.Id}",
new StringContent(json, Encoding.UTF8, "application/json"));
}
public Task DeleteTodoAsync(int id)
{
return http.DeleteAsync($"todos/{id}");
}
✔ Complete API CRUD support
Offline-first apps store data locally in SQLite and sync with API when online.
public class LocalTodo
{
[PrimaryKey, AutoIncrement]
public int LocalId { get; set; }
public int RemoteId { get; set; }
public string Title { get; set; }
public bool Completed { get; set; }
public bool IsSynced { get; set; }
}
public class SyncService
{
private readonly ApiService api;
private readonly TodoDatabase db;
public SyncService(ApiService a, TodoDatabase d)
{
api = a;
db = d;
}
public async Task SyncAsync()
{
var pending = await db.GetUnsyncedTodosAsync();
foreach (var item in pending)
{
var created = await api.CreateTodoAsync(new TodoItem
{
Title = item.Title,
Completed = item.Completed
});
item.RemoteId = created.Id;
item.IsSynced = true;
await db.SaveTodoAsync(item);
}
var remote = await api.GetTodosAsync();
await db.ReplaceLocalCopyAsync(remote);
}
}
✔ Local → remote push ✔ Remote → local refresh ✔ Makes your app work offline
Use MAUI’s Connectivity API to detect network state:
if (Connectivity.NetworkAccess == NetworkAccess.Internet)
{
await syncService.SyncAsync();
}
✔ Prevents sync errors when offline.
| MAUI-CommunityToolkit-8 :👈 | 👉:MAUI-CommunityToolkit-10 |