MAUI-CommunityToolkit-8 :👈 👉:MAUI-CommunityToolkit-10

Hour 9 – REST APIs, HttpClient, JSON & Offline Sync

📘 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:

  • âž¡ REST API calls
  • âž¡ HttpClient (best practices)
  • âž¡ JSON serialization/deserialization
  • âž¡ Offline sync pattern (SQLite + API)

✨ 1. Using HttpClient in MAUI

In .NET MAUI, HttpClient should be created using Dependency Injection.

🔧 1.1 Register HttpClient in MauiProgram

builder.Services.AddHttpClient();
builder.Services.AddTransient<ApiService>();

🧩 1.2 ApiService using HttpClient

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


✨ 2. JSON Models

📄 2.1 Define the Model

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.


✨ 3. ViewModel Calling the API

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


✨ 4. Displaying API Results in XAML

<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


✨ 5. POST, PUT & DELETE Requests

📤 5.1 POST (Create)

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());
}

📥 5.2 PUT (Update)

public async Task UpdateTodoAsync(TodoItem item)
{
    var json = JsonSerializer.Serialize(item);
    await http.PutAsync($"todos/{item.Id}",
        new StringContent(json, Encoding.UTF8, "application/json"));
}

🗑 5.3 DELETE

public Task DeleteTodoAsync(int id)
{
    return http.DeleteAsync($"todos/{id}");
}

✔ Complete API CRUD support


✨ 6. Offline Sync Pattern

Offline-first apps store data locally in SQLite and sync with API when online.

  • ✔ SQLite stores cached items
  • ✔ API updates data when online
  • ✔ Merge logic keeps both sides in sync

🗂 6.1 Local Database Table

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; }
}

🔄 6.2 Sync Service

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


✨ 7. Connectivity Check

Use MAUI’s Connectivity API to detect network state:

if (Connectivity.NetworkAccess == NetworkAccess.Internet)
{
    await syncService.SyncAsync();
}

✔ Prevents sync errors when offline.


📘 Hour 9 Summary

  • ✔ HttpClient should be DI-driven
  • ✔ JSON serialization via System.Text.Json
  • ✔ CRUD operations over REST APIs
  • ✔ Offline Sync pattern for robust apps
  • ✔ Use SQLite + API + Connectivity for reliability
Back to Index
MAUI-CommunityToolkit-8 :👈 👉:MAUI-CommunityToolkit-10
*