Tracking Custom Events with Xamarin, HockeyApp, and Azure Application Insights

This post shows how to log and report on custom events in Xamarin apps. There are a few steps needed to accomplish this:

  • Integrate HockeyApp into your app.
  • Record the events
  • Configure Applications Insights
  • Analyse the data

Integrate HockeyApp

HockeyApp have a guide for this here.

Record Metrics

To create some data for this post I made a little demo app – the source is available at GitHub

sshot-1

You use the MetricsManager.TrackEvent method to send the events to HockeyApp. It batches events and sends them every 15 seconds, or once 50 events are in the batch. You can call it without arguments…

MetricsManager.TrackEvent("Show Page - Page 1");

… or with custom properties…

MetricsManager.TrackEvent("DurationEvent", new Dictionary<string, string> { { "property", "value" } }, new Dictionary<string, double> { { "duration", duration} });

Once set up correctly you’ll see the events appearing in HockeyApp…

screen-shot-2017-02-19-at-16-23-03

Setup Application Insights

The process of linking HockeyApp to Application Insights is described here. The key steps are…

  • Create an API token in HockeyApp. This is under your User Profile, not against the App.
  • In Azure create a new entry in Application Insights. The application type is HockeyApp Bridge Application and the HockeyApp token is the API token you created, not the app’s id.
  • Wait for data to appear. In my experience the no data message was displayed even after events were coming through, so it pays to run some queries to check.

Analyse Data

Once the data is appearing in Insights it’s time to analyse. To do this we need to use it’s special query language. A reference is available but lets look at some examples…

How many times has a page been viewed?

customEvents
| where name startswith "Show Page"
| summarize count() by name

screen-shot-2017-02-20-at-08-23-39

Can we show that data in a pie chart?

customEvents
| where name startswith "Show Page"
| summarize count() by name
| render piechart

screen-shot-2017-02-20-at-08-17-21

What are the minimum, maximum, and average durations of my click events?

customEvents
| where name == "DurationEvent"
| extend Duration = todouble(customMeasurements.duration)
| summarize min(Duration), max(Duration), avg(Duration)

screen-shot-2017-02-20-at-08-30-21

When do most click events occur?

customEvents
| where name == "DurationEvent"
| extend Duration = todouble(customMeasurements.duration)
| summarize Count = count() by Hour = datepart("hour", timestamp)
| render barchart
Tracking Custom Events with Xamarin, HockeyApp, and Azure Application Insights

Xamarin Forms – Binding List Item Commands to the Parent ViewModel

This post shows how to bind a list item command to the parent list views binding context. Often you want to send the command to the view model, not the object that the list item is bound to.

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="App1.DemoPage">
   <ListView ItemsSource="{Binding Items}" x:Name="List">
      <ListView.ItemTemplate>
         <DataTemplate>
            <TextCell Text="{Binding}" Command="{Binding Path=BindingContext.ListItemCommand, Source={x:Reference Name=List}}" CommandParameter="{Binding}" />
         </DataTemplate>
      </ListView.ItemTemplate>
   </ListView>
</ContentPage>

To do it we have given the list view a name. Then in the item command binding we use that reference in the binding path. The command parameter is the list item’s binding context.

Xamarin Forms – Binding List Item Commands to the Parent ViewModel

Xamarin Forms Async Commands with Parameters

A quick mini post to show how to pass parameters to asynchronous commands in Xamarin Forms. I keep forgetting the syntax for this so it’s useful to have a reminder!

public class DemoViewModel
{
    public DemoViewModel()
    {
        this.DemoCommand = new Command<double>(async (x) => await CalculateSquareSlowly(x));
    }

    public ICommand DemoCommand { get; private set; }

    private async Task<double> CalculateSquareSlowly(double number)
    {
        await Task.Delay(2000);
        return number * number;
    }
}
Xamarin Forms Async Commands with Parameters

Android StartActivityForResult and Xamarin Forms

In this post I demonstrate a technique for using getting the results from Android activities in Xamarin Forms apps. The code for this example is available here.

The Problem

In a standard Android app you can use the StartActivityForResult method on your current activity to start a child activity. When the child activity completes the OnActivityResult is called on the parent activity.

In a Xamarin Forms app you don’t really have a parent activity – you have the Forms.Context object but if you use that all results will go to the main activity which is messy. If you are making a library you can’t even do that.

The Solution

The example app prompts the user to select a contact, then displays the name of the selected contact.

