OpenNETCF.Google.Analytics improvements

My original plan when building up the OpenNETCF Google Analytics library (which provides GA support to Xamarin Forms apps) was to make it 100% PCL.  A fine goal, but it turns out that it was unrealistic for a single reason: the User Agent string.  My original implementation had a “user agent generator” that successfully would tell GA whether you were using an Android or iOS device, but getting any finer grained was really not a simple task.  I considered – very, very briefly – keeping a long list of user agent strings and trying to figure out which one to use based on the platform, but that sounded like an unmaintainable nightmare.  Instead, I had to create a simple native piece for each platform for something that seems pretty basic.

In iOS, this was all that was needed:

using (var webView = new UIWebView(CGRect.Empty))
{
    m_userAgent = webView.EvaluateJavascript("navigator.userAgent");
}

In Android it was even less:

m_userAgent = Java.Lang.JavaSystem.GetProperty("http.agent")

But it had to be done native.

I’ve updated the library in NuGet, and now GA will show you exactly what type of device was connected and tracking, which I feel was more than worth the added complexity.

IoC Navigation Sample for Xamarin Forms

The OpenNETCF IoC Library provides a simple and lightweight MVVM Navigation framework for Xamarin Forms application navigation (along with a lightweight DI container).  I’ve blogged a couple examples on how it can be used, but I thought that a more complete example, with full downloadable source code would be a bit friendlier and easier to get your brain around.

With that, I decided to build out a very simple framework for a fairly typical application scenario.  I didn’t make the views complex, nor did I do work to add graphics or anything fancy.  I wanted it to show just what is necessary to show how you might go about creating the application framework and all of the navigation that would be associated.

Some highlights of what the application contains:

  • A Login view that accepts a Username and Password
  • A Home view that has
    • A button for a menu (typically it would be a hamburger icon)
    • A Summary data item that when clicked navigates to a Details view
  • A slide-out left Menu that:
    • Is activated by a left-to-right swipe or clicking on the menu button
    • Has a button that, when clicked, navigates over to a Settings view

The application launches to a Login view.  Once logged in, you end up on a Home view that has a slide-out menu in the left and a “summary” item on the main view.

In the menu is a “settings” button that will take you over to a settings screen.  Clicking on the summary item in the Home View takes you over to a details view.

The source code for the example can be found in source control under the Examples/NavigationExample folder.

This sample required a couple updated to the IoC library, so you’d need to have 1.0.16215 as a minimum (which is what is in source control anyway).  You can get the IoC Library right from NuGet.

Xamarin Forms Navigation with OpenNETCF IoC

The OpenNETCF IoC library has been around for a long time.  I needed a DI container that was simple and fast that provided some cool features like event aggregation and module plugins.  I still use it heavily in every project I work on.

When I started working with Xamarin Forms, I still needed a DI container.  Now I know that Xamarin provides one, but I already know how to use OpenNETCF’s IoC and the syntax for it is pretty much muscle memory for me at this point.  I didn’t want to learn a new paradigm when the one I have solves the problem just fine and it will let me continue to use the same syntax on Windows desktop, Web APIs, Xamarin and still the occasional Windows CE project.

The lack of Reflection support in a PCL assembly forced me to rework things a bit, so I lost event aggregation and plug-ins, but it’s still a really simple and fast DI container that supports both singletons and named instances.

What was missing, however, was a navigation structure.  So I created one and added it to the Xamarin build of IoC.  Here’s a very, very quick overview of how you can use it.

The NavigationService class is the root of what you’ll you and it’s a static to make it easier to use.  I don’t want or need to create or find an instance of it every time I want to use it.

The first step is to create a View/ViewModel pair.  The View must derive from Page.  The ViewModel must implement the provided IViewModel interface (which simply implements INotifyPropertyChanged and nothing else at this point).  Basically it’s a near drop-in for any Xamarin Forms project.

So let’s assume I have a simple app with two pages: Home and Detail.

I’d create the following classes: HomeView, HomeViewModel, DetailsView and DetailsViewModel.  The Views are just the out-of-the-box Pages.  The ViewModels would simply derive from IViewModel and add in the PropertyChanged event (which you probably have already).

Setting things up for using the Navigation service is straightforward.  You simply register the View/ViewModel types.  Note that you don’t actually create any instances of the Views or ViewModels:


