Xamarin has developed a sophisticated system to call native Objective-C libraries from C# in iOS projects. The core of Xamarin.iOS
uses the same technology to call native Apple APIs in UIKit, CoreGraphics, and other iOS frameworks. Developers can create iOS binding projects to expose the Objective-C classes and methods to C# using simple interfaces and attributes.
To aid in creating Objective-C bindings, Xamarin has created a small tool named Objective Sharpie that can process Objective-C header files for you and export the valid C# definitions to add to a binding project. This tool is a great starting point for most bindings that will get your binding three-fourths of the way complete, and you will want to hand-edit and fine-tune things to be more C#-friendly in a lot of cases.
As an example, we will write a binding for the Google Analytics library for iOS. It is a simple and useful library that can track the user activities in your iOS or Android applications. At the time of writing, the version of the Google Analytics SDK was 3.10, so some of these instructions might change as new versions are released.
First, download and install Objective Sharpie from http://tinyurl.com/ObjectiveSharpie, then perform the following steps:
GoogleAnalytics.iOS
.*.h
) files included with the Google Analytics SDK; you can find these in the Library
folder of the download. Click on Next.GoogleAnalytics
and click on Generate.ApiDefinition.cs
file that was generated into your iOS binding project.You should have not received any error messages from Objective Sharpie during the process, and when finished, your screen should look like the following screenshot:
At the time of writing this book, Objective Sharpie does not work properly with Xcode 6.0 and higher. I recommend that you download Xcode 5.1.1 if you run into this issue. You can install two versions of Xcode side by side by renaming an existing one in Finder and installing a second one. You can find older Xcode downloads at https://developer.apple.com/downloads/index.action.
Now if you return to your binding project, you'll notice that Objective Sharpie has generated an interface definition for every class discovered in the header files of the library. It has also generated many enum
values that the library uses and changed casing and naming conventions to follow C# more closely where possible.
As you read through the binding, you'll notice several C# attributes that define different aspects about the Objective-C library such as the following:
BaseType
: This declares an interface as an Objective-C class. The base class (also called superclass) is passed in to the attribute. If it has no base class, NSObject
should be used.Export
: This declares a method or property on an Objective-C class. A string that maps the Objective-C name to the C# name is passed in. Objective-C method names are generally in the following form: myMethod:someParam:someOtherParam
.Static
: This marks a method or property as static
in C#.Bind
: This is used on properties to map a getter or setter to a different Objective-C method. Objective-C properties can rename a getter or setter for a property.NullAllowed
: This allows null
to be passed to a method or property. By default, an exception is thrown if this occurs.Field
: This declares an Objective-C field that is exposed as a public variable in C#.Model
: This identifies a class to Xamarin.iOS
to have methods that can be optionally overridden. This is generally used on Objective-C delegates.Internal
: This flags the generated member with the C# internal keyword. It can be used to hide certain members that you don't want to expose to the outside world.Abstract
: This identifies an Objective-C method as required, which goes hand in hand with Model
. In C#, it will generate an abstract method.The only other rule to know is how to define constructors. Xamarin had to invent a convention for this since C# interfaces do not support constructors.
To define a constructor besides the default one, use the following code:
[Export("initWithFrame:")] IntPtr Constructor(RectangleF frame);
This would define a constructor on the class that takes in RectangleF
as a parameter. The method name Constructor
and the return type IntPtr
signal the Xamarin compiler to generate a constructor.
Now, let's return to our binding project to finish setting up everything. If you compile the project at this point, you'll get a few compiler errors. Let's fix them one by one as follows:
GoogleAnalytics
. This setting is found in the project options by navigating to General | Main Settings.libGoogleAnalyticsServices.a
from the SDK download to the project.using
statements for MonoTouch.Foundation
, MonoTouch.UIKit
, and MonoTouch.ObjCRuntime
at the top of the ApiDefinition.cs
file.GAILogLevel
. You might also wish to move enumerations to the StructsAndEnums.cs
file.GAIErrorCode
.SetAll
method of GAIDictionaryBuilder
, rename the params
parameter to parameters
, as params
is a reserved word in C#.GAILogger
, GAITracker
, GAITrackedViewController
, and any other duplicate classes you find.Field
declarations and change [Field("Foobar")]
to [Field("Foobar", "__Internal")]
. This tells the compiler where the field resides; in this case, it will be included internally in our binding project.Verify
attributes. These are spots where Objective Sharpie was unsure of the operation it performed. In our example, all of them are fine so it is safe to remove them.One more error remains regarding Objective Sharpie not being able to generate C# delegates for methods that have callbacks. Navigate to the GAI interface and change the following method:
[Export ("dispatchWithCompletionHandler:")]void DispatchWithCompletionHandler ( GAIDispatchResultHandler completionHandler);
You will also need to define the following delegate at the top of this file:
public delegate void GAIDispatchResultHandler( GAIDispatchResult result);
After going through these issues, you should be able to compile the binding and get no errors. You could have read the Objective-C header files and written the definitions yourself by hand; however, using Objective Sharpie generally means a lot less work.
At this point, if you try to use the library in an iOS project, you would get an error such as the following:
Error MT5210: Native linking failed, undefined symbol: _FooBar. Please verify that all the necessary frameworks have been referenced and native libraries are properly linked in.
We need to define the other frameworks and libraries that the Objective-C library uses. This is very similar to how references work in C#. If we review the Google Analytics documentation, it says that you must add CoreData
, SystemConfiguration
, and libz.dylib
. Additionally, you must add a weak reference to AdSupport
.
Open libGoogleAnalyticsServices.linkwith.cs
that was created automatically nested underneath the *.a
file and make the following changes:
[assembly: LinkWith ("libGoogleAnalyticsServices.a", LinkTarget.ArmV7 | LinkTarget.ArmV7s | LinkTarget.Simulator, LinkerFlags = "-lz", Frameworks = "CoreData SystemConfiguration", WeakFrameworks = "AdSupport", ForceLoad = true)]
We added references to frameworks in the following ways:
Frameworks
value on the LinkWith
attribute, delimited by spaces.WeakFrameworks
property on the LinkWith
attribute in the same manner. Weak frameworks are libraries that can be ignored if they are not found. In this case, AdSupport
was added in iOS 6; however, this library will still work on older versions of iOS.libz.dylib
can be declared in LinkerFlags
. Generally, you drop the .dylib
extension and replace lib
with –l
.After these changes are implemented, you will be able to successfully use the library from iOS projects. For complete documentation on Objective-C bindings, visit the Xamarin documentation site at http://docs.xamarin.com/ios.
13.58.51.228