The magic takes place in the Phonebook class.

    public class Phonebook : IPhonebook
    {
        public Task&lt;string&gt; GetContactName()
        {
            var intent = new Intent(Forms.Context, typeof(PhonebookActivity));
            Forms.Context.StartActivity(intent);

            var tcs = new TaskCompletionSource&lt;string&gt;();
            EventHandler handler = null;

            handler = (sender, args) =&gt;
            {
                PhonebookActivity.ContactResultHandler -= handler;
                tcs.SetResult(((PhonebookActivity)sender).ContactName);
            };

            PhonebookActivity.ContactResultHandler += handler;

            return tcs.Task;
        }
    }

Although it’s short, its doing a fair bit of work. The GetContactName method starts starts the PhonebookActivity from the app’s main activity.It then uses a TaskCompletionSource to wait for an event from the PhoneBookActivity. It returns a Task so it can be called asynchronously.

There’s more magic in the PhonebookActivity class. The main reason for using this activity is to give us somewhere to call StartActivityForResult from. When the activity starts it creates and starts an Intent to prompt the user to select a contact. When that activity finishes it takes the result and raises an event back to the Phonebook class. The PhonebookActivity is never actually displayed on screen – really it’s just a wrapper for the select contact activity.

    [Activity(Label = "PhonebookActivity")]
    public class PhonebookActivity : Activity
    {
        private const int RequestCodeSelectContact = 1;

        public string ContactName { get; private set; }

        public static EventHandler ContactResultHandler;

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            if (bundle == null)
            {
                var intent = new Intent(Intent.ActionPick, ContactsContract.Contacts.ContentUri);

                if (intent.ResolveActivity(PackageManager) != null)
                {
                    StartActivityForResult(intent, RequestCodeSelectContact);
                }
            }
        }

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            ContactName = string.Empty;

            if (requestCode == RequestCodeSelectContact &amp;&amp; resultCode == Result.Ok)
            {
                var uri = data.Data;

                string[] projection = new string[] { ContactsContract.ContactNameColumns.DisplayNamePrimary };

                var cursor = ContentResolver.Query(uri, projection, null, null, null);

                if (cursor.MoveToFirst())
                {
                    int columnIndex = cursor.GetColumnIndexOrThrow(ContactsContract.ContactNameColumns.DisplayNamePrimary);
                    ContactName = cursor.GetString(columnIndex);
                }
            }

            ContactResultHandler?.Invoke(this, new EventArgs());
            Finish();
        }
    }
Android StartActivityForResult and Xamarin Forms

Creating Nuget Packages For Xamarin Forms

Here’s a quick post that describes how to create a nuget package that can be used across Xamarin Forms and platform specific projects. The goal is to create a nuget package that targets the appropriate platform for each project.

Prerequisites

You need nuget.exe available from here.

Nuget Package Explorer is useful to see what has been packaged. Get it here.

All commands should be run in a command prompt with the path to the nuget.exe in the path.

Creating the Package

First we have to create a .nuspec file…

nuget spec

This creates a file called ProjectName.nuspec

Intially the file looks like this:


<?xml version="1.0"?>
<package >
  <metadata>
    <id>$id$</id>
    <version>$version$</version>
    <title>$title$</title>
    <authors>$author$</authors>
    <owners>$author$</owners>
    <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>
    <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
    <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>$description$</description>
    <releaseNotes>Summary of changes made in this release of the package.</releaseNotes>
    <copyright>Copyright 2016</copyright>
    <tags>Tag1 Tag2</tags>
  </metadata>
</package>

The values between $ signs will be populated automatically from the projects assembly info (make sure these details are set). The remaining values can be edited or removed.

For a single assembly this is all that is required. However for a Xamarin Forms app you need to add assemblys for the platform specific project. These must be added manually. Add a files section inside the package level:


<files>
    <file src="..\Microlise.XamarinForms.Logging.Droid\Bin\Release\Microlise.XamarinForms.Logging.Droid.dll" target="lib\MonoAndroid" />
    <file src="..\Microlise.XamarinForms.Logging.IOS\Bin\Release\Microlise.XamarinForms.Logging.IOS.dll" target="lib\Xamarin.IOS" />
</files>

We are now ready to create the package…

nuget pack {projectfile}.csproj -Prop Configuration=Release

If this works a .nupkg will be created in the current folder.

Uploading The Package

You can upload your package to your nuget repository of choice. If you are using a network share to as the nuget repository you can add the new package with…

nuget init {folder with .nupkg} \\{nuget share}
Creating Nuget Packages For Xamarin Forms

Logging with NLog in Xamarin Forms

This post is a quick demo of how to add logging to a Xamarin Forms application using the NLog library. The complete source code is available here.