NavigationService.Register<HomeView, HomeViewModel>();
NavigationService.Register<DetailsView, DetailsViewModel>();

This registration tells the NavigationService the relationship so that when you need to display a View, it can ensure that the ViewModel also exists and then it can inject it as the BindingSource for you.

Once we’ve registered our Views and ViewModels, we can start up the app like this:


NavigationService.SetMainView<HomeView>(true);

Things to note here are that we don’t directly set the Application.MainView, we simply tell the navigation service the Type we want to use.  We also pass in true here as a parameter to tell the service we want it to wrap the Page in a NavigationPage for us.  If your MainView already derives from NavigationPage, you’d pass in false. Yes, this could probably be made automatic, but for now it’s not.

Now that you have you main page, you can navigate between pages forward and back.  For example, to go from the MainView to the DetailsView, you’d use this (true telling the NavigationService to show animation for the navigation):

 NavigationService.NavigateForward<DetailsView>(true); 

And if you’re on the DetailsView and you want to go back?  That’s as simple as this:

NavigationService.NavigateBack(true);

Google Analytics for Xamarin Forms

I recently needed to add Google Analytics support to a couple Xamarin Forms applications I am working on.  I was a bit surprised that I didn’t find anything that was simply ready-built out in NuGet – it seems like a lot of people would, or at least should, be using some form of analytics for their mobile applications.

Since I’m a fan of generating APIs, and since I like freely sharing things that aren’t part of core business functions, I wrote a simple PCL library, pushed the code out to Github and published it to NuGet.

I didn’t add everything that GA supports – I don’t currently need features for ECommerce or Social Interactions.  What it does provide it the ability to publish Events, Screen Views and Timing information.

First, you’ll need a GA account and then a Tracking ID for your application.  Once that’s done, you just spin up an AnalyticsService instance:

var analytics = new AnalyticsService("UA-XXXXXXXX-X", applicationName: "My Xamarin App");

That’s all there is to it.  You’re now ready to start tracking.  GA has the ability to track things in a “session”, which allows you to get a feel for all of the activities a user did during that session.  I typically start a session when the user opens the app or logs in, and close the session when they leave or log out.  It’s as simple as this:

analytics.TrackSessionStart();

// do stuff you want to track

analytics.TrackSessionEnd();

Tracking a screen view is just as simple:


analytics.TrackScreenView("HomePage");

If you want to track a specific event, let’s say a user clicking on a button click (Help, for example), you’d simply do something along these lines:


analytics.TrackEvent("user action", "button click", label: "help");

If you want to keep track of how long something takes – anything from data loads to how long a user sits on a page or whatever, you can track Timing like this:


analytics.TrackTiming("navigation", "Screen1", 5000);

While these are powerful, they do require you as a developer to do a fair bit of work to track everything you want.  To simplify that, I also added support for GA directly into the OpenNETCF IoC library’s NavigationService.  I’ll do a separate blog entry on how that works, but basically all you have to do is initialize tracking with your Tracking ID and the NavigationService will track all of your screen changes and timings for you with zero added code on your part.

Another ORM Update

One of the laws of software development is “A better way of finding bugs than testing is to release” and sure enough, that happened.  I rolled a release of the ORM yesterday, and no sooner than I did, someone hit a bug with the new connection pooling in the Compact Framework.  It turns out that the SqlCeConnection exposes a Disposed event in the CF, but that event never fires.  The connection pooling implementation relied on that event to know when to remove connections from the pool, and voila – a bug is born.  Is it a bug in the ORM, or a bug in the SqlCeConnection?  I’d say that’s debatable both ways, but since I only have control over one of them I made the fix where I could.

There’s a workaround checked into the source tree over on Codeplex (change set 107904).  This fix is *not* yet rolled into a release package (sure would be nice if that were automated).

ORM Updates

Generally speaking, the OpenNETCF ORM library gets worked on offline.  We use it extensively in our Solution Engine product and so the “working” copy of the code resides on a separate source tree.  The good part of this is that it’s always getting tested, extended and fixed.  The down side is that I have to manually merge those changes back to the public source tree so it doesn’t happen probably as often as it should.

At any rate, I did a large merge this morning and there are some cool new features that have been added, along with a long list of bug fixes.

