Book Image

Xamarin: Cross-Platform Mobile Application Development

By : George Taskos, Jonathan Peppers, Can Bilgin
Book Image

Xamarin: Cross-Platform Mobile Application Development

By: George Taskos, Jonathan Peppers, Can Bilgin

Overview of this book

Developing a mobile application for just one platform is becoming a thing of the past. Companies expect their apps to be supported on iOS, Android, and Windows Phone, while leveraging the best native features on all three platforms. The primary goal of this course is to equip you with the knowledge to successfully analyze, develop, and manage Xamarin cross-platform projects using the most efficient, robust, and scalable implementation patterns. Module 1 is a step-by-step guide to building real-world applications for iOS and Android. The module walks you through building a chat application, complete with a backend web service and native features such as GPS location, camera, and push notifications. Additionally, you'll learn how to use external libraries with Xamarin and Xamarin.Forms. Module 2 provide you recipes on how to create an architecture that will be maintainable, extendable, use Xamarin.Forms plugins to boost productivity. We start with a simple creation of a Xamarin.Forms solution, customize the style and behavior of views for each platform. Further on, we demonstrate the power of architecting a cross-platform solution. Next, you will utilize and access hardware features that vary from platform to platform with cross-platform techniques. In the last and the final Module, you will learn about essential tools to leverage the pattern and advanced implementation strategies. We'll show you the toolset for application lifecycle management to help you prepare the development pipeline to manage and see cross-platform projects through to public or private release. After the completion of this course, you will learn a path that will get you up and running with developing cross-platform mobile applications and help you become the go-to person when it comes to Xamarin. This Learning Path combines some of the best that Packt has to offer in one complete, curated package. It includes content from the following Packt products: ?Xamarin Cross-platform Application Development - Second Edition by Jonathan Peppers ?Xamarin Cross-Platform Development Cookbook by George Taskos ?Mastering Cross-Platform Development with Xamarin by Can Bilgin
Table of Contents (6 chapters)

Chapter 8. Web Services with Push Notifications

Modern mobile applications are defined by their network connectivity. A mobile app that does not interact with a web server is both a rare find and not as interactive or social as it would otherwise be. In this module, we'll use the Windows Azure cloud platform to implement a server-side backend for our XamChat application. We'll use a feature called Azure Mobile Services, which is an excellent fit for our application and has the benefit of built-in push notifications. Once we are done with this chapter, our XamChat sample application will be much closer to being a real application and will allow its users to interact with one another.

In this chapter, we will cover the following topics:

  • The services offered by Windows Azure
  • Setting up your Azure account
  • Azure Mobile Services as a backend for XamChat
  • Creating tables and scripts
  • Implementing a real web service for XamChat
  • Using the Apple Push Notification service
  • Sending notifications with Google Cloud Messaging

Learning Windows Azure

Windows Azure is an excellent cloud platform released by Microsoft in 2010. Azure provides both Infrastructure as a Service (IaaS) and Platform as a Service (PaaS) for building modern web applications and services. This means that it provides you with access to direct virtual machines within which you can deploy any operating system or software of your choice. This is known as IaaS. Azure also provides multiple platforms for building applications such as Azure Websites or SQL Azure. These platforms are known as PaaS since you deploy your software at a high level and do not have to deal directly with virtual machines or manage software upgrades.

Let's go through the following more common services provided by Windows Azure:

  • Virtual machines: Azure provides you with access to virtual machines of all sizes. You can install practically any operating system of your choice. There are many premade distributions to choose from within Azure's gallery.
  • Websites: You can deploy any type of website that will run in Microsoft IIS from ASP.NET sites to PHP or Node.js.
  • SQL Azure: This is a cloud-based version of Microsoft SQL Server, which is a full-featured Relational Database Management System (RDMS) for storing data.
  • Mobile Services: This is a simple platform for building web services for mobile apps. It uses SQL Azure for backend storage and a simple JavaScript scripting system based on Node.js for adding business logic. In the latest version of Azure Mobile Services, you can also use C# and the ASP.NET Web API for developing server-side code.
  • Storage: Azure provides Blob storage, a method for storing binary files, and Table storage, which is a NoSQL solution for persisting data.
  • Service bus: This is a cloud-based solution for creating queues to facilitate communication between other cloud services. It also includes notification hubs as a simple way of providing push notifications to mobile apps.
  • Worker roles: A simple way to run a custom process in the cloud can be a plain Windows executable or a .NET worker role project.
  • Media services: A mechanism for providing streaming audio or video to nearly any device. It handles both encoding and delivery and can scale to support a large volume of users.
  • HDInsight: A version of Apache Hadoop running in Windows Azure for managing extremely large databases, also called big data.
  • Cloud services: This is a conglomeration of other services. Cloud services allow you to bundle multiple services together and create staging and production environments. It is a great tool for deployment; you can deploy changes to staging and swap staging and production to preserve uptime for your users.

Apart from these services, there are many more, and new ones are added pretty regularly. We will use Azure Mobile Services, which leverages SQL Azure, to build our web service for XamChat. You can visit http://windowsazure.com for a full rundown of pricing and services offered.

In this module, we chose to demonstrate a solution using Windows Azure as a web service backend for XamChat, since it is very easy to use with Xamarin applications because of the fantastic library found in the Xamarin Component Store. However, there are many more choices out there besides Azure that you might want to look at. Using Xamarin's development platform does not limit the types of web services your applications can interact with.

Here are a few more common ones:

  • Parse: This service provides a product similar to that of Azure Mobile Services, complete with data storage and push notifications. This is a popular service among many mobile developers, even those not using Xamarin. You can get more information at http://parse.com.
  • Urban Airship: This service provides push notifications for mobile apps across multiple platforms. You can get more information at http://urbanairship.com.
  • Amazon Web Services: This service is a complete cloud solution that is equivalent to Windows Azure. It has everything that you need to deploy applications in the cloud with total virtual machine support. The main difference is that Azure is very C# focused and built for .NET developers. There are also not as many PaaS options on Amazon. You can get more information at http://aws.amazon.com.

Additionally, you can develop your own web services with on-premises web servers or inexpensive hosting services using the languages and technologies of your choice.

Setting up your Azure account

To start developing with Windows Azure, you can subscribe to a free one-month trial along with free credits worth $200. You can get even more perks if you have an MSDN subscription. To go along with this, many of Azure's services have free tiers that give you lower performance versions. So if your trial expires, you can continue your development at little or no cost, depending on the services you are using.

Let's begin by navigating to http://windowsazure.com/en-us/pricing/free-trial and performing the following steps:

  1. Click on the Try it now link.
  2. Sign in with a Windows Live ID.
  3. For security purposes, verify your account via phone or text message.
  4. Enter the payment information. This is only used if you exceed your spending limits. You won't accidentally spend beyond budget by developing your app—it is fairly difficult to accidentally spend money until real users are interacting with your services.
  5. Check I agree to the policies and click on Sign Up.
  6. Review the final setting and click on Submit.
  7. If all the required information is entered correctly, you will now finally have access to the Azure subscription page. Your subscription page will look similar to the following screenshot:
Setting up your Azure account

You can click on the Portal link in the top-right corner of the page to access your account. In future, you can manage your Azure services at: http://manage.windowsazure.com.

Complete the Windows Azure tour to get a quick rundown of the management portal's features. You can then access the main menu to create new Azure services, virtual machines, and so on. The main menu looks similar to the following screenshot:

Setting up your Azure account

This concludes the sign-up process for Windows Azure. It is pretty simple compared to the Apple and Google Play developer programs. Feel free to play around, but don't be too worried about spending money. Azure has free versions of most services and also delivers a good amount of bandwidth for free. You can get more information on pricing at http://windowsazure.com/en-us/pricing/overview.

Note that there are a lot of misconceptions about Windows Azure being expensive. You can do all of your development for an application on the free tier without spending a dime. When putting applications into production, you can easily scale up or down on the number of VM instances to keep your costs under control. In general, you will not be spending much money if you do not have a lot of users. Likewise, you should be earning plenty of revenue if you happen to have lots of users.

Exploring Azure Mobile Services

For the server side of XamChat, we'll use Azure Mobile Services to provide backend storage to the application. A mobile service is a neat solution to accelerate development for mobile applications that provide data storage and a REST-based API, which is a standards-based way of communicating with a web service over HTTP. Azure Mobile Services also includes a .NET client library for interacting with the service from C#.

A few nice features of Azure Mobile Services are as follows:

  • Storage of data in the cloud with SQL Azure or other Azure data services such as Blob or Table storage
  • Easy authentication with Windows Live ID, Facebook, Google, and Twitter
  • Push notifications with iOS, Android, and Windows devices
  • Code the server side with JavaScript and Node.js or C#
  • An easy-to-use .NET library for client-side development
  • Scale Azure Mobile Services to accommodate high volumes of data

