Creating Xamarin.iOS bindings for a native library

This past week I've been checking out Appsee, a cool app user analytics tool. The app I'm integrating this into is built using Xamarin.iOS, which Appsee doesn't support out of the box. This means creating a C# binding for the Appsee static library. I got it working after some Googling, and I thought I'd put together a post documenting the process.

First head over to the Appsee site and grab the latest UIKit SDK.

Unzip it and you should see Appsee.framework. This is just a folder that contains the static library and required headers and files in a specific folder structure, making it easy to add to an Xcode project. Appsee.framework contains the static library, called Appsee, and the header file, called Appsee.h.

First, we need to add the .a extension to the library to indicate that it is a static library.

Next, we need to generate the API definitions. This is the part where we define the C# methods and properties that are going to be available to you. You can do this manually if you want, but a really quick way to do it is to use a tool from Xamarin called Objective Sharpie.

Download it and run it. Select the target version as appropriate. On the next screen, add the header file we saw earlier.

Enter a namespace that makes sense for you; I'm using Appsee here.

For the output file, I'm using the name ApiDefinitions.cs. You can use any name except Appsee. If the file and the interface have the same name, you're going to encounter errors down the line. The contents of the file look like this :

using System.Drawing;
using System;

namespace Appsee {

   [BaseType (typeof (NSObject))]
    public partial interface Appsee {

       [Static, Export ("start:")]
        void Start (string apiKey);

       [Static, Export ("stop")]
        void Stop ();

        ...

    }
}

It's a normal C# class, and the bits in square brackets point to the Objective-C class/method/property you're wrapping with the following C#.

Objective Sharpie does a pretty good job, but there's some cleanup left to do. There are a couple of properties with a Verify attribute above them.

[Static, Export ("debugToNSLog"), Verify ("ObjC method massaged into setter property", "/Users/kannan/Downloads/Appsee/Appsee.framework/Versions/A/Headers/Appsee.h", Line = 49)]
bool DebugToNSLog { set; }

Verify is an invalid attribute and will not compile. Objective Sharpie puts it there to draw your attention. In this case, it's because Obj-C setter methods were transformed into C# properties. This is alright, so go ahead and remove the Verify.

[Static, Export ("debugToNSLog")]
bool DebugToNSLog { set; }

There are also a few missing using statements. Add the following:

using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.ObjCRuntime;

If you have a look at Appsee's page about the SDK, it lists a few builtin frameworks that Appsee uses.

We need to add using statements for these as well, so go ahead and do that :

using MonoTouch.AVFoundation;
using MonoTouch.CoreGraphics;
using MonoTouch.CoreMedia;
using MonoTouch.CoreVideo;
using MonoTouch.SystemConfiguration;
using MonoTouch.CoreAnimation;

The final ApiDefinitions.cs file looks like this :

 1 using System.Drawing;
 2 using System;
 3 using MonoTouch.Foundation;
 4 using MonoTouch.UIKit;
 5 using MonoTouch.ObjCRuntime;
 6 using MonoTouch.AVFoundation;
 7 using MonoTouch.CoreGraphics;
 8 using MonoTouch.CoreMedia;
 9 using MonoTouch.CoreVideo;
10 using MonoTouch.SystemConfiguration;
11 using MonoTouch.CoreAnimation;
12 
13 namespace Appsee {
14 
15     [BaseType (typeof (NSObject))]
16  public partial interface Appsee {
17 
18         [Static, Export ("start:")]
19      void Start (string apiKey);
20 
21         [Static, Export ("stop")]
22      void Stop ();
23 
24         [Static, Export ("stopAndUpload")]
25      void StopAndUpload ();
26 
27         [Static, Export ("pause")]
28      void Pause ();
29 
30         [Static, Export ("resume")]
31      void Resume ();
32 
33         [Static, Export ("debugToNSLog")]
34      bool DebugToNSLog { set; }
35 
36         [Static, Export ("addEvent:")]
37      void AddEvent (string eventName);
38 
39         [Static, Export ("addEvent:withProperties:")]
40      void AddEvent (string eventName, NSDictionary properties);
41 
42         [Static, Export ("startScreen:")]
43      void StartScreen (string screenName);
44 
45         [Static, Export ("overlayImage:inRect:")]
46      void OverlayImage (UIImage image, RectangleF rect);
47 
48         [Static, Export ("userID")]
49      string UserID { set; }
50 
51         [Static, Export ("setLocation:longitude:horizontalAccuracy:verticalAccuracy:")]
52      void SetLocation (double latitude, double longitude, float horizontalAccuracy, float verticalAccuracy);
53 
54         [Static, Export ("locationDescription")]
55      string LocationDescription { set; }
56 
57         [Static, Export ("markViewAsSensitive:")]
58      void MarkViewAsSensitive (UIView view);
59  }
60 }

We need to prepare another file that defines how linking should happen. I'm going to call this file linkwith.cs.

using MonoTouch.ObjCRuntime;

[assembly: LinkWith ("Appsee.a", LinkTarget.Simulator | LinkTarget.ArmV7 | LinkTarget.ArmV7s, ForceLoad = true,
   Frameworks = "AVFoundation CoreGraphics CoreMedia CoreVideo QuartzCore SystemConfiguration")]

The bit that says Frameworks = "..." is important. It contains the list of native libraries we need to link against. Without it, the creation of the binding will go alright, but there will be compilation errors when the resulting DLL is added to your project.

Let's get the three files, Appsee.a, ApiDefinitions.cs, and linkwith.cs into one folder. I'm calling this Appsee.

Open up your terminal and navigate over to the folder.

$ cd /Users/kannan/Documents/Appsee

We use a tool called btouch, provided by Xamarin, to create the binding.

$ /Developer/MonoTouch/usr/bin/btouch -unsafe --outdir=tmp -out:Appsee.dll ApiDefinitions.cs -x=linkwith.cs --link-with=Appsee.a,Appsee.a

The location of the btouch executable might vary depending on your installation. The out argument defines the name of the output DLL as Appsee.dll.

Running the above in your terminal will create the DLL file.

In your mobile project, right-click References and Edit References.... Navigate to the location of your DLL and add it to your project.

Add a using statement for the namespace you defined earlier, and you're good to go!