The general bug fixes and enhancements are largely SQL Server related, since we did a large SQL Server push for a customer this year, and the ORM benefitted from that.

The new features are twofold:

1. I added some rudimentary Connection Pool support.  The ORM itself can now keep a pool of connections to your target data store and it will re-use them in a round-robin fashion.  It’s nothing overly complex, but if you’ve got a few different threads doing different work on the database, it improves performance.

2. I added what I call a “Recovery Service” because I couldn’t come up with a better name.  This doesn’t restore data, but instead it attempt to recover from failures when it can. It’s really useful for remotely connected data stores.  For example, if you are using a SQL Server or DreamFactory DSP and the server or network goes down, your data actions are going to fail and you have to add in a load of handling logic for that.  The Recovery Service is designed to help you out here.  It will look for those types of failure and cache your operations until the store comes back online, at which time it will then apply the changes.  Sort of a “store-and-forward” type of capability, but at the command level and in RAM.

The new release is over on CodePlex in source and binary format.

OpenNETCF Scanner Compatibility Library

Some days I think I have too much code “lying around”.  As you would expect from many years as a developer, I have utility libraries for all sorts of tasks.  Generally when I think something is likely to be useful for others I like to make it publicly available for anyone to use – just take a look at the list of Codeplex projects I admin.

This morning I saw a question on StackOverflow about intelligently detecting a platform and loading the proper binaries for it.  In this case it was specific to doing so with Windows Mobile barcode scanners.  I immediately thought, “hey, I have a library for that” and went to answer and give a link.  Except the link didn’t exist.  I never created the open source project for it, so the code has just been sitting here doing nothing.

Yes, this code is probably 5 years or more past it’s prime useful period due to the decline in use of Windows Mobile, but hey, I just used it on a project last week, so it’s got some life left in it.

So, here’s yet another open source library from my archive – the OpenNETCF Barcode Scanner Compatibility Library.

Row filtering in the ORM

For a while now we’ve had an unwanted behavior in our Solution Engine product. The larger the on-device database got, the longer the app took to load. To the point that some devices in the field were taking nearly 5 minutes to boot (up from roughly 1 minute under normal circumstances). This morning we decided to go figure out what was causing it.

First, we pulled a database file from a device that is slow to boot. It turns out that the database was largely empty except to about 50k rows in a log table where we record general boot/run information on the device for diagnostics.

At startup the logging service pulls the last hour of log information and outputs it to the console, which has proven to be very helpful in diagnosing bad behaviors and crashes. Looking at the code that gets that last hour of data, we saw the following:

var lastHourEntries = m_store.Select<SFTraceEntry>(a => a.TimeStamp >= selectFromTime);

Now let’s look at this call in the context of having 50k rows in the table. What it effectively says is “Retrieve every row from the SFTraceEntry Table, hydrate a SFTraceEntry class for each row, then walk through that entire list checking the TimeStamp field. If the TimeStamp is less that an hour old, then copy that item to a new list and when you’re done, return the filtered list.” Ouch. This falls into the category of “using a tool wrong”. The ORM supports FilterConditions that, depending on the backing store, will attempt to decipher into a SQL statement, index walk or something more efficient than “return all rows”. In this case, the change was as simple as this:

var dateFilter = new FilterCondition("TimeStamp", selectFromTime, FilterCondition.FilterOperator.GreaterThan);
var lastHourEntries = m_store.Select<SFTraceEntry>(new FilterCondition[] { dateFilter });

Getting Mono Process Info from a Mono App

Since a large amount of the work I tend to do is for embedded devices, and since I don’t like to have to visit deployed devices to restart an app whenever it may crash, a pretty common pattern I use is to create a watchdog application that periodically checks to see if the actual application is running and start it if it’s not.  It a bit more complex than that because I typically have support for intentional shutdowns and I like to log all restarts for diagnostics, but the general premise is pretty simple.  The app should always be running.  If it’s not, start it again.

In Mono (under Linux anyway) that task turns out to be a bit of a challenge.  Process.GetProcesses doesn’t work because rather than giving the actuall Application name like the .NET Framework does under Windows, Mono simply returns a Process with a ProcessName of “mono-sgen” for all of the Mono apps running.  I can’t differentiate between the Watchdog app, the target app and any other app that may or may not be running.