You can see why using Azure is a good choice for simple mobile applications. The benefits of accelerated development and the many features it provides are a great fit for our XamChat sample application.

Let's navigate to your account at http://manage.windowsazure.com and perform the following steps to create a mobile service:

  1. Click on the plus button in the bottom-left corner of the window.
  2. Navigate to Compute | Mobile Service | Create through the menu.
  3. Enter a domain URL of your choice such as yourname-xamchat.
  4. We use the free database option for now.
  5. Select a data center near your location in the Region dropdown.
  6. For the Backend dropdown, leave the selection on JavaScript for this module. It will be simpler to set up the backend since we are focusing more on the client side. Feel free to use C# as an alternative, but keep in mind the examples in this module will be written in JavaScript.
  7. Now, click on Next.
  8. Use the default database name and choose New SQL database server.
  9. Enter a login name and password for the SQL server, and keep this information in a safe place.
  10. Make sure the region is the same as that of your mobile service to ensure good performance between your mobile service and its database.
  11. Review your final settings and hit the Finish button.

The management portal will display the progress, and it will take several seconds to create your mobile service and SQL Server instances. Remember that Azure is creating and starting new virtual machines for you under the hood, so it is really doing a decent amount of work to accommodate your request.

When completed, your account will have one Mobile Service and one SQL database in addition to the Default Directory that is included in all the accounts, as shown in the following screenshot:

Exploring Azure Mobile Services

If you take a look at the Scale tab for your mobile service, you'll notice that it is running under the Free tier by default. This is a great place for development. At the time of writing this module, it accommodates 500 devices. When deploying your applications to production, you might consider the Basic or Standard tiers, which also give you the option to add multiple instances.

Creating tables and scripts

The first step to implement something in Azure Mobile Services is to create a new table. By default, Azure Mobile Services uses a feature called dynamic schemas with its SQL database. When you insert a row from the client, new columns are dynamically added to the table. This prevents you from having to create the database schema manually and is a neat code-first approach to develop your backend database. You can always connect to the SQL database manually to fine tune things or make manual changes to the schema.

Return to the management portal, select your mobile services instance, and perform the following steps:

  1. Click on the Data tab.
  2. Click on the Create button found at the bottom center of the page.
  3. Enter User as the table name.
  4. Leave everything else at its default value and click on the Save button.
  5. Repeat this process to create three more tables named Friend, Message, and Conversation.

Now that we have our four tables, we need to create a script to facilitate the login process for the users of our app. Azure Mobile Services allows you to add custom logic to your tables by creating scripts that run in Node.js, a framework for developing web services with JavaScript. You can override what happens to each table during the insert, read, update, or delete operations. In addition to this, you can also create scripts that are completely customized if you need other functionalities.

Click on the User table and then click on the Script tab. Make sure you are viewing the insert operation. By default, your script will be very simple, as shown in the following snippet:

function insert(item, user, request) {

  request.execute();

}

Scripts in Azure Mobile Services have three parameters, which are stated as follows:

  • item: This parameter is the object that the client sends to the service. In our case, it will be the User object we created in the previous chapters.
  • user: This parameter includes information about the authenticated user. We won't be using this in our examples.
  • request: This parameter is an object used to run the table operation and send a response back to the client. Calling execute will complete the operation and return a successful response to the client.

We need to modify the preceding script to only insert a new user when that user does not already exist. If the user does exist, we need to make sure that the password matches the username. Let's make a few changes to the script, as shown in the following lines of code:

function insert(item, user, request) { 
  var users = tables.getTable('user');
  users.where({ username : item.Username }).read({
    success: function(results) {
      if (results.length == 0) {
        //This is a new user
        request.execute();
      }
      else {
        var user = results[0];
        if (item.Password == user.Password) {
          request.respond(statusCodes.OK, user);
        }
        else {
        request.respond(statusCodes.UNAUTHORIZED, "Incorrect username or password");
        }
      }
    }
  });
}

Let's summarize what we did in the preceding JavaScript:

  1. First, we grabbed the user table. Note that you can reference the name of the table using lowercase.
  2. Next, we ran a query to pull out any existing users with the where function. We used item.Username since this matches our User object in C#. Notice how this method is similar to Linq in C#.
  3. If there are no results, we let the request execute normally.
  4. Otherwise, we compare the passwords and return statusCodes.OK if they match.
  5. If the passwords do not match, we return statusCodes.UNAUTHORIZED. This will cause the client to receive an error.
  6. For a complete list of available functions and methods, make sure you check out the server script reference on MSDN at http://tinyurl.com/AzureMobileServices.

From here, just make sure that you click on the Save button to apply your changes. Azure Mobile Services also has the option of providing source control for your scripts via Git. Feel free to take advantage of this feature if you want to make changes to the script in your favorite editor locally instead of the website editor.

After this, we need to create one more script. The way XamChat was implemented earlier in the module allows, users to add friends by entering their friends' usernames. So, in order to insert into the Friend table, we will need to modify the insert script to look up users by their usernames.

Let's modify the insert script for the Friends table as follows:

function insert(item, user, request) { 
  var users = tables.getTable('user');
  users.where({ username : item.Username }).read({
    success: function(results) {
      if (results.length === 0) {
        //Could not find the user
        request.respond(statusCodes.NOT_FOUND, "User not found");
      }
      else {
        var existingUser = results[0];
        item.UserId = existingUser.id;
        request.execute();
      }
    }
  });
}

This is pretty similar to what we did before; we ran a simple query to load the user table based on the Username value. We merely have to set the UserId value on the new friend table prior to the execution of the request.

Adding a backend to XamChat

With our server-side changes complete, the next step is to implement our new service in our XamChat iOS and Android applications. Luckily, as we used an interface named IWebService, all we need to do is implement this interface to get it working in our application.

Now we can start setting up our service in our iOS application by performing the following steps:

  1. Open the XamChat.Core project that we created in Chapter 4, XamChat – a Cross-platform App.
  2. Create an Azure folder within the project.
  3. Create a new class named AzureWebService.cs.
  4. Create the public class and implement IWebService.
  5. Right-click on IWebService in your code and navigate to Refactor | Implement Interface.
  6. A line will appear; press Enter to insert the method stubs.

When this setup is complete, your class will look something similar to the following:

public class AzureWebService : IWebService
{
  #region IWebService implementation

  public Task<User> Login(string username, string password)
  {
    throw new NotImplementedException();
  }

  // -- More methods here -- 

  #endregion
}

Adding the Azure Mobile Services NuGet package

To make development with Azure Mobile Services much easier, we need to add a reference to the .NET client library. To do this, we will use NuGet to add the library:

  1. Right-click on the XamChat.Core project and navigate to Add | Add Packages.
  2. Search for Azure Mobile Services using the search box.
  3. Select the Azure Mobile Services package, which at the time of writing this module was version 1.2.5.
  4. Click on Add Package.
  5. Repeat this process for the XamChat.iOS and XamChat.Android projects. There is some platform-specific setup for each platform.

Tip

You can also get the Azure Mobile Services library from the Xamarin Component Store if you like. It is very similar to using NuGet.

This will download the library and automatically add references to it in your projects. The NuGet package manager might complain of warnings, which can be ignored. NuGet was originally developed for Visual Studio on Windows, so any packages that contain PowerShell scripts or prompt for a license agreement might give you a warning.

Now, let's modify our AzureWebService.cs file. Add using Microsoft.WindowsAzure.MobileServices to the top of the file, and then make the following changes:

public class AzureWebService : IWebService
{
  MobileServiceClient client = new MobileServiceClient("https://your-service-name.azure-mobile.net/", "your-application-key");

  // -- Existing code here --
}

Make sure you fill in your mobile service name and application key. You can find your key on the Dashboard tab of the Azure Management Portal under the Manage Keys section.

Now let's implement our first method, Login, in the following manner:

public async Task<User> Login(string username, string password)
{
  var user = new User 
  {
    Username = username, 
    Password = password 
  };
  await client.GetTable<User>().InsertAsync(user);
  return user;
}

This is fairly straightforward, because of how nice this library is to use. The GetTable<T> method knows how to use a table named User based on the C# class name. Upon the first call, the dynamic schema feature will create two new columns named Username and Password based on the C# properties of our class. Note that the InsertAsync method will also fill in the user's Id property for later use in our application since we will need the Id for future calls to the mobile service.

Next, open the AppDelegate.cs file to set up our new service and add the following code:

//Replace this line
ServiceContainer.Register<IWebService>(() => new FakeWebService());

//With this line
ServiceContainer.Register<IWebService>(() => new AzureWebService());

Additionally, you will need to add some platform-specific setup for Azure Mobile Services. Add using Microsoft.WindowsAzure.MobileServices to the top of the file and add the following line of code to the bottom of FinishedLaunching in your AppDelegate.cs file:

CurrentPlatform.Init();

Now, if you compile and run your application after you log in, your app should successfully call Azure Mobile Services and insert a new user. Navigate to the Data tab of your Azure Mobile Service in the Azure Management Portal, and select the User table. You will see the user you just inserted, as shown in the following screenshot:

Adding the Azure Mobile Services NuGet package

Note

It is generally a bad idea to store passwords in plain text in your database. A simple approach to make things a bit more secure would be to store them as an MD5 hash. You should be able to make this change in the custom JavaScript that we are using to insert the password on the User table. A complete guide to securing Windows Azure applications can be found at http://msdn.microsoft.com/en-us/library/windowsazure/hh696898.aspx.

Next, let's make a new class named Friend.cs. Add it to the Azure folder that is next to the other class specific to Azure as follows:

public class Friend
{
  public string Id { get; set; }
  public string MyId { get; set; }
  public string UserId { get; set; }
  public string Username { get; set; }
}

We'll use this class to store the friends information about each user. Note that we also have an Id property, and all the classes saved in Azure Mobile Services should have a string property named Id. This will be the table's primary key in the SQL database.

Next, let's modify the Message and Conversation classes to prepare for push notifications down the road. Add a new property to the Message class as follows:

public string ToId { get; set; }

Then, add the following new property to Conversation.cs:

public string MyId { get; set; }

Here, we'll need to insert or seed some test data for our application to function correctly. The easiest way to insert data would be from C#, so let's implement the following simple method on our service to do so:

public async Task LoadData()
{
  var users = client.GetTable<User>();
  var friends = client.GetTable<Friend>();

  var me = new User
  {
    Username = "jonathanpeppers",
    Password = "password"
  };
  var friend = new User
  {
    Username = "chucknorris",
    Password = "password"
  };
  await users.InsertAsync(me);
  await users.InsertAsync(friend);
  await friends.InsertAsync(new Friend { MyId = me.Id, Username = friend.Username });
  await friends.InsertAsync(new Friend { MyId = friend.Id, Username = me.Username });
}

Next, let's add the following method to AppDelegate.cs and call it from within FinishedLaunching:

private async void LoadData()
{
  var service = ServiceContainer.Resolve<IWebService>() as AzureWebService;
  await service.LoadData();
}

If you run your application at this point, it will insert two users and make them friends with one another. Before doing so, let's add some more code to the LoadData method in AzureWebService.cs to insert conversations and messages as follows:

var conversations = client.GetTable<Conversation>();
var messages = client.GetTable<Message>();

var conversation = new Conversation
{
  MyId = me.Id,
  UserId = friend.Id,
  Username = friend.Username,
  LastMessage = "HEY!"
};
await conversations.InsertAsync(conversation);
await messages.InsertAsync(new Message { 
  ConversationId = conversation.Id, 
  ToId = me.Id,
  UserId = friend.Id, Username = friend.Username, 
  Text = "What's up?", Date = DateTime.Now.AddSeconds(-60)
});
await messages.InsertAsync(new Message { 
  ConversationId = conversation.Id, 
  ToId = friend.Id,
  UserId = me.Id, Username = me.Username, 
  Text = "Not much", Date = DateTime.Now.AddSeconds(-30)
});
await messages.InsertAsync(new Message { 
  ConversationId = conversation.Id, 
  ToId = me.Id,
  UserId = friend.Id, Username = friend.Username, 
  Text = "HEY!", Date = DateTime.Now
});

Now, if you run the application, it will seed the database with some good data to work with. I'd recommend that you remove the call to LoadData once it is successful the first time, and perhaps remove the method entirely when the development is complete.

Before going further, let's implement the rest of our IWebService interface. It can be done as follows:

public async Task<User> Register(User user)
{
  await client.GetTable<User>().InsertAsync(user);
  return user;
}
public async Task<User[]> GetFriends(string userId)
{
  var list = await client.GetTable<Friend>().Where(f => f.MyId == userId).ToListAsync();
  return list.Select(f => new User { Id = f.UserId, Username = f.Username }).ToArray();
}
public async Task<User> AddFriend( string userId, string username)
{
  var friend = new Friend { MyId = userId, Username = username };
  await client.GetTable<Friend>().InsertAsync(friend);
  return new User { Id = friend.UserId, Username = friend.Username };
}

Each method here is pretty simple. Register is very similar to Login, but the main complication for the other methods is the need to convert a Friend object to User. We used the ToListAsync method from the Azure library to get List<T>; however, since our interface uses arrays, we quickly converted the list to an array. We also utilized a couple of basic Linq operators such as Where and Select to accomplish our implementation of IWebService.

Now let's complete the methods related to conversations and messages, which are as follows:

public async Task<Conversation[]> GetConversations(string userId)
{
  var list = await client.GetTable<Conversation>().Where(c => c.MyId == userId).ToListAsync();
  return list.ToArray();
}
public async Task<Message[]> GetMessages(string conversationId)
{
  var list = await client.GetTable<Message>().Where(m => m.ConversationId == conversationId).ToListAsync();
  return list.ToArray();
}
public async Task<Message> SendMessage(Message message)
{
  await client.GetTable<Message>().InsertAsync(message);
  return message;
}

This completes our implementation of IWebService. If you run the application at this point, it will function exactly as before with the exception that the app is actually talking to a real web server. New messages will get persisted in the SQL database, and our custom scripts will handle the custom logic that we need. Feel free to play around with our implementation; you might discover some features of Azure Mobile Services that will work great with your own applications.

At this point, another good exercise would be to set up Azure Mobile Services in our Android application. To do so, you will merely need to add the Azure Mobile Services NuGet package. After that, you should be able to swap out the ServiceContainer.Register call in your Application class and call CurrentPlatform.Init(). Everything will function exactly like on iOS. Isn't cross-platform development great?

Adding the Azure Mobile Services NuGet package

To make development with Azure Mobile Services much easier, we need to add a reference to the .NET client library. To do this, we will use NuGet to add the library:

  1. Right-click on the XamChat.Core project and navigate to Add | Add Packages.
  2. Search for Azure Mobile Services using the search box.
  3. Select the Azure Mobile Services package, which at the time of writing this module was version 1.2.5.
  4. Click on Add Package.
  5. Repeat this process for the XamChat.iOS and XamChat.Android projects. There is some platform-specific setup for each platform.

Tip

You can also get the Azure Mobile Services library from the Xamarin Component Store if you like. It is very similar to using NuGet.

This will download the library and automatically add references to it in your projects. The NuGet package manager might complain of warnings, which can be ignored. NuGet was originally developed for Visual Studio on Windows, so any packages that contain PowerShell scripts or prompt for a license agreement might give you a warning.

Now, let's modify our AzureWebService.cs file. Add using Microsoft.WindowsAzure.MobileServices to the top of the file, and then make the following changes:

public class AzureWebService : IWebService
{
  MobileServiceClient client = new MobileServiceClient("https://your-service-name.azure-mobile.net/", "your-application-key");

  // -- Existing code here --
}

Make sure you fill in your mobile service name and application key. You can find your key on the Dashboard tab of the Azure Management Portal under the Manage Keys section.

Now let's implement our first method, Login, in the following manner:

public async Task<User> Login(string username, string password)
{
  var user = new User 
  {
    Username = username, 
    Password = password 
  };
  await client.GetTable<User>().InsertAsync(user);
  return user;
}

This is fairly straightforward, because of how nice this library is to use. The GetTable<T> method knows how to use a table named User based on the C# class name. Upon the first call, the dynamic schema feature will create two new columns named Username and Password based on the C# properties of our class. Note that the InsertAsync method will also fill in the user's Id property for later use in our application since we will need the Id for future calls to the mobile service.

Next, open the AppDelegate.cs file to set up our new service and add the following code:

//Replace this line
ServiceContainer.Register<IWebService>(() => new FakeWebService());

//With this line
ServiceContainer.Register<IWebService>(() => new AzureWebService());

Additionally, you will need to add some platform-specific setup for Azure Mobile Services. Add using Microsoft.WindowsAzure.MobileServices to the top of the file and add the following line of code to the bottom of FinishedLaunching in your AppDelegate.cs file:

CurrentPlatform.Init();

Now, if you compile and run your application after you log in, your app should successfully call Azure Mobile Services and insert a new user. Navigate to the Data tab of your Azure Mobile Service in the Azure Management Portal, and select the User table. You will see the user you just inserted, as shown in the following screenshot:

Adding the Azure Mobile Services NuGet package

Note

It is generally a bad idea to store passwords in plain text in your database. A simple approach to make things a bit more secure would be to store them as an MD5 hash. You should be able to make this change in the custom JavaScript that we are using to insert the password on the User table. A complete guide to securing Windows Azure applications can be found at http://msdn.microsoft.com/en-us/library/windowsazure/hh696898.aspx.

