Monday, November 4, 2013

Using Xamarin Sharpie to build a Flurry assembly

The main project I work on is a series of Xamarin based apps for iOS devices.
To help track usage stats we use Flurry Analytics which gives us a handy dashboard showing us session usage, device & OS breakdown, error logs, etc.

With the iOS 7 release, Flurry required a update:







Flurry doesn't have a .Net assembly out-of-the-box, so we needed a way of converting the native library into .Net.

When we had added Flurry to our project ages ago, we started with the Flurry monotouch-bindings to get things started. However this time Flurry had changed something in their library and the monotouch-binding no longer worked correctly. Trying to run the app with the built assembly resulted in build errors including:
Undefined symbols for architecture armv7:
  "_SecItemAdd", referenced from:
      -[FlurryKeychainWrapper setData:forKey:] in libFlurry.a(libFlurry.a-armv7-master.o)
  "_SecItemCopyMatching", referenced from:
      -[FlurryKeychainWrapper dataForKey:] in libFlurry.a(libFlurry.a-armv7-master.o)
  "_SecItemDelete", referenced from:
      -[FlurryKeychainWrapper removeObjectForKey:] in libFlurry.a(libFlurry.a-armv7-master.o)

Missing "sec" stuff. Doh!
I'll walk you through what I had to do to sort this out.

What's changed in Flurry?

Before starting, I had a quick look at the release notes to see if anything jumped out:














Hmmm…. point 4…. a new "Security" framework was added. Best keep that in mind for when we do the linking later.

Building a Flurry assembly

Firstly, download the native SDK from Flurry. You will end up with the following files as part of the sdk:






Next, we will need to convert the ".a" native library into a .Net assembly. Head over to Xamarin and download Sharpie. This useful tool will parse the Flurry.h header file and output a C# binding definition.















Follow through the steps in the tool and you'll end up with a binding file which will include something similar to:
using System;
using MonoTouch.Foundation;

namespace FlurryAnalytics {

     public enum FlurryLogLevel : [unmapped: unexposed: Elaborated] {
          None = 0,
          CriticalOnly,
          Debug,
          All
     }
     [BaseType (typeof (NSObject))]
     public partial interface Flurry {

          [Static, Export ("appVersion")]
          string AppVersion { set; }

          [Static, Export ("getFlurryAgentVersion")]
          string GetFlurryAgentVersion { get; }

          [Static, Export ("showErrorInLogEnabled")]
          bool ShowErrorInLogEnabled { set; }

          [Static, Export ("debugLogEnabled")]
          bool DebugLogEnabled { set; }
        
          [Static, Export ("sessionContinueSeconds")]
          int SessionContinueSeconds { set; }

          [Static, Export ("secureTransportEnabled")]
          bool SecureTransportEnabled { set; }
          ….

Its done a pretty good job, but has just gotten a bit confused with the enum 'FlurryLogLevel'. I took that enum out of the binding file and added it to a new "extras.cs" file I created:
namespace FlurryAnalytics
{
     public enum FlurryLogLevel
     {
          None = 0,
          CriticalOnly,
          Debug,
          All
     }
}

I now had the following four files in the folder:






Next, we will need to give some build instructions to Xamarin to tell it how to handle building and linking the native library and the binding definition. To do that, we add an AssemblyInfo.cs with the following content:
using System;
using MonoTouch.ObjCRuntime;

[assembly: LinkWith ("libFlurry_4.2.3.a", LinkTarget.Simulator | LinkTarget.ArmV7, Frameworks = "SystemConfiguration Security", ForceLoad = true)]

The linker is going to be told to grab the libFlurry_4.2.3.a library, link in the simulator and armv7 targets (armv7 is for the devices) and include the SystemConfiguration framework. Notice also that we included the Security framework. That should take care of the build errors.

Lastly, lets take the monotouch-bindings 'makefile' and alter it to better match our needs:
BTOUCH=/Developer/MonoTouch/usr/bin/btouch

all: FlurryAnalytics.dll

FlurryAnalytics.dll: Makefile AssemblyInfo.cs FlurryAnalytics.cs libFlurry_4.2.3.a
     $(BTOUCH)  FlurryAnalytics.cs AssemblyInfo.cs extras.cs --out=$@ --link-with=libFlurry_4.2.3.a,libFlurry_4.2.3.a

You'll now have the following files:








Open up a terminal, go to the above folder and run the build by typing "make". A FlurryAnalytics.dll should appear in the folder. You can include this in your project and should be able to start using the Flurry magic.