I ended up creating a new class I called LinuxProcess (for lack of a better name).  Ideally it would be a Process derivative, or even rolled back into the Mono source, but for now it’s stand-alone and feature-limited to what I needed for a Watchdog.  Full source is below (sorry about the length, but I prefer this over a zip, and it’s searchable and indexable).

using System;
using System.IO;
using System.Linq;

using Output = System.Console;
using System.Collections.Generic;

namespace System.Diagnostics
{
	public class LinuxProcess
	{
		private Process m_process;

        private LinuxProcess(string fileName)
        {
            m_process = Process.Start(fileName);
        }

		private LinuxProcess(int pid)
		{
			Id = pid;
			m_process = Process.GetProcessById(pid);

			if (m_process == null)
			{
				Output.WriteLine("GetProcessById returned null");
			}
		}

        public static LinuxProcess Start(string fileName)
        {
            return new LinuxProcess(fileName);
        }

        public bool HasExited
        {
            get { return m_process.HasExited; }
        }

		public void Kill()
		{
			m_process.Kill();
		}

		public static LinuxProcess[] GetProcessesByName(string processName)
		{
			return GetProcesses().Where(p => p.ProcessName == processName).ToArray();
		}

		public static LinuxProcess[] GetProcesses()
		{
			var list = new List<LinuxProcess>();

			foreach (var path in Directory.GetDirectories("/proc")) 
			{
				var d = Path.GetFileName(path);
				int pid;

				if (!int.TryParse(d, out pid))
				{
					continue;
				}
					
				// stat
				var stat = GetStat(pid);
				if (stat == null) continue;

				var proc = new LinuxProcess(stat.PID);
				proc.ProcessState = stat.State;

				// look for mono-specific processes
				if (stat.FileName == "(mono)")
				{
					// TODO: handle command-line args to the Mono app
					var cmdline = GetCommandLine(stat.PID);

					// cmdline[0] == path to mono
					// cmdline[1] == mono app
					// cmdline[1+n] == mono app args
					proc.ProcessName = Path.GetFileName(cmdline[1]);
				}
				else
				{
					// trim out the parens
					proc.ProcessName = stat.FileName.Trim(new char[] { '(', ')' });
				}

				list.Add(proc);
			}

			return list.ToArray();
		}

		private static Stat GetStat(int pid)
		{
			try
			{
				var statDir = string.Format("/proc/{0}/stat", pid);
				if (!File.Exists(statDir))
					return null;

				var proc = new LinuxProcess(pid);

				using (var reader = File.OpenText(statDir))
				{
					var line = reader.ReadToEnd();
					return new Stat(line);
				}
			}
			catch (Exception ex)
			{
				Output.WriteLine("Stat Exception: " + ex.Message);
				return null;
			}
		}

		private static string[] GetCommandLine(int pid)
		{
			// The command line arguments appear in this file as a set of null-separated strings, with a further null byte after the last string. 
			using (var reader = File.OpenText(string.Format("/proc/{0}/cmdline", pid)))
			{
				string contents = reader.ReadToEnd();
				var args = contents.Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
				return args;
			}
		}

		public int Id { get; private set; }
		public string ProcessName { get; private set; }

		public ProcessState ProcessState { get; private set; }
	}

	internal class Stat
	{
		internal Stat(string procLine)
		{
			try
			{
				var items = procLine.Split(new char[] { ' ' }, StringSplitOptions.None);

				PID = Convert.ToInt16(items[0]);
				FileName = items[1];

				switch (items[2][0])
				{
					case 'R':
						State = ProcessState.Running;
						break;
					case 'S':
						State = ProcessState.InterruptableWait;
						break;
					case 'D':
						State = ProcessState.UninterruptableDiskWait;
						break;
					case 'Z':
						State = ProcessState.Zombie;
						break;
					case 'T':
						State = ProcessState.Traced;
						break;
					case 'W':
						State = ProcessState.Paging;
						break;
				}
			}
			catch (Exception ex)
			{
				Output.WriteLine("Stat parse exception: " + ex.Message);
			}
		}

		public int PID { get; private set; }
		public string FileName { get; private set; }
		public ProcessState State { get; private set; }
	}

	public enum ProcessState
	{
		Running, // R
		InterruptableWait, // S
		UninterruptableDiskWait, // D
		Zombie, // Z
		Traced, // T
		Paging // W
	}
}