Next, let's make a new class named Friend.cs. Add it to the Azure folder that is next to the other class specific to Azure as follows:

public class Friend
{
  public string Id { get; set; }
  public string MyId { get; set; }
  public string UserId { get; set; }
  public string Username { get; set; }
}

We'll use this class to store the friends information about each user. Note that we also have an Id property, and all the classes saved in Azure Mobile Services should have a string property named Id. This will be the table's primary key in the SQL database.

Next, let's modify the Message and Conversation classes to prepare for push notifications down the road. Add a new property to the Message class as follows:

public string ToId { get; set; }

Then, add the following new property to Conversation.cs:

public string MyId { get; set; }

Here, we'll need to insert or seed some test data for our application to function correctly. The easiest way to insert data would be from C#, so let's implement the following simple method on our service to do so:

public async Task LoadData()
{
  var users = client.GetTable<User>();
  var friends = client.GetTable<Friend>();

  var me = new User
  {
    Username = "jonathanpeppers",
    Password = "password"
  };
  var friend = new User
  {
    Username = "chucknorris",
    Password = "password"
  };
  await users.InsertAsync(me);
  await users.InsertAsync(friend);
  await friends.InsertAsync(new Friend { MyId = me.Id, Username = friend.Username });
  await friends.InsertAsync(new Friend { MyId = friend.Id, Username = me.Username });
}

Next, let's add the following method to AppDelegate.cs and call it from within FinishedLaunching:

private async void LoadData()
{
  var service = ServiceContainer.Resolve<IWebService>() as AzureWebService;
  await service.LoadData();
}

If you run your application at this point, it will insert two users and make them friends with one another. Before doing so, let's add some more code to the LoadData method in AzureWebService.cs to insert conversations and messages as follows:

var conversations = client.GetTable<Conversation>();
var messages = client.GetTable<Message>();

var conversation = new Conversation
{
  MyId = me.Id,
  UserId = friend.Id,
  Username = friend.Username,
  LastMessage = "HEY!"
};
await conversations.InsertAsync(conversation);
await messages.InsertAsync(new Message { 
  ConversationId = conversation.Id, 
  ToId = me.Id,
  UserId = friend.Id, Username = friend.Username, 
  Text = "What's up?", Date = DateTime.Now.AddSeconds(-60)
});
await messages.InsertAsync(new Message { 
  ConversationId = conversation.Id, 
  ToId = friend.Id,
  UserId = me.Id, Username = me.Username, 
  Text = "Not much", Date = DateTime.Now.AddSeconds(-30)
});
await messages.InsertAsync(new Message { 
  ConversationId = conversation.Id, 
  ToId = me.Id,
  UserId = friend.Id, Username = friend.Username, 
  Text = "HEY!", Date = DateTime.Now
});

Now, if you run the application, it will seed the database with some good data to work with. I'd recommend that you remove the call to LoadData once it is successful the first time, and perhaps remove the method entirely when the development is complete.

Before going further, let's implement the rest of our IWebService interface. It can be done as follows:

public async Task<User> Register(User user)
{
  await client.GetTable<User>().InsertAsync(user);
  return user;
}
public async Task<User[]> GetFriends(string userId)
{
  var list = await client.GetTable<Friend>().Where(f => f.MyId == userId).ToListAsync();
  return list.Select(f => new User { Id = f.UserId, Username = f.Username }).ToArray();
}
public async Task<User> AddFriend( string userId, string username)
{
  var friend = new Friend { MyId = userId, Username = username };
  await client.GetTable<Friend>().InsertAsync(friend);
  return new User { Id = friend.UserId, Username = friend.Username };
}

Each method here is pretty simple. Register is very similar to Login, but the main complication for the other methods is the need to convert a Friend object to User. We used the ToListAsync method from the Azure library to get List<T>; however, since our interface uses arrays, we quickly converted the list to an array. We also utilized a couple of basic Linq operators such as Where and Select to accomplish our implementation of IWebService.

Now let's complete the methods related to conversations and messages, which are as follows:

public async Task<Conversation[]> GetConversations(string userId)
{
  var list = await client.GetTable<Conversation>().Where(c => c.MyId == userId).ToListAsync();
  return list.ToArray();
}
public async Task<Message[]> GetMessages(string conversationId)
{
  var list = await client.GetTable<Message>().Where(m => m.ConversationId == conversationId).ToListAsync();
  return list.ToArray();
}
public async Task<Message> SendMessage(Message message)
{
  await client.GetTable<Message>().InsertAsync(message);
  return message;
}

This completes our implementation of IWebService. If you run the application at this point, it will function exactly as before with the exception that the app is actually talking to a real web server. New messages will get persisted in the SQL database, and our custom scripts will handle the custom logic that we need. Feel free to play around with our implementation; you might discover some features of Azure Mobile Services that will work great with your own applications.

At this point, another good exercise would be to set up Azure Mobile Services in our Android application. To do so, you will merely need to add the Azure Mobile Services NuGet package. After that, you should be able to swap out the ServiceContainer.Register call in your Application class and call CurrentPlatform.Init(). Everything will function exactly like on iOS. Isn't cross-platform development great?

Using the Apple Push Notification service

Implementing push notifications with Azure Mobile Services on iOS is very simple to set up from an Azure backend perspective. The most complicated part is working through Apple's process of creating certificates and provisioning profiles in order to configure your iOS application. Before we continue, make sure you have a valid iOS Developer Program account, as you will not be able to send push notifications without one. If you are unfamiliar with the concept of push notifications, take a look at Apple's documentation at http://tinyurl.com/XamarinAPNS.

To send push notifications, you need to set up the following:

  • An explicit App ID registered with Apple
  • A provisioning profile targeting that App ID
  • A certificate for your server to trigger the push notification

Apple provides both a development and production certificate that you can use to send push notifications from your server.

Setting up proper provisioning

Let's begin by navigating to http://developer.apple.com/account and performing the following steps:

  1. Click on the Identifiers link.
  2. Click on the plus button in the top-right corner of the window.
  3. Enter a description, such as XamChat, for the bundle ID.
  4. Enter your bundle ID under the Explicit App ID section. This should match the bundle ID you set up in your Info.plist file, for example, com.yourcompanyname.xamchat.
  5. Under App Services, make sure you check Push Notifications.
  6. Now, click on Continue.
  7. Review your final settings and hit Submit.

This will create an explicit App ID similar to what you can see in the following screenshot, which we can use for sending push notifications:

Setting up proper provisioning

Setting up your provisioning profile

For push notifications, we have to use a profile with an explicit App ID that is not a development certificate. Now let's set up a provisioning profile:

  1. Click on the Development link under Provisioning Profiles in the right-hand side pane.
  2. Click on the plus button in the top-right corner.
  3. Check iOS App Development and click on Continue.
  4. Select the App ID we just created and click on Continue.
  5. Select the developer and click on Continue.
  6. Select the devices you will be using and click on Continue.
  7. Enter a name for the profile and click on Generate.
  8. Download the profile and install it, or open XCode and use the sync button by navigating to Preferences | Accounts.

When finished, you should arrive at a success web page that looks similar to the following screenshot:

Setting up your provisioning profile

Setting up a certificate signing request

Next, we perform the following steps to set up the certificate our server needs:

  1. Click on the Development link under Certificates in the right-hand side pane.
  2. Click on the plus button in the top-right corner.
  3. Enable Apple Push Notifications service SSL (Sandbox) and click on Continue.
  4. Select your App ID as before and click on Continue.
  5. Create a new certificate signing request as per Apple's instructions. You can also refer to Chapter 7, Deploying and Testing on Devices, or locate the *.certSigningRequest file.
  6. Next, click on Continue.
  7. Upload the signing request file and click on Generate.
  8. Next, click on Download.
  9. Open the file to import the certificate into Keychain.
  10. Locate the certificate in Keychain. It will be titled Apple Development iOS Push Services and will contain your bundle ID.
  11. Right-click on the certificate and export it somewhere on your filesystem. Enter a password that you will remember.

This will create the certificate that we need to send push notifications to our users from Azure Mobile Services. All that remains is to return to the Azure Management Portal and upload the certificate from the Push tab under Apple Push Notification Settings, as shown in the following screenshot:

Setting up a certificate signing request

This upload concludes the configuration we need from Apple's side.

Making client-side changes for push notifications

Next, let's return to our XamChat.iOS project in Xamarin Studio to make the necessary changes on the client side for push notifications. We will need to add a few new classes to our shared code to start with.

Open IWebService.cs and add the following new method:

Task RegisterPush(string userId, string deviceToken);

Next, let's implement this method in FakeWebService.cs (just so it compiles) as follows:

