Chapter 9. Third-party Libraries
Xamarin supports a subset of the .NET framework, but for the most part, it includes all the standard APIs you would expect in the .NET base class libraries. Because of this, a large portion of C#'s open source libraries can be used directly in Xamarin projects. Additionally, if an open source project doesn't have a Xamarin or portable class library version, porting the code to be used in a Xamarin project can often be very straightforward. Xamarin also supports calling native Objective-C and Java libraries, so we will explore these as additional means of reusing existing code.
In this chapter, we will cover the following:
- The Xamarin Component Store
- NuGet
- Porting existing C# libraries
- Objective-C bindings
- Java bindings
The Xamarin Component Store
The primary and obvious way to add third-party components to your project is via the Xamarin Component Store. The Component Store is fairly similar to NuGet, which we will cover later, except that the Component Store also contains premium components that are not free. All Xamarin components are required to include full sample projects and a getting started guide, while NuGet does not inherently provide documentation in its packages.
All the Xamarin.iOS
and Xamarin.Android
projects come with a Components
folder. To get started, simply right-click on the folder and select Get More Components to launch the store dialog, as shown in the following screenshot:
At the time of writing this module, there are well over 200 components available to enhance your iOS and Android applications. This is a great place to find the most common components to use within your Xamarin applications. Each component is complete with artwork. You might possibly need a demonstration video, reviews, and other information before purchasing a premium component.
The most common components
The most well-known and useful components are as follows:
- Json.NET: This is the de facto standard for parsing and serializing JSON with C#
- RestSharp: This is a commonly used simple REST client for .NET
- SQLite.NET: This is a simple Object Relational Mapping (ORM) to use when working with local SQLite databases in your mobile applications
- Facebook SDK: This is the standard SDK provided by Facebook to integrate its services into your apps
- Xamarin.Mobile: This is a cross-platform library to access your device's contacts, GPS, photo library, and camera with a common API
- ActionBarSherlock: This is a powerful
ActionBar
replacement for Android
Note that some of these libraries are native Java or Objective-C libraries, while some are plain C#. Xamarin is built from the ground up to support calling native libraries, so the Component Store offers many of the common libraries that Objective-C or Java developers would leverage when developing mobile applications.
You can also submit your own components to the Component Store. If you have a useful open source project or just want to earn some extra cash, creating a component is simple. We won't be covering it in this module, but you can navigate to http://components.xamarin.com/submit for full documentation on the subject, as shown in the following screenshot:
Porting existing C# libraries
Even though Xamarin is becoming a popular platform, many open source .NET libraries are simply not up to speed with supporting Xamarin.iOS
and Xamarin.Android
. However, in these cases, you are definitely not out of luck. Often, if there is a Silverlight or Windows Phone version of the library, you can simply create an iOS or Android class library and add the files with no code changes.
To help with this process, Xamarin has created an online service tool to scan your existing code and determine how far off a library is from being portable. Navigate to http://scan.xamarin.com and upload any *.exe
or *.dll
file to have its methods analyzed for cross-platform development. After the scanning process, you'll get a report of the porting percentage (how much your component / application is portable to all platforms: Android, iOS, Windows Phone, and Windows Store).
The following screenshot is a sample report of the Lucene .NET client library:
If the library is running a high percentage on portability, you should have a relatively easy time porting it to Android or iOS. In most cases, it can even be easier to port the library to Xamarin than Windows Phone or WinRT.
To illustrate this process, let's port an open source project that doesn't have Xamarin or a portable class library support. I have selected a dependency injection library called Ninject due to its usefulness and relationship to ninjas. You can find out more about the library at http://www.ninject.org.
Let's begin setting up the library to work with Xamarin projects as follows:
- First, download the source code for Ninject from https://github.com/ninject/Ninject.
- Open
Ninject.sln
in Xamarin Studio. - Add a new iOS Library Project named
Ninject.iOS
. - Link all the files from the
Ninject
main project. Make sure you use the Add Existing Folder dialog to speed up this process.
Tip
If you aren't familiar with GitHub, I recommend that you download the desktop client for Mac found at http://mac.github.com.
Now try to build the Ninject.iOS
project; you will get several compiler errors in a file named DynamicMethodFactory.cs
, as shown in the following screenshot:
Open DynamicMethodFactory.cs
and notice the following code at the top of the file:
#if !NO_LCG #region Using Directivesusing System; using System.Reflection; using System.Reflection.Emit; using Ninject.Components; #endregion /// *** File contents here *** #endif
It is not possible to use System.Reflection.Emit
on iOS due to Apple's platform restrictions. Luckily, the library writers have created a preprocessor directive called NO_LCG
(which stands for Lightweight Code Generation) to allow the library to run on platforms that do not support System.Reflection.Emit
.
To fix our iOS project, open the project options and navigate to the Build | Compiler section. Add NO_LCG
to the Define Symbols field for both Debug and Release in the Configuration drop-down menu. Click on OK to save your changes. Notice how the entire file is now highlighted with a light gray color in Xamarin Studio, as shown in the following screenshot. This means that the code will be omitted from being compiled.
If you compile the project now, it will be completed successfully and a Ninject.iOS.dll
file will be created, which you can reference from any Xamarin.iOS
project. You can also reference the Ninject.iOS
project directly instead of using the *.dll
file.
At this point, you might wish to repeat the process to create a Xamarin.Android
class library project. Luckily, Xamarin.Android
supports System.Reflection.Emit
, so you can skip adding the additional preprocessor directive if you wish.
Objective-C bindings
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.
Working with Objective Sharpie
First, download and install Objective Sharpie from http://tinyurl.com/ObjectiveSharpie, then perform the following steps:
- Download the latest Google Analytics SDK for iOS available at https://tinyurl.com/GoogleAnalyticsForiOS.
- Create a new iOS Binding Project named
GoogleAnalytics.iOS
. - Run Objective Sharpie.
- Select iOS 7.1 as Target SDK and click on Next.
- Add all of the header (
*.h
) files included with the Google Analytics SDK; you can find these in theLibrary
folder of the download. Click on Next. - Pick a suitable namespace such as
GoogleAnalytics
and click on Generate. - Copy the resulting
ApiDefinition.cs
file that was generated into your iOS binding project. - After a few seconds, your C# file will be generated. Click on Quit.
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:
Tip
At the time of writing this module, 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 asstatic
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 allowsnull
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 toXamarin.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 withModel
. 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:
- Change the default namespace of the project to
GoogleAnalytics
. This setting is found in the project options by navigating to General | Main Settings. - Add
libGoogleAnalyticsServices.a
from the SDK download to the project. - Add
using
statements forMonoTouch.Foundation
,MonoTouch.UIKit
, andMonoTouch.ObjCRuntime
at the top of theApiDefinition.cs
file. - Remove the multiple duplicate declarations of
GAILogLevel
. You might also wish to move enumerations to theStructsAndEnums.cs
file. - Remove the declaration for
GAIErrorCode
. - In the
SetAll
method ofGAIDictionaryBuilder
, rename theparams
parameter toparameters
, asparams
is a reserved word in C#. - Remove the duplicate declarations for
GAILogger
,GAITracker
,GAITrackedViewController
, and any other duplicate classes you find. - Go through any
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. - Remove all the
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: Add them to the
Frameworks
value on theLinkWith
attribute, delimited by spaces. - Weak Frameworks: Add them to the
WeakFrameworks
property on theLinkWith
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. - Dynamic Libraries: Libraries such as
libz.dylib
can be declared inLinkerFlags
. Generally, you drop the.dylib
extension and replacelib
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.
Working with Objective Sharpie
First, download and install Objective Sharpie from http://tinyurl.com/ObjectiveSharpie, then perform the following steps:
- Download the latest Google Analytics SDK for iOS available at https://tinyurl.com/GoogleAnalyticsForiOS.
- Create a new iOS Binding Project named
GoogleAnalytics.iOS
. - Run Objective Sharpie.
- Select iOS 7.1 as Target SDK and click on Next.
- Add all of the header (
*.h
) files included with the Google Analytics SDK; you can find these in theLibrary
folder of the download. Click on Next. - Pick a suitable namespace such as
GoogleAnalytics
and click on Generate. - Copy the resulting
ApiDefinition.cs
file that was generated into your iOS binding project. - After a few seconds, your C# file will be generated. Click on Quit.
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:
Tip
At the time of writing this module, 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 asstatic
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 allowsnull
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 toXamarin.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 withModel
. 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:
- Change the default namespace of the project to
GoogleAnalytics
. This setting is found in the project options by navigating to General | Main Settings. - Add
libGoogleAnalyticsServices.a
from the SDK download to the project. - Add
using
statements forMonoTouch.Foundation
,MonoTouch.UIKit
, andMonoTouch.ObjCRuntime
at the top of theApiDefinition.cs
file. - Remove the multiple duplicate declarations of
GAILogLevel
. You might also wish to move enumerations to theStructsAndEnums.cs
file. - Remove the declaration for
GAIErrorCode
. - In the
SetAll
method ofGAIDictionaryBuilder
, rename theparams
parameter toparameters
, asparams
is a reserved word in C#. - Remove the duplicate declarations for
GAILogger
,GAITracker
,GAITrackedViewController
, and any other duplicate classes you find. - Go through any
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. - Remove all the
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: Add them to the
Frameworks
value on theLinkWith
attribute, delimited by spaces. - Weak Frameworks: Add them to the
WeakFrameworks
property on theLinkWith
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. - Dynamic Libraries: Libraries such as
libz.dylib
can be declared inLinkerFlags
. Generally, you drop the.dylib
extension and replacelib
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.
Java bindings
In the same manner as iOS, Xamarin has provided full support for calling into Java libraries from C# with Xamarin.Android
. The native Android SDKs function in this way and developers can leverage the Android Java Bindings
project to take advantage of other native Java libraries in C#. The main difference here is that not a lot has to be done by hand in comparison to Objective-C bindings. The Java syntax is very similar to that of C#, so many mappings are exactly one-to-one. In addition, Java has metadata information included with its libraries, which Xamarin uses to automatically generate the C# code required for calling into Java.
As an example, let's make a binding for the Android version of the Google Analytics SDK. Before we begin, download the SDK from http://tinyurl.com/GoogleAnalyticsForAndroid. At the time of writing, the version of the Android SDK 3.01, so some of these instructions might change over time.
Let's begin creating a Java binding as follows:
- Start a new
Android Java Bindings Library
project in Xamarin Studio. You can use the same solution as we did for iOS if you wish. - Name the project
GoogleAnalytics.Droid
. - Add
libGoogleAnalyticsServices.jar
from the Android SDK to the project under theJars
folder. By default, the build action for the file will beEmbeddedJar
. This packs the jar file into the DLL, which is the best option for ease of use. - Build the project. You will get a few errors, which we'll address in a moment.
Most of the time you spend working on Java bindings will be to fix small issues that prevent the generated C# code from compiling. Don't fret; a lot of libraries will work on the first try without having to make any changes at all. Generally, the larger the Java library is, the more work you have to do to get it working from C#.
The following are the types of issues you might run into:
- Java obfuscation: If the library is run through an obfuscation tool such as ProGuard, the class and method names might not be valid C# names.
- Covariant return types: Java has different rules for return types in overridden virtual methods than C# does. For this reason, you might need to modify the return type for the generated C# code to compile.
- Visibility: The rules that Java has for accessibility are different from those of C#; the visibility of methods in subclasses can be changed. Sometimes, you will have to change visibility in C# to get it to compile.
- Naming collisions: Sometimes, the C# code generator can get things a bit wrong and generate two members or classes with the same name.
- Java generics: The use of generic classes in Java can often cause issues in C#.
So before we get started on solving these issues in our Java binding, let's first clean up the namespaces in the project. Java namespaces are of the form com.mycompany.mylibrary
by default, so let's change the definition to match C# more closely. In the Transforms
directory of the project, open Metadata.xml
and add the following XML tag inside the root metadata node:
<attr path="/api/package[@name='com.google.analytics.tracking .android']" name="managedName">GoogleAnalytics.Tracking</attr>
The attr
node tells the Xamarin compiler what needs to be replaced in the Java definition with another value. In this case, we are replacing managedName
of the package with GoogleAnalytics.Tracking
because it will make much more sense in C#. The path value might look a bit strange, which is because it uses an XML matching query language named XPath. In general, just think of it as a pattern matching query for XML. For full documentation on XPath syntax, check out some of the many resources online such as http://w3schools.com/xpath.
You might be asking yourself at this point, what is the XPath expression matching against? Return to Xamarin Studio and right-click on the solution at the top. Navigate to Display Options | Show All Files. Open api.xml
under the obj/Debug
folder. This is the Java definition file that describes all the types and methods within the Java library. If you notice, the XML here directly correlates to the XPath expressions we'll be writing.
In our next step, let's remove all the packages (or namespaces) we don't plan on using in this library. This is generally a good idea for large libraries since you don't want to waste time fixing issues with parts of the library you won't even be calling from C#. Note that it doesn't actually remove the Java code; it just prevents the generation of any C# declarations for calling it from C#.
Add the following declarations in Metadata.xml
:
<remove-node path="/api/package[@name='com.google.analytics .containertag.common']" /> <remove-node path="/api/package[@name='com.google.analytics .containertag.proto']" /> <remove-node path="/api/package[@name='com.google.analytics .midtier.proto.containertag']" /> <remove-node path="/api/package[@name='com.google.android .gms.analytics.internal']" /> <remove-node path="/api/package[@name='com.google.android .gms.common.util']" /> <remove-nodepath="/api/package[@name='com.google.tagmanager']" /> <remove-node path="/api/package[@name='com.google.tagmanager.proto']" /> <remove-node path="/api/package[@name='com.google.tagmanager.protobuf.nano']" />
Now when you build the library, we can start resolving issues. The first error you will receive will be something like the following:
GoogleAnalytics.Tracking.GoogleAnalytics.cs(74,74): Error CS0234: The type or namespace name 'TrackerHandler' does not exist in the namespace 'GoogleAnalytics.Tracking'. Are you missing an assembly reference?
If we locate TrackerHandler
within the api.xml
file, we'll see the following class declaration:
<class abstract="true" deprecated="not deprecated" extends="java.lang.Object" extends-generic-aware="java.lang.Object" final="false" name="TrackerHandler" static="false" visibility=""/>
So, can you spot the problem? We need to fill out the visibility
XML attribute, which for some reason is blank. Add the following line to Metadata.xml
:
<attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='TrackerHandler']" name="visibility">public</attr>
This XPath expression will locate the TrackerHandler
class inside the com.google.analytics.tracking.android
package and change visibility
to public
.
If you build the project now, it will complete successfully with one warning. In Java binding projects, it is a good idea to fix warnings since they generally indicate that a class or method is being omitted from the binding. Notice the following warning:
GoogleAnalytics.Droid: Warning BG8102: Class GoogleAnalytics.Tracking.CampaignTrackingService has unknown base type android.app.IntentService (BG8102) (GoogleAnalytics.Droid)
To fix this issue, locate the type definition for CampaignTrackingService
in api.xml
, which is as follows:
<class abstract="false" deprecated="not deprecated" extends="android.app.IntentService" extends-generic-aware="android.app.IntentService" final="false" name="CampaignTrackingService" static="false" visibility="public">
The way to fix the issue here is to change the base class to the Xamarin.Android
definition for IntentService
. Add the following code to Metadata.xml
:
<attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='CampaignTrackingService']" name="extends">mono.android.app.IntentService</attr>
This changes the extends
attribute to use the IntentService
found in Mono.Android.dll
. I located the Java name for this class by opening Mono.Android.dll
in Xamarin Studio's Assembly Browser. Let's take a look at the Register
attribute, as shown in the following screenshot:
To inspect the *.dll
files in Xamarin Studio, you merely have to open them. You can also double-click on any assembly in the References
folder in your project.
If you build the binding project now, we're left with one last error, which is as follows:
GoogleAnalytics.Tracking.CampaignTrackingService.cs(24,24): Error CS0507: 'CampaignTrackingService.OnHandleIntent(Intent)': cannot change access modifiers when overriding 'protected' inherited member 'IntentService.OnHandleIntent(Android.Content.Intent)' (CS0507) (GoogleAnalytics.Droid)
If you navigate to the api.xml
file, you can see the definition for OnHandleIntent
as follows:
<method abstract="false" deprecated="not deprecated" final="false" name="onHandleIntent" native="false" return="void" static="false" synchronized="false" visibility="public">
We can see here that the Java method for this class is public
, but the base class is protected
. So, the best way to fix this is to change the C# version to protected
as well. Writing an XPath expression to match this is a bit more complicated, but luckily Xamarin has an easy way to retrieve it. If you double-click on the error message in the Errors pad of Xamarin Studio, you'll see the following comment in the generated C# code:
// Metadata.xml XPath method reference: path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='CampaignTrackingService'] /method[@name='onHandleIntent' and count(parameter)=1 and parameter[1][@type='android.content.Intent']]"
Copy this value to path
and add the following to Metadata.xml
:
<attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='CampaignTrackingService'] /method[@name='onHandleIntent' and count(parameter)=1 and parameter[1][@type='android.content.Intent']]" name="visibility">protected</attr>
Now, we can build the project and get zero errors and zero warnings. The library is now ready for use within your Xamarin.Android
projects.
However, if you start working with the library, notice how the parameter names for the methods are p0
, p1
, p2
, and so on. Here are a few method definitions of the EasyTracker
class:
public static EasyTracker GetInstance(Context p0); public static void SetResourcePackageName(string p0); public virtual void ActivityStart(Activity p0); public virtual void ActivityStop(Activity p0);
You can imagine how difficult it would be to consume a Java library without knowing the proper parameter names. The reason the parameters are named this way is because the Java metadata for its libraries does not include the information to set the correct name for each parameter. So, Xamarin.Android
does the best thing it can and autonames each parameter sequentially.
To rename the parameters in this class, we can add the following to Metadata.xml
:
<attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='EasyTracker'] /method[@name='getInstance']/parameter[@name='p0']" name="name">context</attr> <attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='EasyTracker'] /method[@name='setResourcePackageName']/parameter[@name='p0']" name="name">packageName</attr> <attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='EasyTracker'] /method[@name='activityStart']/parameter[@name='p0']" name="name">activity</attr> <attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='EasyTracker'] /method[@name='activityStop']/parameter[@name='p0']" name="name">activity</attr>
On rebuilding the binding project, this will effectively rename the parameters for these four methods in the EasyTracker
class. At this time, I would recommend that you go through the classes you plan on using in your application and rename the parameters so that it will make more sense to you. You might need to refer to the Google Analytics documentation to get the naming correct. Luckily, there is a javadocs.zip
file included in the SDK that provides HTML reference for the library.
For a full reference on implementing Java bindings, make sure you check out Xamarin's documentation site at http://docs.xamarin.com/android. There are certainly more complicated scenarios than what we ran into when creating a binding for the Google Analytics library.
Summary
In this chapter, we added libraries from the Xamarin Component Store to Xamarin projects and ported an existing C# library, Ninject, to both Xamarin.iOS
and Xamarin.Android
. Next, we installed Objective Sharpie and explored its usage to generate Objective-C bindings. Finally, we wrote a functional Objective-C binding for the Google Analytics SDK for iOS and a Java binding for the Google Analytics SDK for Android. We also wrote several XPath expressions to clean up the Java binding.
There are several available options to use the existing third-party libraries from your Xamarin.iOS
and Xamarin.Android
applications. We looked at everything from using the Xamarin Component Store, porting existing code and setting up Java and Objective-C libraries to be used from C#. In the next chapter, we will cover the Xamarin.Mobile
library as a way to access a user's contacts, camera, and GPS location.