Analytics is an important piece in today's applications. Now that we are doing rapid development and providing updates on a more frequent basis, it is necessary to understand how your users are using your application. In this blog I will go over a high level architecture and provide general implementation guidelines for adding analytics into your modern Windows applications.

Assumptions

Since analytics is a very vague term and since there are so many libraries and variations that can be used to implement analytics, I want to define certain assumptions that I am making for this blog. The first assumption is that you are using an MVVM design pattern within your application. You can read more about the MVVM pattern and why you should be using it in my other blog post here. The second assumption is that the analytics you are sending use the format of an ID and its payload comprised of key/value pairs. In the example that I am basing this post off of I will be using Adobe's analytics library ADB Mobile. If you are not using ADB Mobile in your implementation then I will assume you have some library or custom tool that you can call to record or send the analytics; this blog post will not go into any details about actually logging and/or analyzing analytics.

1. Creating Helper Classes

We will be using four different helper classes to support our analytics. I have created these classes in the App.Utility.Analytics namespace alongside other utility classes I have such as extension classes.

The first two classes to create will simply store the string constants that are needed. The first, which I will call PageNames, will contain the IDs that will be logged. The second, which I will call ContextDataKeys, will contain the keys for the payloads that will be sent with the IDs.

Now that the IDs and keys are defined there needs to be a helper class to abstract the logging of the analytics and to provide a static way to report the analytics. I have called this static class AnalyticsHelper and I've added a static method to provide the analytics reporting:

public static void TrackState(string pageName, IDictionary contextData = null, includeBaseData = true),>

The first two parameters of the method are obvious: the pageName is the ID and the contextData is the payload. The third parameter includeBaseData is there to specify if the AnalyticsHelper should include any generic data when reporting the analytics. In my application this generic data includes the application version and application language but you could also decide to include additional information that is appropriate for your application.

The final helper class that we will create is the AnalyticsAttribute class. This class will be an attribute that we can declare on pages to indicate the ID of a page. If your application does not track analytics from page to page then this class might not be necessary. Below is my implementation:

[AttributeUsage(AttributeTargets.Class)]
public class AnalyticsAttribute : Attribute
{
 public string PageName;
}

2. Implementing Automatic Page Analytics Reporting

Although I expect that any complex application will require reporting analytics at various points throughout, a very common scenario is simply reporting page visits. Since reporting analytics is not real business logic we don't want it to be intrusive within our code. We will first use our new AnalyticsAttribute to declare a page's ID:

[Analytics(PageName = PageNames.Home)]
public sealed partial class HomePage : BasePage
{
…
}

I am referencing the PageNames static class to get the constant for my page's ID so that all of the page names are in one location and easy to change.

Now that the ID is declared for the page we need to actually do the reporting. In the code above you can see that my HomePage is inheriting from BasePage instead of Page. This is a class that I have created that all of my pages inherit. Within my BasePage I am able to override the OnNavigatedTo method from the Page class and do the analytics reporting:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
 base.OnNavigatedTo(e);
 …
 AnalyticsHelper.TrackState(this.GetPageName(), null);
}

You can make this method more complex as needed, such as providing a base class for the navigation parameters, casting to it, and getting additional context data from that to pass into TrackState. In my example we will keep it simple and assume there is no additional carry-over data from the last page that needs to be reported in the analytics for this page.

In the method above you can see that to get the page name (ID) for the page I am using a method called GetPageName(). This is just a method that traverses the Page's attributes to retrieve the page name:

public static string GetPageName()
{
 var analyticsAttr = this.GetType().GetTypeInfo().GetCustomAttributes(true)
 .FirstOrDefault();
 if (analyticsAttr != null)
 return analyticsAttr.PageName;
 return null;
}

You'll need to add some additional usings to the class to provide the extensions that are used: System.Reflection and System.Linq;

3. Getting Contextual Data

So now, assuming you have implemented the TrackState() method, you should be sending analytics for each page to which you navigate. This is helpful, but not necessarily everything that you need. Since the assumption is that you are using the MVVM pattern, the payload that you actually want to send when reporting analytics is all within your view models. Unfortunately there may not be a great way to get that data and maintain our "non-intrusive" approach to adding analytics to our application. Depending on how you set your view model as the data context to your page you may be able to just send the data into the TrackState call by getting it from a property in the view model (i.e. (this.DataContext as HomeViewModel).ContextData). If you are not able to do that because of when you set the data context on the page then you may need to use another method such as the page's Loaded event to make the TrackState() call. You can also call TrackState() in your view model to guarantee the data is there, but I like to keep the analytics data with the view since that's what actually should trigger the reporting.

I hope this has provided you with some ideas and a foundation for implementing analytics into your application. Every application has different needs so be flexible with whatever approach you take.