public async Task RegisterPush(string userId, string deviceToken)
{
  await Sleep();
}

Now, let's add a new class named Device.cs in the Core/Azure folder:

public class Device
{
  public string Id { get; set;}
  public string UserId { get; set; }
  public string DeviceToken { get; set; }
}

Finally, we can implement the real method in AzureWebService.cs as follows:

public async Task RegisterPush( string userId, string deviceToken)
{
  await client.GetTable<Device>().InsertAsync(new Device {
      UserId = userId,
      DeviceToken = deviceToken
    });
}

As for ViewModels, we need to add one more new method to LoginViewModel.cs:

public async Task RegisterPush(string deviceToken)
{
  if (settings.User == null)
    throw new Exception("User is null");
  await service.RegisterPush(settings.User.Id, deviceToken);
}

Then, we need to add a small modification to MessageViewModel.cs. Add the following line when creating a new Message object in the SendMessage method:

ToId = Conversation.UserId,

This modification concludes what we need to add to our shared code. We will reuse this new functionality when we add push notifications to Android, so go ahead and take the time to link in the new Device.cs file in your XamChat.Droid project to build your entire solution.

Now, let's add the iOS platform-specific code we need. Add the following methods to your AppDelegate.cs file:

public async override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
  var loginViewModel = ServiceContainer.Resolve<LoginViewModel>();
  try
  {
    string token = deviceToken.Description;
    token = token.Substring(1, token.Length - 2);
    await loginViewModel.RegisterPush(token);
  }
  catch (Exception exc)
  {
    Console.WriteLine("Error registering push: " + exc);
  }
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
  Console.WriteLine("Error registering push: " + error.LocalizedDescription);
}

We implemented a couple of important methods in the preceding code snippet. RegisteredForRemoteNotifications will occur when Apple successfully returns a device token from its servers. It is returned within angle brackets, so we do a little work to trim those off and pass the device token through LoginViewModel to Azure Mobile Services. We also implemented FailedToRegisterForRemoteNotifications just to report any errors that might occur throughout the process.

One last thing to do is to actually make a call to register for remote notifications. Open LoginController.cs and add the following line of code directly after the successful call to log in:

UIApplication.SharedApplication.RegisterForRemoteNotificationTypes( 
UIRemoteNotificationType.Alert | 
UIRemoteNotificationType.Badge | 
UIRemoteNotificationType.Sound);

You can also call the method on startup; however, in our situation, we need a valid user ID to store in the Device table in Azure.

Now let's switch to the Azure Management Portal and make the remaining changes in JavaScript on the server side. Under the Data tab, create a new table named Device with the default settings.

Next, we need to modify the insert script so that the duplicate device tokens are not inserted:

function insert(item, user, request)
{
  var devicesTable = tables.getTable('device');
  devicesTable.where({ userId: item.UserId, deviceToken: item.DeviceToken }).read({ success: function (devices)
    {
      if (devices.length > 0)
      {
        request.respond(200, devices[0]);
      }
      else
      {
        request.execute();
      }
    }
  });
}

Last but not least, we need to modify the insert script for the Message table to send push notifications to the user. The message was sent as follows:

function insert(item, user, request) {
  request.execute();
  var devicesTable = tables.getTable('device');
  devicesTable.where({ userId : item.ToId }).read({
    success: function(devices) {
      devices.forEach(function(device) {
        var text = item.Username + ": " + item.Text;
        push.apns.send(device.DeviceToken, {
          alert: text,
          badge: 1,
          payload: {
            message: text
          }
        });
      });
    }
  });
}

After executing the request, we retrieve a list of devices from the database and send out a push notification for each one. To test push notifications, deploy the application and log in with the secondary user (if using our examples: chucknorris). After logging in, you can just background the app with the home button. Next, log in with the primary user on your iOS simulator and send a message. You should receive a push notification, as shown in the following screenshot:

Making client-side changes for push notifications

Setting up proper provisioning

Let's begin by navigating to http://developer.apple.com/account and performing the following steps:

  1. Click on the Identifiers link.
  2. Click on the plus button in the top-right corner of the window.
  3. Enter a description, such as XamChat, for the bundle ID.
  4. Enter your bundle ID under the Explicit App ID section. This should match the bundle ID you set up in your Info.plist file, for example, com.yourcompanyname.xamchat.
  5. Under App Services, make sure you check Push Notifications.
  6. Now, click on Continue.
  7. Review your final settings and hit Submit.

This will create an explicit App ID similar to what you can see in the following screenshot, which we can use for sending push notifications:

Setting up proper provisioning

Setting up your provisioning profile

For push notifications, we have to use a profile with an explicit App ID that is not a development certificate. Now let's set up a provisioning profile:

  1. Click on the Development link under Provisioning Profiles in the right-hand side pane.
  2. Click on the plus button in the top-right corner.
  3. Check iOS App Development and click on Continue.
  4. Select the App ID we just created and click on Continue.
  5. Select the developer and click on Continue.
  6. Select the devices you will be using and click on Continue.
  7. Enter a name for the profile and click on Generate.
  8. Download the profile and install it, or open XCode and use the sync button by navigating to Preferences | Accounts.

When finished, you should arrive at a success web page that looks similar to the following screenshot:

Setting up your provisioning profile

Setting up a certificate signing request

Next, we perform the following steps to set up the certificate our server needs:

  1. Click on the Development link under Certificates in the right-hand side pane.
  2. Click on the plus button in the top-right corner.
  3. Enable Apple Push Notifications service SSL (Sandbox) and click on Continue.
  4. Select your App ID as before and click on Continue.
  5. Create a new certificate signing request as per Apple's instructions. You can also refer to Chapter 7, Deploying and Testing on Devices, or locate the *.certSigningRequest file.
  6. Next, click on Continue.
  7. Upload the signing request file and click on Generate.
  8. Next, click on Download.
  9. Open the file to import the certificate into Keychain.
  10. Locate the certificate in Keychain. It will be titled Apple Development iOS Push Services and will contain your bundle ID.
  11. Right-click on the certificate and export it somewhere on your filesystem. Enter a password that you will remember.

This will create the certificate that we need to send push notifications to our users from Azure Mobile Services. All that remains is to return to the Azure Management Portal and upload the certificate from the Push tab under Apple Push Notification Settings, as shown in the following screenshot:

Setting up a certificate signing request

This upload concludes the configuration we need from Apple's side.

Making client-side changes for push notifications

Next, let's return to our XamChat.iOS project in Xamarin Studio to make the necessary changes on the client side for push notifications. We will need to add a few new classes to our shared code to start with.

Open IWebService.cs and add the following new method:

Task RegisterPush(string userId, string deviceToken);

Next, let's implement this method in FakeWebService.cs (just so it compiles) as follows:

public async Task RegisterPush(string userId, string deviceToken)
{
  await Sleep();
}

Now, let's add a new class named Device.cs in the Core/Azure folder:

public class Device
{
  public string Id { get; set;}
  public string UserId { get; set; }
  public string DeviceToken { get; set; }
}

Finally, we can implement the real method in AzureWebService.cs as follows:

public async Task RegisterPush( string userId, string deviceToken)
{
  await client.GetTable<Device>().InsertAsync(new Device {
      UserId = userId,
      DeviceToken = deviceToken
    });
}

As for ViewModels, we need to add one more new method to LoginViewModel.cs:

public async Task RegisterPush(string deviceToken)
{
  if (settings.User == null)
    throw new Exception("User is null");
  await service.RegisterPush(settings.User.Id, deviceToken);
}

Then, we need to add a small modification to MessageViewModel.cs. Add the following line when creating a new Message object in the SendMessage method:

ToId = Conversation.UserId,

This modification concludes what we need to add to our shared code. We will reuse this new functionality when we add push notifications to Android, so go ahead and take the time to link in the new Device.cs file in your XamChat.Droid project to build your entire solution.

Now, let's add the iOS platform-specific code we need. Add the following methods to your AppDelegate.cs file:

public async override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
  var loginViewModel = ServiceContainer.Resolve<LoginViewModel>();
  try
  {
    string token = deviceToken.Description;
    token = token.Substring(1, token.Length - 2);
    await loginViewModel.RegisterPush(token);
  }
  catch (Exception exc)
  {
    Console.WriteLine("Error registering push: " + exc);
  }
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
  Console.WriteLine("Error registering push: " + error.LocalizedDescription);
}

We implemented a couple of important methods in the preceding code snippet. RegisteredForRemoteNotifications will occur when Apple successfully returns a device token from its servers. It is returned within angle brackets, so we do a little work to trim those off and pass the device token through LoginViewModel to Azure Mobile Services. We also implemented FailedToRegisterForRemoteNotifications just to report any errors that might occur throughout the process.