NLog is a popular logging library for .net and it’s compatible for Xamarin so it’s a natural choice if you want to add logging to your Xamarin app. It isn’t PCL compatible though, so you do need an implementation for each platform. This isn’t a major problem, and you might want to set the logger up differently for each platform anyway.

Let’s dive into the code…

using System;
namespace LoggingDemo
{
    public interface ILogger
    {
        void Trace(string text, params object[] args);
        void Debug(string text, params object[] args);
        void Info(string text, params object[] args);
        void Warn(string text, params object[] args);
        void Error(string text, params object[] args);
        void Fatal(string text, params object[] args);
    }
}
using System;
using System.Diagnostics.Contracts;
namespace LoggingDemo
{
    public interface ILogManager
    {
        ILogger GetLog([System.Runtime.CompilerServices.CallerFilePath]string callerFilePath = "");
    }
}

In the cross platform project we define a couple of interfaces, ILogManager and ILogger. ILogManager is responsible for creating instances of the ILogger class. ILogger contains the logging message.

In this demo the code for the IOS and Android implementations of the interfaces is pretty much the same. Let’s look at the IOS version…

using NLog;
using NLog.Config;
using NLog.Targets;
using Foundation;
using LoggingDemo.iOS;
using Xamarin.Forms;

[assembly: Dependency(typeof(NLogManager))]
namespace LoggingDemo.iOS
{
    public class NLogManager : ILogManager
    {
        public NLogManager()
        {
            var config = new LoggingConfiguration();

            var consoleTarget = new ConsoleTarget();
            config.AddTarget("console", consoleTarget);

            var consoleRule = new LoggingRule("*", LogLevel.Trace, consoleTarget);
            config.LoggingRules.Add(consoleRule);

            var fileTarget = new FileTarget();
            string folder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            fileTarget.FileName = Path.Combine(folder, "Log.txt");
            config.AddTarget("file", fileTarget);

            var fileRule = new LoggingRule("*", LogLevel.Warn, fileTarget);
            config.LoggingRules.Add(fileRule);

            LogManager.Configuration = config;
        }

        public ILogger GetLog([System.Runtime.CompilerServices.CallerFilePath] string callerFilePath = "")
        {
            string fileName = callerFilePath;

            if (fileName.Contains("/"))
            {
                fileName = fileName.Substring(fileName.LastIndexOf("/", StringComparison.CurrentCultureIgnoreCase) + 1);
            }

            var logger = LogManager.GetLogger(fileName);
            return new NLogLogger(logger);  
        }
    }
}

The NLogManager configures the logging in its constructor. Here we are logging all log entries to the console and logs of warning or above into a file called log.txt. The GetLog function returns an instance of a logger. It uses the CallerFilePath attribute to set the name of the log entries.

using System;
using System.Diagnostics.Contracts;
namespace LoggingDemo
{
    public interface ILogManager
    {
        ILogger GetLog([System.Runtime.CompilerServices.CallerFilePath]string callerFilePath = "");
    }
}

The implementation of ILogger is really simple. It just gives us a bunch of functions to log messages of various levels.

In both of these classes we use the Dependency attribute so that the shared project knows about it.

Now let’s look at using the loggers. The demo app has one screen with buttons which create log entries.

screen-shot-2016-10-16-at-21-40-45

using System;
using System.Collections.Generic;

using Xamarin.Forms;

namespace LoggingDemo
{
    public partial class MainPage : ContentPage
    {
        private static ILogger logger = DependencyService.Get<ILogManager>().GetLog();

        public MainPage()
        {
            InitializeComponent();
        }

        private void Trace_Clicked(Object sender, EventArgs args)
        {
            logger.Trace("Trace Clicked");
        }

        private void Debug_Clicked(Object sender, EventArgs args)
        {
            logger.Trace("Debug Clicked");
        }

        private void Info_Clicked(Object sender, EventArgs args)
        {
            logger.Trace("Info Clicked");
        }

        private void Warn_Clicked(Object sender, EventArgs args)
        {
            logger.Trace("Warn Clicked");
        }

        private void Error_Clicked(Object sender, EventArgs args)
        {
            logger.Trace("Error Clicked");
        }

        private void Fatal_Clicked(Object sender, EventArgs args)
        {
            logger.Trace("Fatal Clicked");
        }
    }
}

We set the logger up as a static variable of the class. Also, the dependency service will create one global static interface of the log manager (this is default behaviour and is easy to forget).

And just to prove it works, here are some log messages appearing in the console…

screen-shot-2016-10-16-at-21-41-15

 

Logging with NLog in Xamarin Forms