Creating a Xamarin Forms client app - stefffdev/NubeSync GitHub Wiki

This documentation assumes that you have a working Xamarin Forms app. We will add the NubeSync client framework and a SQLite storage for the offline cache.

You can download the complete source code for this sample here: https://github.com/stefffdev/NubeSync/tree/master/samples/Clients/Xamarin.Forms/NubeSync.Mobile

Add the packages

Add the NubeSync.Client.SQLiteStore nuget package to all projects, including the platform projects.

Create the DTO

Similar to the server project the DTO (Data Transfer Object) contains the structure of the records to be synced with the server.

  1. Add a file TodoItem.cs to the project
  2. Add the following using:
using Nube.Client.Data;
  1. Add the following content to the class:
public class TodoItem : NubeTable
{
    public string Name { get; set; }

    public bool IsChecked { get; set; }
}

Let's add some UI

Change your MainPage.xaml to look like this:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="NubeSync.Mobile.MainPage"
             Appearing="ContentPage_Appearing">

    <StackLayout>
        <Button Text="Add Item" Clicked="Add_Button_Clicked" />
        <Button Text="Sync" Clicked="Sync_Button_Clicked" />
        <CollectionView x:Name="collectionView">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        
                        <Entry Text="{Binding Name}" Unfocused="Entry_Unfocused" />
                        <CheckBox Grid.Column="1" IsChecked="{Binding IsChecked}" CheckedChanged="CheckBox_CheckedChanged" />
                        <Button Grid.Column="2" Text="Delete" Clicked="Delete_Button_Clicked" />
                    </Grid>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </StackLayout>

</ContentPage>

Wire up in code behind

Make your MainPage.xaml.cs look like this:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using NubeSync.Client;
using NubeSync.Client.SQLiteStore;
using SQLite;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace NubeSync.Mobile
{
    [DesignTimeVisible(false)]
    public partial class MainPage : ContentPage
    {
        private NubeSQLiteDataStore _dataStore;
        private NubeClient _nubeClient;

        public MainPage()
        {
            InitializeComponent();

            var databasePath = Path.Combine(FileSystem.AppDataDirectory, "offline.db");
            _dataStore = new NubeSQLiteDataStore(databasePath);

            // this is needed when the server is running local for debugging
            var clientHandler = new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; }
            };
            var httpClient = new HttpClient(clientHandler);

            var server = "https://localhost:5001/";

            if (DeviceInfo.Platform == DevicePlatform.Android)
            {
                server = "https://10.0.2.2:5001/";
            }

            _nubeClient = new NubeClient(_dataStore, server, httpClient: httpClient);
        }

        private async void ContentPage_Appearing(object sender, EventArgs e)
        {
            await _dataStore.InitializeAsync();
            await _nubeClient.AddTableAsync<TodoItem>("todoitems");
            
            await RefreshItemsAsync();
        }

        private async Task RefreshItemsAsync()
        {
            var list = (await _nubeClient.GetAllAsync<TodoItem>()).ToList().OrderBy(i => i.CreatedAt);
            collectionView.ItemsSource = list;
        }
        
        private async Task SyncAsync()
        {
            await _nubeClient.PushChangesAsync();
            await _nubeClient.PullTableAsync<TodoItem>();

            await RefreshItemsAsync();
        }

        private async void Add_Button_Clicked(object sender, EventArgs e)
        {
            var item = new TodoItem() { Name = "New Item" };
            await _nubeClient.SaveAsync(item);
            await SyncAsync();
        }

        private async void Delete_Button_Clicked(object sender, EventArgs e)
        {
            if (sender is Button button && button.BindingContext is TodoItem item)
            {
                await _nubeClient.DeleteAsync(item);
                await _nubeClient.PushChangesAsync();
                await RefreshItemsAsync();
            }
        }

        private async void Sync_Button_Clicked(object sender, EventArgs e)
        {
            await SyncAsync();
        }

        private async void Entry_Unfocused(object sender, FocusEventArgs e)
        {
            if (sender is Entry entry && entry.BindingContext is TodoItem item)
            {
                item.Name = entry.Text;
                await _nubeClient.SaveAsync(item);
                await _nubeClient.PushChangesAsync();
            }
        }

        private async void CheckBox_CheckedChanged(object sender, CheckedChangedEventArgs e)
        {
            if (sender is CheckBox checkBox && checkBox.BindingContext is TodoItem item)
            {
                item.IsChecked = checkBox.IsChecked;
                await _nubeClient.SaveAsync(item);
                await _nubeClient.PushChangesAsync();
            }
        }
    }
}

Done!

Run both your server and client app and add some records. On sync those records should be persisted to the server database.

⚠️ **GitHub.com Fallback** ⚠️