One last thing to do is to actually make a call to register for remote notifications. Open LoginController.cs and add the following line of code directly after the successful call to log in:

UIApplication.SharedApplication.RegisterForRemoteNotificationTypes( 
UIRemoteNotificationType.Alert | 
UIRemoteNotificationType.Badge | 
UIRemoteNotificationType.Sound);

You can also call the method on startup; however, in our situation, we need a valid user ID to store in the Device table in Azure.

Now let's switch to the Azure Management Portal and make the remaining changes in JavaScript on the server side. Under the Data tab, create a new table named Device with the default settings.

Next, we need to modify the insert script so that the duplicate device tokens are not inserted:

function insert(item, user, request)
{
  var devicesTable = tables.getTable('device');
  devicesTable.where({ userId: item.UserId, deviceToken: item.DeviceToken }).read({ success: function (devices)
    {
      if (devices.length > 0)
      {
        request.respond(200, devices[0]);
      }
      else
      {
        request.execute();
      }
    }
  });
}

Last but not least, we need to modify the insert script for the Message table to send push notifications to the user. The message was sent as follows:

function insert(item, user, request) {
  request.execute();
  var devicesTable = tables.getTable('device');
  devicesTable.where({ userId : item.ToId }).read({
    success: function(devices) {
      devices.forEach(function(device) {
        var text = item.Username + ": " + item.Text;
        push.apns.send(device.DeviceToken, {
          alert: text,
          badge: 1,
          payload: {
            message: text
          }
        });
      });
    }
  });
}

After executing the request, we retrieve a list of devices from the database and send out a push notification for each one. To test push notifications, deploy the application and log in with the secondary user (if using our examples: chucknorris). After logging in, you can just background the app with the home button. Next, log in with the primary user on your iOS simulator and send a message. You should receive a push notification, as shown in the following screenshot:

Making client-side changes for push notifications

Setting up your provisioning profile

For push notifications, we have to use a profile with an explicit App ID that is not a development certificate. Now let's set up a provisioning profile:

  1. Click on the Development link under Provisioning Profiles in the right-hand side pane.
  2. Click on the plus button in the top-right corner.
  3. Check iOS App Development and click on Continue.
  4. Select the App ID we just created and click on Continue.
  5. Select the developer and click on Continue.
  6. Select the devices you will be using and click on Continue.
  7. Enter a name for the profile and click on Generate.
  8. Download the profile and install it, or open XCode and use the sync button by navigating to Preferences | Accounts.

When finished, you should arrive at a success web page that looks similar to the following screenshot:

Setting up your provisioning profile

Setting up a certificate signing request

Next, we perform the following steps to set up the certificate our server needs:

  1. Click on the Development link under Certificates in the right-hand side pane.
  2. Click on the plus button in the top-right corner.
  3. Enable Apple Push Notifications service SSL (Sandbox) and click on Continue.
  4. Select your App ID as before and click on Continue.
  5. Create a new certificate signing request as per Apple's instructions. You can also refer to Chapter 7, Deploying and Testing on Devices, or locate the *.certSigningRequest file.
  6. Next, click on Continue.
  7. Upload the signing request file and click on Generate.
  8. Next, click on Download.
  9. Open the file to import the certificate into Keychain.
  10. Locate the certificate in Keychain. It will be titled Apple Development iOS Push Services and will contain your bundle ID.
  11. Right-click on the certificate and export it somewhere on your filesystem. Enter a password that you will remember.

This will create the certificate that we need to send push notifications to our users from Azure Mobile Services. All that remains is to return to the Azure Management Portal and upload the certificate from the Push tab under Apple Push Notification Settings, as shown in the following screenshot:

Setting up a certificate signing request

This upload concludes the configuration we need from Apple's side.

Making client-side changes for push notifications

Next, let's return to our XamChat.iOS project in Xamarin Studio to make the necessary changes on the client side for push notifications. We will need to add a few new classes to our shared code to start with.

Open IWebService.cs and add the following new method:

Task RegisterPush(string userId, string deviceToken);

Next, let's implement this method in FakeWebService.cs (just so it compiles) as follows:

public async Task RegisterPush(string userId, string deviceToken)
{
  await Sleep();
}

Now, let's add a new class named Device.cs in the Core/Azure folder:

public class Device
{
  public string Id { get; set;}
  public string UserId { get; set; }
  public string DeviceToken { get; set; }
}

Finally, we can implement the real method in AzureWebService.cs as follows:

public async Task RegisterPush( string userId, string deviceToken)
{
  await client.GetTable<Device>().InsertAsync(new Device {
      UserId = userId,
      DeviceToken = deviceToken
    });
}

As for ViewModels, we need to add one more new method to LoginViewModel.cs:

public async Task RegisterPush(string deviceToken)
{
  if (settings.User == null)
    throw new Exception("User is null");
  await service.RegisterPush(settings.User.Id, deviceToken);
}

Then, we need to add a small modification to MessageViewModel.cs. Add the following line when creating a new Message object in the SendMessage method:

ToId = Conversation.UserId,

This modification concludes what we need to add to our shared code. We will reuse this new functionality when we add push notifications to Android, so go ahead and take the time to link in the new Device.cs file in your XamChat.Droid project to build your entire solution.

Now, let's add the iOS platform-specific code we need. Add the following methods to your AppDelegate.cs file:

public async override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
  var loginViewModel = ServiceContainer.Resolve<LoginViewModel>();
  try
  {
    string token = deviceToken.Description;
    token = token.Substring(1, token.Length - 2);
    await loginViewModel.RegisterPush(token);
  }
  catch (Exception exc)
  {
    Console.WriteLine("Error registering push: " + exc);
  }
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
  Console.WriteLine("Error registering push: " + error.LocalizedDescription);
}

We implemented a couple of important methods in the preceding code snippet. RegisteredForRemoteNotifications will occur when Apple successfully returns a device token from its servers. It is returned within angle brackets, so we do a little work to trim those off and pass the device token through LoginViewModel to Azure Mobile Services. We also implemented FailedToRegisterForRemoteNotifications just to report any errors that might occur throughout the process.

One last thing to do is to actually make a call to register for remote notifications. Open LoginController.cs and add the following line of code directly after the successful call to log in:

UIApplication.SharedApplication.RegisterForRemoteNotificationTypes( 
UIRemoteNotificationType.Alert | 
UIRemoteNotificationType.Badge | 
UIRemoteNotificationType.Sound);

You can also call the method on startup; however, in our situation, we need a valid user ID to store in the Device table in Azure.

Now let's switch to the Azure Management Portal and make the remaining changes in JavaScript on the server side. Under the Data tab, create a new table named Device with the default settings.

Next, we need to modify the insert script so that the duplicate device tokens are not inserted:

function insert(item, user, request)
{
  var devicesTable = tables.getTable('device');
  devicesTable.where({ userId: item.UserId, deviceToken: item.DeviceToken }).read({ success: function (devices)
    {
      if (devices.length > 0)
      {
        request.respond(200, devices[0]);
      }
      else
      {
        request.execute();
      }
    }
  });
}

Last but not least, we need to modify the insert script for the Message table to send push notifications to the user. The message was sent as follows:

function insert(item, user, request) {
  request.execute();
  var devicesTable = tables.getTable('device');
  devicesTable.where({ userId : item.ToId }).read({
    success: function(devices) {
      devices.forEach(function(device) {
        var text = item.Username + ": " + item.Text;
        push.apns.send(device.DeviceToken, {
          alert: text,
          badge: 1,
          payload: {
            message: text
          }
        });
      });
    }
  });
}

After executing the request, we retrieve a list of devices from the database and send out a push notification for each one. To test push notifications, deploy the application and log in with the secondary user (if using our examples: chucknorris). After logging in, you can just background the app with the home button. Next, log in with the primary user on your iOS simulator and send a message. You should receive a push notification, as shown in the following screenshot:

Making client-side changes for push notifications

Setting up a certificate signing request

Next, we perform the following steps to set up the certificate our server needs:

  1. Click on the Development link under Certificates in the right-hand side pane.
  2. Click on the plus button in the top-right corner.
  3. Enable Apple Push Notifications service SSL (Sandbox) and click on Continue.
  4. Select your App ID as before and click on Continue.
  5. Create a new certificate signing request as per Apple's instructions. You can also refer to Chapter 7, Deploying and Testing on Devices, or locate the *.certSigningRequest file.
  6. Next, click on Continue.
  7. Upload the signing request file and click on Generate.
  8. Next, click on Download.
  9. Open the file to import the certificate into Keychain.
  10. Locate the certificate in Keychain. It will be titled Apple Development iOS Push Services and will contain your bundle ID.
  11. Right-click on the certificate and export it somewhere on your filesystem. Enter a password that you will remember.

