Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Xamarin

Build Xamarin Apps Faster With BaaS

5.00/5 (2 votes)
26 Aug 2019CPOL13 min read 7.5K   133  
Learn how to quickly build mobile apps using a Xamarin with a cloud hosted mobile backend in minutes.

The Problem

As creators, we are frequently faced with the challenge of creating value by developing apps and products that people want or need to use. The problem though is getting an app to this point typically requires tons of money and time. All the while, there remains uncertainty if users will actually want or use your app. The typical approach to this challenge is more upfront research, market analysis and design meant to validate the work before too many resources are applied to the project. While all the previously mentioned things are necessary, planning can only go so far. Sometimes, you just need to get your app in front of users to truly understand if your idea or solution is valuable. So how can we reconcile the need to have a working app with the costs and risks involved? Answer..... Minimum Viable Product.

What's an MVP?

Minimum Viable Products (MVPs) are intended to showcase the core functionality of an app to users. This is done in order to validate your solution before too much time, money and effort is applied. Because of this, it's important that your MVP be able to stand alone and function without all the bells and whistles. For example: if you want to build a car, an MVP might be building a go cart—this is in contrast to building just the car frame. Remember, you're trying to figure out if people even want to drive (let's assume driving is something new in this make believe scenario). Finishing just one part of your car doesn't help with that question. What you really need is something that a person can ride in, steer, drive and stop. The same could be said for your app! What problem or need are you trying to solve or satisfy? Build only what is necessary to validate your solution—and build it quickly! Then get your users to tell you if your solution needs any changes. Do this right and you will spend the least amount of resources while creating the largest possible value.

The Solution

Image 1

MeshyDB was created to solve this problem by making it simple and fast to get a working app up and running —we're talking minutes or hours not days or weeks. This article is intended to showcase how you could build a mobile MVP in minutes. The specific example we will be covering is a blogging app for Android phones. If after reading this article you think this solution could work for you, we suggest checking out the complete solution via GitHub. Don't forget though, you need a free MeshyDB account to make this project run.

Creating a RESTful Back-end

In this example, MeshyDB will serve as the RESTful back-end, meaning your application data will be stored and accessed in and through MeshyDB. As a result, you will need to make sure you have a MeshyDB account before you can begin.

Grabbing Your Account Name

Once your account is created and verified, you will want to grab your account name. This unique identifier will help the SDK know where to store and access your data. Not sure what your account name is? Simply go to your Account page and grab your account name.

Image 2

Grabbing Your API Key

Now that you have an account, you will need to locate your public key. This value is used to identify your app. You can find your default public key under the Clients page for your Default tenant. Copy this value, you're going to need it later. :)

Image 3

Create a New Project

Now that we have our back-end, let's create our application using Visual Studio 2019 and Xamarin Android SDK. In this example, we will be creating a Xamarin Forms Application If you do not have the option available, you might need to update or install Xamarin (https://visualstudio.microsoft.com/xamarin/).

Image 4

Getting the Android Emulator Setup

We suggest running the project as-is to get your local Android emulator configured. Setting the Android project as your default startup project and running the app will initiate the device setup. This process can take a few minutes.

Attention! Xamarin emulators can perform very slowly on local workstations, we recommend enabling hardware acceleration on your device to see significant performance improvements. See this article by Microsoft for how to enable hardware acceleration to improve emulator performance on your local workstation.

Adding MeshyDB SDK

Once you have your project created, you will need to add the NuGet dependency via Package Manager Console. Simply open the console and type Install-Package MeshyDB.SDKmake sure you run this command for the cross-platform project (not the iOS or Android one). This package will give you a set of libraries used to integrate your app with MeshyDB. While this package is not required, it will definitely make building a C# app much more pleasant.

For more information about the MeshyDB NuGet package, please see NuGet.org (https://www.nuget.org/packages/meshyDb.sdk/).

Establishing a Connection

Because all operations against the MeshyDB API must be performed as an authenticated user, we must first register a user. For the sake of this example, we are going to focus on anonymous users. Think of an anonymous user as a device based user, meaning each device connected to your app will be given a unique identifier. The benefit of this particular user model is its low barrier to entry for users. There is no user registration or account setup, simply create a user behind the scenes using an ID unique to the device and you are off and running.

To accomplish this, we are going to add some code to the App.xaml.cs file generated automatically by Xamarin Forms.

Configuring the Client

MeshyDB is built as a RESTful API, however to make things easier for developers, we've created an SDK that handles all the HTTPS traffic for you. The core of this SDK is the MeshyClient. Before we can begin, we need to create a static property that manages our connection. The Client property requires you enter your account name and client id from above.

App.xaml.cs (view in GitHub)
C#
private static IMeshyClient client;

public static IMeshyClient Client
{
      get
      {
            if (client == null)
            {
                var accountName = "<!-- your account name -->";
                var publicKey = "<!-- your client id -->";

                client = MeshyClient.Initialize(accountName, publicKey);
            }
        
            return client;
      }
} 

Registering the User's Device

First, let's check to see if the device is registered and if not, let's register the device as a user via the RegisterDeviceUser() function. This function uses the DeviceID property to create an anonymous user. We will initiate this process when the application starts up by invoking the InitializeMeshy() function in the App() constructor.

The code below shows how to create and store a unique identifier using Xamarin's Preferences feature. This feature does not persist preferences across app installations, meaning if the user uninstalls and then reinstalls the app, they will get a new id.

App.xaml.cs (view in GitHub)
C#
public static Guid DeviceId
{
    get
    {
        var id = Preferences.Get("device_id", string.Empty);
        
        if (string.IsNullOrWhiteSpace(id))
        {
            id = System.Guid.NewGuid().ToString();

            Preferences.Set("device_id", id);
        }

        return new Guid(id);
    }
}

private void RegisterDeviceUser()
{
    var userExists = Client.CheckUserExist(DeviceId.ToString());

    if (!userExists.Exists)
    {
        Client.RegisterAnonymousUser(DeviceId.ToString());
    }
}

public App()
{
    InitializeComponent();
    InitializeMeshy();
    
    MainPage = new MainPage();
}

private void InitializeMeshy()
{
    RegisterDeviceUser();
}

Authenticating the User's Device

Once the user is registered, we then need to authenticate that user / device using the LoginAnonymously(string id) function. This function performs an anonymous login using the DeviceID property and then returns an IMeshyConnection. We will lazy load this connection when the Connection Property is referenced.

App.xaml.cs (view in GitHub)
C#
private static IMeshyConnection connection;

public static IMeshyConnection Connection
{
    get
    {
        if (connection == null)
        {
            connection = Client.LoginAnonymously(DeviceId.ToString());
        }

        return connection;
    }
}

Defining Your Data

The core of MeshyDB is the Mesh. Think of a Mesh as a data structure you define at runtime; with it you can perform read, write, update and delete operations.

For the sake of this example, we need a model that represents a blog post. The template project we implemented already has a model defined called Item.cs. We need to make a few changes to this model before we can start saving data. First, the model needs to extend MesyDB.SDK.Models.MeshData. Also, we need to add a few more pieces of information relevant to our example. Lastly, we want to store this data in our back-end under a different name, so let's add the [MeshName] decorator and tell our API to call this data "BlogPost".

Remember, this can be whatever you want it to be–there are no requirements outside of implementing MeshyDB.SDK.Models.MeshData.

Item.cs (view in GitHub)
C#
[MeshName("BlogPost")]
public class Item : MeshyDB.SDK.Models.MeshData
{
    public string Text { get; set; }

    public string Description { get; set; }

    public string CreatedById { get; set; }
    
    public DateTimeOffset DateCreated { get; set; }
    
    public string CreatedByName { get; set; }
}

At this point, your project is going to be pretty upset with you. By extending MeshyDB.SDK.MOdels.MeshData, you are making Item.Id a read only property. The problem is the existing project has some mock data setup where Item.ID is set. We need to comment out this assignment to make your project happy again.

MockDataStore.cs (view in GitHub)
C#
public MockDataStore()
{
    items = new List<Item>();

    var mockItems = new List<Item>
    {
        new Item { /*Id = Guid.NewGuid().ToString(),*/ Text = "First item", 
        Description="This is an item description." },
        new Item { /*Id = Guid.NewGuid().ToString(),*/ Text = "Second item", 
        Description="This is an item description." },
        new Item { /*Id = Guid.NewGuid().ToString(),*/ Text = "Third item", 
        Description="This is an item description." },
        new Item { /*Id = Guid.NewGuid().ToString(),*/ Text = "Fourth item", 
        Description="This is an item description." },
        new Item { /*Id = Guid.NewGuid().ToString(),*/ Text = "Fifth item", 
        Description="This is an item description." },
        new Item { /*Id = Guid.NewGuid().ToString(),*/ Text = "Sixth item", 
        Description="This is an item description." }
    };

    foreach (var item in mockItems)
    {
        items.Add(item);
    }
}

Interacting with MeshyDB API (CRUD)

Now that we have our object defined, we need to write the code necessary to interact with the back-end. We can make these necessary changes to the existing ItemsViewModel.cs file.

Adding Records

The constructor already has an event handler that facilitates adding data, we simply need to change how it adds data from writing to a local db to using our MeshyDB API instead. This is done using the CreateAsync<Item>() function given to us by our SDK.

ItemViewModel.cs (view in GitHub)
C#
MessagingCenter.Subscribe<NewItemPage, Item>(this, "AddItem", async (obj, item)=>
{
    var newItem = item as Item;
    newItem.CreatedById = App.Connection.CurrentUser.Id;
    newItem.DateCreated = DateTimeOffset.Now;

    var username = App.Connection.CurrentUser.Username;
    var name = $"{App.Connection.CurrentUser.FirstName} 
               {App.Connection.CurrentUser.LastName}".Trim();

    username = !string.IsNullOrWhiteSpace(name) ? name : username;
    newItem.CreatedByName = username;

    try
    {
        var createResult = await App.Connection.Meshes.CreateAsync<Item>(newItem);

        Items.Insert(0, createResult);
    }
    catch (Exception)
    {
        throw;
    }
});

Deleting Records

Deleting records can be done in a similar way. A new message subscription needs to be added to the constructor that listens for delete actions and calls the DeleteAsync() function.

ItemViewModel.cs (view in GitHub)
C#
MessagingCenter.Subscribe<ItemDetailPage, Item>(this, "DeleteItem", async (obj, item) =>
{
    try
    {
        await App.Connection.Meshes.DeleteAsync<Item>(item.Id);

        Items.Remove(item);
    }
    catch (Exception)
    {
        throw;
    }
});

Editing Records

A third message subscription needs to be created that detects save operations and pushes the new model to the MeshyDB API. As with the create and delete operations, the SDK has an UpdateAsync function for updating a specific record by id.

ItemViewModel.cs (view in GitHub)
C#
MessagingCenter.Subscribe<EditItemPage, Item>(this, "UpdateItem", async (obj, item) =>
{
    var username = App.Connection.CurrentUser.Username;
    var name = $"{App.Connection.CurrentUser.FirstName} 
               {App.Connection.CurrentUser.LastName}".Trim();

    username = !string.IsNullOrWhiteSpace(name) ? name : username;
    item.CreatedByName = username;
    
    try
    {
        var updatedResult = await App.Connection.Meshes.UpdateAsync<Item>(item.Id, item);
        var post = Items.FirstOrDefault(x => x.Id == updatedResult.Id);
        
        post = updatedResult;
    }
    catch (Exception)
    {
        throw;
    }
});

Loading Data

In the ItemsViewModel.cs, we need to make sure we have the proper commands defined to handle loading data and scrolling for more. The quick start project gives you a pattern for defining LoadItemsCommand in the constructor. We need to copy that same pattern for the LoadMoreItemsCommand.

ItemViewModel.cs (view in GitHub)
C#
public ObservableCollection<Item>

Items { get; set; }

public Command LoadItemsCommand { get; set; }
public Command LoadMoreItemsCommand { get; set; }

public ItemsViewModel()
{
    Title = "Recent Posts";

    Items = new ObservableCollection<Item>();

    LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());

    LoadMoreItemsCommand = new Command(async () => await ExecuteLoadItemsCommand(true));
    
    ...
}

The existing ExecuteLoadItemsCommand() function needs to be modified to load from the MeshyDB API rather than the local db. This can be accomplished with the SearchAsync() function along with some basic filtering and sorting expressions. The example below demonstrates how to filter and sort data by using lambda expressions and our strongly typed model.

In this example, we want users to be able to scroll down to load more data. To support this feature, some additional logic has been added to the ExecuteLoadItemsCommand() function. Calling this function with the more parameter as true will load the next 10 records older than the last one in the current list.

ItemViewModel.cs (view in GitHub)
C#
async Task ExecuteLoadItemsCommand(bool more = false)
{
    if (IsBusy)
        return;

    IsBusy = true;

    try
    {
        Expression<Func<Item, bool>> filter = (Item x) => true;

        if (more)
        {
            var lastItem = Items[Items.Count - 1];
    
            filter = (Item x) => x.DateCreated < lastItem.DateCreated;
        }
        else
        {
            Items.Clear();
        }

        var sort = SortDefinition<Item>.SortByDescending(x => x.DateCreated);

        var items = await App.Connection.Meshes.SearchAsync(filter, sort, 1, 10);

        if (items.TotalRecords > 0)
        {
            foreach (var item in items.Results)
            {
                Items.Add(item);
            }
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex);
    }
    finally
    {
        IsBusy = false;
    }
}

The User Interface

Now let's create a place to enter data. Fortunately, the template project does most of this work for us; we simply need to make a few modifications relevant to this example.

Adding a Blog Post

The template creates an xaml file that defines the UI for new item creation NewItemPage.xaml. We just need to make a few changes to the field labels and make the description support multi-line input.

NewItemPage.xaml (view in GitHub)
XML
<ContentPage.Content>
    <StackLayout Spacing="20" Padding="15">
        <Label Text="Title" FontSize="Medium" />
        <Entry Text="{Binding Item.Text}" FontSize="Small" />
        <Label Text="Post" FontSize="Medium" />
        <Editor Text="{Binding Item.Description}" FontSize="Small" 
         Margin="0" AutoSize="TextChanges" />
    </StackLayout>
</ContentPage.Content>

Listing Out Blogs Posts

The template code for listing out records is also pretty close to what we need, however a label for who created the blog needs to be added along with some slight modifications to text formatting.

ItemsPage.xaml (view in GitHub)
XML
<DataTemplate>
    <ViewCell>
        <StackLayout Padding="10">
            <Label Text="{Binding Text}"
                LineBreakMode="NoWrap"
                Style="{DynamicResource ListItemTextStyle}"
                FontSize="24" />
            <Label Text="{Binding CreatedByName, StringFormat='by {0:N}'}" FontSize="8" />
                <Label Text="{Binding Description}"
                LineBreakMode="TailTruncation"
                MaxLines="3"
                Style="{DynamicResource ListItemDetailTextStyle}"
                FontSize="13" />
        </StackLayout>
    </ViewCell>
</DataTemplate>

Scrolling For More

In order to support scrolling for more data, an event handler needs to be wired-up that detects users scrolling to the bottom of the list and loads more data. This can be accomplished via the ItemsListView.ItemAppearing event. We will detect when the last item is viewed and trigger the LoadMoreItmesCommand function defined in the ItemsViewModel.cs to load more data.

ItemsPage.xaml.cs (view in GitHub)
C#
object lastItem;

public ItemsPage()
{
    InitializeComponent();

    BindingContext = viewModel = new ItemsViewModel();

    ItemsListView.ItemAppearing += (sender, e) =>
    {
        if (viewModel.IsBusy || viewModel.Items.Count == 0)
            return;

        var item = e.Item as Item;

        if (e.Item != lastItem && item.Id == viewModel.Items[viewModel.Items.Count - 1].Id)
        {
        lastItem = e.Item;

        viewModel.LoadMoreItemsCommand.Execute(null);
        }
    };
}

Viewing Blog Post Details

The existing UI for viewing details found in ItemDetailPage.xaml already has most of what we need for viewing details. After a couple small tweaks to the UI to show the author and date created, we're all good.

ItemDetailPage.xaml (view in GitHub)
XML
<StackLayout Spacing="20" Padding="15">
    <Label Text="{Binding Item.Text}" FontSize="Large" />
    <Label Text="{Binding Item.CreatedByName, StringFormat='by {0:N}'}" FontSize="8" />
    <Label Text="Post:" FontSize="Medium" />
    <Label Text="{Binding Item.Description}" FontSize="Small" />
</StackLayout>

Managing Your Blog Posts

We want to give users the ability to edit or delete their posts, but we only want users to mange their own posts. A new function called InitializeToolbarItems() will be created that checks the CreatedById against the App.Connection.CurrentUser.Id. If the two values match, we know the user viewing the post is the one who created it.

Also, a message subscription needs to be added for when the model you are viewing is updated. To do this, you will need to add a new function called InitializeMessagingSubscription. This function's job is to listen for updates in the app and make sure the model being viewed by the user stays up-to-date.

ItemDetailPage.xaml.cs (view in GitHub)
C#
ItemDetailViewModel viewModel;

public ItemDetailPage(ItemDetailViewModel viewModel)
{
    InitializeComponent();
    
    BindingContext = this.viewModel = viewModel;
    
    InitializeToolbarItems();
    InitializeMessagingSubscription();
}

private void InitializeMessagingSubscription()
{
    MessagingCenter.Subscribe<EditItemPage, Item>(this, "UpdateItem", (obj, item) =>
    {
        if (item.Id == this.viewModel.Item.Id)
        {
            BindingContext = viewModel = new ItemDetailViewModel(item);
        }
    });
}

private void InitializeToolbarItems()
{
    if (viewModel.Item.CreatedById == App.Connection.CurrentUser.Id)
    {
        ToolbarItems.Add(new ToolbarItem
                        ("Edit", null, new Action(async () => await EditItem())));
        ToolbarItems.Add(new ToolbarItem
                        ("Delete", null, new Action(async () => await DeleteItem())));
    }
} 

Deleting Your Blog Posts

Deleting the post is as simple as sending a message with the model we want to remove. Remember, the ItemsViewModel has already been configured to listen to this message and perform the delete.

ItemDetailPage.xaml.cs (view in GitHub)
C#
private async Task DeleteItem()
{
    var confirm = await DisplayAlert("Delete This Item?", 
                  "Are you sure you want to delete this item?", "YES", "NO");
    
    if (confirm)
    {
        MessagingCenter.Send(this, "DeleteItem", viewModel.Item);
        await Navigation.PopAsync();
    }
} 

Editing Your Blogs Posts

Unlike deletion, editing requires a bit more. We need to create a new function that opens a new interface for editing passing in the view model.

ItemDetailPage.xaml.cs (view in GitHub)
C#
private async Task EditItem()
{
    var editPage = new NavigationPage(new EditItemPage(viewModel));

    await Navigation.PushModalAsync(editPage);
}

Now that we have our edit navigation wired up to open a new page, we need to create that new edit page. This is where users will perform the updates.

EditItemPage.xaml (view in GitHub)
XML
<ContentPage.ToolbarItems>
    <ToolbarItem Text="Cancel" Clicked="Cancel_Clicked" />
    <ToolbarItem Text="Update" Clicked="Update_Clicked" />
</ContentPage.ToolbarItems>

<ContentPage.Content>
    <StackLayout Spacing="20" Padding="15">
        <Label Text="Text" FontSize="Medium" />
        <Entry Text="{Binding Item.Text}" FontSize="Small" />
        <Label Text="Description" FontSize="Medium" />

        <Editor Text="{Binding Item.Description}" FontSize="Small" 
                Margin="0" AutoSize="TextChanges" />
    </StackLayout>
</ContentPage.Content>

The corresponding code behind file needs to be created, however since we already created the message handlers in ItemsViewModel.cs, the only thing we need to do here is emit the message of "UpdateItem" and pass through the modified object.

EditItemPage.xaml.cs (view in GitHub)
C#
[DesignTimeVisible(false)]
public partial class EditItemPage : ContentPage
{
    ItemDetailViewModel viewModel;

    public EditItemPage()
    {
        InitializeComponent();

        var item = new Item();

        viewModel = new ItemDetailViewModel(item);

        BindingContext = viewModel;
    }

    public EditItemPage(ItemDetailViewModel item)
    {
        InitializeComponent();

        BindingContext = viewModel = item;
    }

    async void Update_Clicked(object sender, EventArgs e)
    {
        MessagingCenter.Send(this, "UpdateItem", viewModel.Item);
        
        await Navigation.PopModalAsync();
     }

     async void Cancel_Clicked(object sender, EventArgs e)
     {
        await Navigation.PopModalAsync();
     }
}

Where's All My Data?

Now that we have everything in place, let's run the app and create some blog posts. If we've done everything correctly, you should start seeing data appear in your MeshyDB back-end. To check, simply login to your MeshyDB administrative account and goto Meshes under the Default tenant. Find your mesh called BlogPost and look to see how many documents are in the collection. There should be a document for every post you created.

Image 5

If you want to dig into the records themselves, simply click on the Mesh to open the details page and click Search. This will open up a list of all records in the collection. You can view more data about the records by clicking on the record itself.

Image 6

Troubleshooting

First things first, let's check the logs to see if your calls are succeeding. To do this, let's go to the Dashboard page under the Default tenant. Here, you will see your most recent error logs. Clicking the errors will show you the request details and the status code. Below are a list of error status codes and the reasoning behind them:

  • 400: Bad request
    • Mesh name is invalid and must be alpha characters only.
    • Mesh property cannot begin with ‘$’ or contain ‘.’.
  • 401: Unauthorized
    • User is not authorized to make call.
    • Your client_id is incorrect.
  • 429: Too many requests
    • You have either hit your API or Database limit. Please review your account.

If you have no logs, try checking to see that your account name is correct. As long as you have the right account name, you will see error logs in your system.

And That's It!

Believe it or not, you now have an application where users can publish and manage content while also viewing content created by other users. Better yet, your application data is centralized, secure and highly available. Not to mention, you have spent nothing building your app, and have invested only minutes of your time. Congrats!

If you haven't already, we suggest checking out the complete sample application on our GitHub. Enjoy! :)

History

  • 27th August, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)