This will create the certificate that we need to send push notifications to our users from Azure Mobile Services. All that remains is to return to the Azure Management Portal and upload the certificate from the Push tab under Apple Push Notification Settings, as shown in the following screenshot:

Setting up a certificate signing request

This upload concludes the configuration we need from Apple's side.

Making client-side changes for push notifications

Next, let's return to our XamChat.iOS project in Xamarin Studio to make the necessary changes on the client side for push notifications. We will need to add a few new classes to our shared code to start with.

Open IWebService.cs and add the following new method:

Task RegisterPush(string userId, string deviceToken);

Next, let's implement this method in FakeWebService.cs (just so it compiles) as follows:

public async Task RegisterPush(string userId, string deviceToken)
{
  await Sleep();
}

Now, let's add a new class named Device.cs in the Core/Azure folder:

public class Device
{
  public string Id { get; set;}
  public string UserId { get; set; }
  public string DeviceToken { get; set; }
}

Finally, we can implement the real method in AzureWebService.cs as follows:

public async Task RegisterPush( string userId, string deviceToken)
{
  await client.GetTable<Device>().InsertAsync(new Device {
      UserId = userId,
      DeviceToken = deviceToken
    });
}

As for ViewModels, we need to add one more new method to LoginViewModel.cs:

public async Task RegisterPush(string deviceToken)
{
  if (settings.User == null)
    throw new Exception("User is null");
  await service.RegisterPush(settings.User.Id, deviceToken);
}

Then, we need to add a small modification to MessageViewModel.cs. Add the following line when creating a new Message object in the SendMessage method:

ToId = Conversation.UserId,

This modification concludes what we need to add to our shared code. We will reuse this new functionality when we add push notifications to Android, so go ahead and take the time to link in the new Device.cs file in your XamChat.Droid project to build your entire solution.

Now, let's add the iOS platform-specific code we need. Add the following methods to your AppDelegate.cs file:

public async override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
  var loginViewModel = ServiceContainer.Resolve<LoginViewModel>();
  try
  {
    string token = deviceToken.Description;
    token = token.Substring(1, token.Length - 2);
    await loginViewModel.RegisterPush(token);
  }
  catch (Exception exc)
  {
    Console.WriteLine("Error registering push: " + exc);
  }
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
  Console.WriteLine("Error registering push: " + error.LocalizedDescription);
}

We implemented a couple of important methods in the preceding code snippet. RegisteredForRemoteNotifications will occur when Apple successfully returns a device token from its servers. It is returned within angle brackets, so we do a little work to trim those off and pass the device token through LoginViewModel to Azure Mobile Services. We also implemented FailedToRegisterForRemoteNotifications just to report any errors that might occur throughout the process.

One last thing to do is to actually make a call to register for remote notifications. Open LoginController.cs and add the following line of code directly after the successful call to log in:

UIApplication.SharedApplication.RegisterForRemoteNotificationTypes( 
UIRemoteNotificationType.Alert | 
UIRemoteNotificationType.Badge | 
UIRemoteNotificationType.Sound);

You can also call the method on startup; however, in our situation, we need a valid user ID to store in the Device table in Azure.

Now let's switch to the Azure Management Portal and make the remaining changes in JavaScript on the server side. Under the Data tab, create a new table named Device with the default settings.

Next, we need to modify the insert script so that the duplicate device tokens are not inserted:

function insert(item, user, request)
{
  var devicesTable = tables.getTable('device');
  devicesTable.where({ userId: item.UserId, deviceToken: item.DeviceToken }).read({ success: function (devices)
    {
      if (devices.length > 0)
      {
        request.respond(200, devices[0]);
      }
      else
      {
        request.execute();
      }
    }
  });
}

Last but not least, we need to modify the insert script for the Message table to send push notifications to the user. The message was sent as follows:

function insert(item, user, request) {
  request.execute();
  var devicesTable = tables.getTable('device');
  devicesTable.where({ userId : item.ToId }).read({
    success: function(devices) {
      devices.forEach(function(device) {
        var text = item.Username + ": " + item.Text;
        push.apns.send(device.DeviceToken, {
          alert: text,
          badge: 1,
          payload: {
            message: text
          }
        });
      });
    }
  });
}

After executing the request, we retrieve a list of devices from the database and send out a push notification for each one. To test push notifications, deploy the application and log in with the secondary user (if using our examples: chucknorris). After logging in, you can just background the app with the home button. Next, log in with the primary user on your iOS simulator and send a message. You should receive a push notification, as shown in the following screenshot:

Making client-side changes for push notifications

Making client-side changes for push notifications

Next, let's return to our XamChat.iOS project in Xamarin Studio to make the necessary changes on the client side for push notifications. We will need to add a few new classes to our shared code to start with.

Open IWebService.cs and add the following new method:

Task RegisterPush(string userId, string deviceToken);

Next, let's implement this method in FakeWebService.cs (just so it compiles) as follows:

public async Task RegisterPush(string userId, string deviceToken)
{
  await Sleep();
}

Now, let's add a new class named Device.cs in the Core/Azure folder:

public class Device
{
  public string Id { get; set;}
  public string UserId { get; set; }
  public string DeviceToken { get; set; }
}

Finally, we can implement the real method in AzureWebService.cs as follows:

public async Task RegisterPush( string userId, string deviceToken)
{
  await client.GetTable<Device>().InsertAsync(new Device {
      UserId = userId,
      DeviceToken = deviceToken
    });
}

As for ViewModels, we need to add one more new method to LoginViewModel.cs:

public async Task RegisterPush(string deviceToken)
{
  if (settings.User == null)
    throw new Exception("User is null");
  await service.RegisterPush(settings.User.Id, deviceToken);
}

Then, we need to add a small modification to MessageViewModel.cs. Add the following line when creating a new Message object in the SendMessage method:

ToId = Conversation.UserId,

This modification concludes what we need to add to our shared code. We will reuse this new functionality when we add push notifications to Android, so go ahead and take the time to link in the new Device.cs file in your XamChat.Droid project to build your entire solution.

Now, let's add the iOS platform-specific code we need. Add the following methods to your AppDelegate.cs file:

public async override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
  var loginViewModel = ServiceContainer.Resolve<LoginViewModel>();
  try
  {
    string token = deviceToken.Description;
    token = token.Substring(1, token.Length - 2);
    await loginViewModel.RegisterPush(token);
  }
  catch (Exception exc)
  {
    Console.WriteLine("Error registering push: " + exc);
  }
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
  Console.WriteLine("Error registering push: " + error.LocalizedDescription);
}

We implemented a couple of important methods in the preceding code snippet. RegisteredForRemoteNotifications will occur when Apple successfully returns a device token from its servers. It is returned within angle brackets, so we do a little work to trim those off and pass the device token through LoginViewModel to Azure Mobile Services. We also implemented FailedToRegisterForRemoteNotifications just to report any errors that might occur throughout the process.

One last thing to do is to actually make a call to register for remote notifications. Open LoginController.cs and add the following line of code directly after the successful call to log in:

UIApplication.SharedApplication.RegisterForRemoteNotificationTypes( 
UIRemoteNotificationType.Alert | 
UIRemoteNotificationType.Badge | 
UIRemoteNotificationType.Sound);

You can also call the method on startup; however, in our situation, we need a valid user ID to store in the Device table in Azure.

Now let's switch to the Azure Management Portal and make the remaining changes in JavaScript on the server side. Under the Data tab, create a new table named Device with the default settings.

Next, we need to modify the insert script so that the duplicate device tokens are not inserted:

function insert(item, user, request)
{
  var devicesTable = tables.getTable('device');
  devicesTable.where({ userId: item.UserId, deviceToken: item.DeviceToken }).read({ success: function (devices)
    {
      if (devices.length > 0)
      {
        request.respond(200, devices[0]);
      }
      else
      {
        request.execute();
      }
    }
  });
}

Last but not least, we need to modify the insert script for the Message table to send push notifications to the user. The message was sent as follows:

function insert(item, user, request) {
  request.execute();
  var devicesTable = tables.getTable('device');
  devicesTable.where({ userId : item.ToId }).read({
    success: function(devices) {
      devices.forEach(function(device) {
        var text = item.Username + ": " + item.Text;
        push.apns.send(device.DeviceToken, {
          alert: text,
          badge: 1,
          payload: {
            message: text
          }
        });
      });
    }
  });
}

After executing the request, we retrieve a list of devices from the database and send out a push notification for each one. To test push notifications, deploy the application and log in with the secondary user (if using our examples: chucknorris). After logging in, you can just background the app with the home button. Next, log in with the primary user on your iOS simulator and send a message. You should receive a push notification, as shown in the following screenshot:

Making client-side changes for push notifications

Implementing Google Cloud Messaging

Since we have already set up everything we need in the shared code and on Azure, setting up push notifications for Android will be a lot less work at this point of time. To continue, you will need a Google account with a verified e-mail address; however, I would recommend that you use an account registered with Google Play if you have one. You can refer to the full documentation on Google Cloud Messaging (GCM) at http://developer.android.com/google/gcm.

Note

Note that Google Cloud Messaging requires that Google Play be installed on the Android device and that the Android OS be at least version 2.2.

Let's begin by navigating to http://cloud.google.com/console and performing the following steps:

  1. Click on the Create Project button.
  2. Enter an appropriate project name such as XamChat.
  3. Enter a project ID; you can use the generated one. I prefer to use my application's bundle ID and replace the periods with hyphens.
  4. Agree to the Terms of Service.
  5. Click on the Create button.
  6. When creating your first project, you might have to verify the mobile number associated with your account.
  7. Note the Project Number field on the Overview page. We will need this number later.

The following screenshot shows the Overview tab:

Implementing Google Cloud Messaging

Now we can continue with our setup as follows:

  1. Click on APIs & auth in the left-hand side pane.
  2. Scroll down and click on Google Cloud Messaging for Android.
  3. Click on the OFF button at the top to enable the service. You might have to accept another agreement.
  4. Click on Registered Apps in the left-hand side pane.
  5. Click on the Register App button at the top.
  6. Enter XamChat in the App Name field and click on Register. You can leave the Platform selection on Web Application at its default value.
  7. Expand the Server Key section and copy the API Key value to your clipboard.
  8. Switch to the Azure Management Portal and navigate to the Push tab in your Azure Mobile Service instance.
  9. Paste the API key in the google cloud messaging settings section and click on Save.
Implementing Google Cloud Messaging

Next, let's modify our insert script for the Message table to support Android as follows:

function insert(item, user, request) {
  request.execute();
  var devicesTable = tables.getTable('device');
  devicesTable.where({ userId : item.ToId }).read({
    success: function(devices) {
      devices.forEach(function(device) {
        if (device.DeviceToken.length > 72) {
          push.gcm.send(device.DeviceToken, {
            title: item.Username, 
            message: item.Text, 
          });
        }
        else {
          var text = item.Username + ": " + item.Text;
          push.apns.send(device.DeviceToken, {
            alert: text,
            badge: 1,
            payload: {
              message: text
            }
          });
        }
      });
    }
  });
}

Basically, we send any deviceToken values that are longer than 72 characters to GCM. This is one simple way to do it, but you can also add a value to the Device table that indicates whether the device is Android or iOS. GCM also supports sending custom values to be displayed in the notification area, so we send an actual title along with the message.

This completes our setup on Azure's side. Setting up the next part in our Android application can be a bit difficult, so we will use a library called PushSharp to simplify our implementation.

First, navigate to http://github.com/Redth/PushSharp and perform the following steps:

  1. Download the project and place it in the same folder as your XamChat solution.
  2. Add the PushSharp.Client.MonoForAndroid.Gcm project to your solution. You can locate the project in the PushSharp.Client subdirectory.
  3. Reference the new project from your XamChat.Droid project.
  4. If it's not already installed, you will need to install the Android SDK Platform for Android 2.2 (API 8). You can install this from the Android SDK manager that can be launched from the Tools menu in Xamarin Studio.

Next, create a new class called PushConstants.cs as follows:

public static class PushConstants
{
  public const string BundleId = "your-bundle-id";
  public const string ProjectNumber = "your-project-number";
}

Fill out the BundleId value with your application's bundle ID and the ProjectNumber value with the project number found on the Overview page of your Google Cloud Console.

Next, we need to set up some permissions to support push notifications in our application. Above the namespace declaration in this file, add the following:

[assembly: Permission(
  Name = XamChat.Droid.PushConstants.BundleId + 
  ".permission.C2D_MESSAGE")]
[assembly: UsesPermission(
  Name = XamChat.Droid.PushConstants.BundleId + 
  ".permission.C2D_MESSAGE")]
[assembly: UsesPermission(
  Name = "com.google.android.c2dm.permission.RECEIVE")]
[assembly: UsesPermission(
  Name = "android.permission.GET_ACCOUNTS")]
[assembly: UsesPermission(
  Name = "android.permission.INTERNET")]
[assembly: UsesPermission(
  Name = "android.permission.WAKE_LOCK")]

You can also make these changes in our AndroidManifest.xml file; however, using C# attributes can be better since it gives you the ability to use code completion while typing.

Next, create another new class named PushReceiver.cs as follows:

[BroadcastReceiver(
  Permission = GCMConstants.PERMISSION_GCM_INTENTS)]
[IntentFilter(
  new string[] { GCMConstants.INTENT_FROM_GCM_MESSAGE }, 
  Categories = new string[] { PushConstants.BundleId })]
[IntentFilter(
  new string[] { 
  GCMConstants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, 
  Categories = new string[] { PushConstants.BundleId })]
[IntentFilter(
  new string[] { 
  GCMConstants.INTENT_FROM_GCM_LIBRARY_RETRY }, 
  Categories = new string[] { PushConstants.BundleId })]
public class PushReceiver :
  PushHandlerBroadcastReceiverBase<PushHandlerService>
{ }

The PushReceiver.cs class sets up BroadcastReceiver, which is Android's native way for different applications to talk with one another. For more information on this topic, check out the Android documentation at http://developer.android.com/reference/android/content/BroadcastReceiver.html.

Next, create one last class named PushService.cs as follows:

[Service]
public class PushHandlerService : PushHandlerServiceBase
{
  public PushHandlerService() : base (PushConstants.ProjectNumber) 
  { }
}

Now, right-click on PushHandlerServiceBase and navigate to Refactor | Implement abstract members. Next, let's implement each member one by one:

protected async override void OnRegistered (Context context, string registrationId)
{
  Console.WriteLine("Push successfully registered!");
  var loginViewModel = ServiceContainer.Resolve<LoginViewModel>();
  try
  {
    await loginViewModel.RegisterPush(registrationId);
  }
  catch (Exception exc)
  {
    Console.WriteLine("Error registering push: " + exc);
  }
}

The preceding code is very similar to what we did on iOS. We merely have to send the registrationId value to loginViewModel.

Next, we have to write the following code when the message is received:

protected override void OnMessage (Context context, Intent intent)
{
  //Pull out the notification details
  string title = intent.Extras.GetString("title");
  string message = intent.Extras.GetString("message");

  //Create a new intent
  intent = new Intent(this, typeof(ConversationsActivity));

  //Create the notification
  var notification = new Notification(Android.Resource.Drawable.SymActionEmail, title);
  notification.Flags = NotificationFlags.AutoCancel;
  notification.SetLatestEventInfo(this, 
    new Java.Lang.String(title), 
    new Java.Lang.String(message), PendingIntent.GetActivity(this, 0, intent, 0));

  //Send the notification through the NotificationManager
  var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
  notificationManager.Notify(1, notification);
}

This code will actually pull out the values from the notification and display them in the notification center of the Android device. We used the built-in resource for SymActionEmail to display an e-mail icon in the notification.

Next, we just need to implement two more abstract methods. For now, let's just use Console.WriteLine to report these events as follows:

protected override void OnUnRegistered(Context context, string registrationId)
{
  Console.WriteLine("Push unregistered!");
}

protected override void OnError (Context context, string errorId)
{
  Console.WriteLine("Push error: " + errorId);
}

Down the road, you should consider removing registrations from the Device table in Azure when OnUnRegistered is called. Occasionally, a user's registrationId will change, so this is the place where your application is notified of this change.

Next, open Application.cs and add the following lines to the end of OnCreate:

PushClient.CheckDevice(this);
PushClient.CheckManifest(this);

Next, open LoginActivity.cs and add the following line after a successful login:

PushClient.Register(this, PushConstants.ProjectNumber);

Now if you repeat the steps for testing push notifications on iOS, you should be able to send a push notification to our Android app. Even better, you should be able to send push notifications across platforms, since an iOS user can send a message to an Android user.

Implementing Google Cloud Messaging

Summary

In this chapter, we went through what Windows Azure provides: Infrastructure as a Service and Platform as a Service. We set up a free Windows Azure account and set up an Azure Mobile Services instance. Next, we created all the tables we needed to store our data and wrote a few scripts to add the business logic to the web service. We implemented the client-side code for making requests against Azure Mobile Services. Lastly, we implemented push notifications for iOS using the Apple Push Notification Service and for Android using Google Cloud Messaging.

Using Azure Mobile Services, we were able to get by without writing much of the server-side code—mainly a few simple scripts. It would be pretty challenging to implement push notifications yourself instead of leveraging Azure's functionality for this. In the next chapter, we'll explore how to use third-party libraries with Xamarin. This includes everything from the Xamarin Component Store to using native Objective-C or Java libraries.