Objective-C library binding for Xamarin.iOS
Xamarin platform enables iOS application development. Developer has option to create application with either Xamarin.Forms approach or Xamarin.iOS. Besides which way you choose sometimes there is a need to use native libraries which are written in Objective-C. There are many open source projects available on GitHub connected, especially connected with custom controls. Of course developer can rewrite them to C# but why? There is a another great solution to access them from Xamarin.iOS.
This solution is called “Library binding” and enables binding between Objective-C and C#. Developer does not have to rewrite the whole library code then. In this article I would like to present how to do such binding.
What do I need to start?
- Xcode 7 or later with iOS API
- Xcode Command Line Tools installed
- Mac computer
- Visual Studio for Mac
- Latest version of Objective Sharpie
Before we start I would like to shortly described each tool used to create Objective-C binding library:
- Xcode Command Line Tools – contain many tools and libraries from Apple for developing software on OS X. In our case this tool provides tool called “make” to automate the process of creating .Fat library which contains all supported architectures (iOS device or simulator) of our library
- Objective Sharpie – command line tool (provided by Xamarin) that can assist in creating the definitions required to bind a 3rd party Objective-C library to C#
Choose project for binding
There are many open source projects on Github. For this sample I used YLProgressBar project available here. This is highly and fully customizable animated progress bar in pure Core Graphics.
Once you download the project, you can open it in Xcode and see its structure in Xcode Navigator:
For us in this case crucial folders are:
- YLProgressBar – this directory contains the Objective-C code for the project
- Frameworks – this directory contains all frameworks used in the project
Create a static library in Xcode
Next step is to add YLProgressBar source code into the Static Library. To do it we need to create “Cocoa Touch Static Library”. In Xcode select “File” -> “New project” and then “Cocoa Touch Static Library” as shown below:
Once you click “Next” you have to provide project name and organization info. I did it like on the screen below:
Once you click “Next”, choose location for your project and save it there. Newly created project structure should look like below:
Now we need to replace the code inside “.h” and “.m” files. You can either copy two files from original project and replace them in folder or just replace the source code in these files using Xcode. I just replaced files in folder. Once you do it you should see small “M” letter which informs that files were modified:
Once you have files replaced we need to check if our library using any Frameworks. Go to original project (downloaded from Github) and open “Frameworks” folder. You should see all frameworks used by the library. In our case there are three:
- Foundation framework
We need to include these frameworks in static library we created above. To do it select “Build phases” in Xcode:
Click “+” button in “Link Binary With Libraries” section. From the list below select all frameworks that are required (you can use search box):
Once you add them, structure should look like below:
Our static library is ready but this is not the end.
Create Fat Binary
To be sure that our library works on both – simulator and iOS devices we need to compile it against different architectures:
- armv7s, arm64 architectures for iOS devices powered by ARM
- x86 and x84_64 (named i386) for iOS simulator
Fat library with “.a” extension contains all supported architectures in one file. To create Fat library there are many tools but in our case we will automate this process with “make”. Once we installed the Xcode Command Line Tools, we also installed “make”, so that is the build system we will use here.
Open text editor and create new file called “Makefile” (it can be also named “makefile” or “MakeFile”). Below you can see ready-to-go scrript. Add below script to it:
XBUILD=/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild PROJECT_ROOT=./YOUR-PROJECT-NAME PROJECT=$(PROJECT_ROOT)/YOUR-PROJECT-NAME.xcodeproj TARGET=YOUR-PROJECT-NAME all: lib$(TARGET).a lib$(TARGET)-i386.a: $(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphonesimulator -configuration Release clean build -mv $(PROJECT_ROOT)/build/Release-iphonesimulator/lib$(TARGET).a $@ lib$(TARGET)-armv7.a: $(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphoneos -arch armv7 -configuration Release clean build -mv $(PROJECT_ROOT)/build/Release-iphoneos/lib$(TARGET).a $@ lib$(TARGET)-arm64.a: $(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphoneos -arch arm64 -configuration Release clean build -mv $(PROJECT_ROOT)/build/Release-iphoneos/lib$(TARGET).a $@ lib$(TARGET).a: lib$(TARGET)-i386.a lib$(TARGET)-armv7.a lib$(TARGET)-arm64.a xcrun -sdk iphoneos lipo -create -output $@ $^ clean: -rm -f *.a *.dll
Replace “YOUR-PROJECT-NAME” sections with the name of your project. In my case this is “YLProgressBar”:
XBUILD=/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild PROJECT_ROOT=./YLProgressBar PROJECT=$(PROJECT_ROOT)/YLProgressBar.xcodeproj TARGET=YLProgressBar all: lib$(TARGET).a lib$(TARGET)-i386.a: $(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphonesimulator -configuration Release clean build -mv $(PROJECT_ROOT)/build/Release-iphonesimulator/lib$(TARGET).a $@ lib$(TARGET)-armv7.a: $(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphoneos -arch armv7 -configuration Release clean build -mv $(PROJECT_ROOT)/build/Release-iphoneos/lib$(TARGET).a $@ lib$(TARGET)-arm64.a: $(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphoneos -arch arm64 -configuration Release clean build -mv $(PROJECT_ROOT)/build/Release-iphoneos/lib$(TARGET).a $@ lib$(TARGET).a: lib$(TARGET)-i386.a lib$(TARGET)-armv7.a lib$(TARGET)-arm64.a xcrun -sdk iphoneos lipo -create -output $@ $^ clean: -rm -f *.a *.dll
Please note that identations in the file are made with “Tab”. These are not white spaces. (lines with “$”, “-mv”, “xcrun” and “-rm”). Remember also to remove extensions from the saved file so system not recognize it as plane text file.
File should look like below:
Now open terminal with location set to folder which contains “Makefile” file:
Type “make” into terminal and see results listed. If you set everything correctly there should be information displayed: “BUILD SUCCEEDED”. There should be also files generated for different architectures (including Fat library “.a” file):
You can confirm the architectures within your Fat binary by using the following command:
xcrun -sdk iphoneos lipo -info libYLProgressBar.a
You should see information in terminal:
At this point our static library created with Xcode and Make command line tools is ready.
Xamarin.iOS Binding Project
Open Visual Studio, select “File” -> “New project” -> “Bindind Library”:
Type the name of the project. It does not have to be exactly the same like Objective-C static library:
Project should have structure like on the screen below:
Note two important files:
- ApiDefinition.cs – file will contain the contracts that define how Objective-C API’s will be wrapped in C#.
- Structs.cs – file will hold any structures or enumeration values that are required by the interfaces and delegates.
Now wee need to add our Fat library to “Native References” folder. Right click on it and select “Add Native Reference”:
Navigate to folder which contains “YLProgressBar.a” file and add it to project:
Done. Now we are ready to use Objective Sharpie Xamarin tool.
Using Objective Sharpie
Objective Sharpie is a command line tool (provided by Xamarin) that can assist in creating the definitions required to bind a 3rd party Objective-C library to C#. In this case I used Objective Sharpie to create the initial ApiDefinition.cs for the YLProgressBar project.
If you are curious about tools that Objective Sharpie exposes you can type “sharpie -help” in the terminal:
We need to get information about our current installed SDKs by entering the following command into the terminal: “sharpie xcode -sdks”:
As you can see in my case I am using “iphone 10.3” SDK. With this information I can use Objective Sharpie to generate “ApiDefinition.cs” and “Structs.cs” files which contains C# code describing native Objective-C library code.
We have to provide two important details for below script:
- Path to folder with “.h” file (in our case “YLProgressBar.h”)
- Namespace – in our case this is “YLProgressBar”
- Iphone sdk version
- Output – where two files (ApiDefinition.cs and Structs.cs) will be generated.
Type below command in terminal:
sharpie bind --output=/Users/daniel_krzyczkowski/Desktop/YLProgressBarXamarin_Binding --namespace=YLProgressBar --sdk=iphoneos10.3 /Users/daniel_krzyczkowski/Desktop/YLProgressBarXamarin/YLProgressBar/YLProgressBar/*.h
Once succeeded you should see two files generated in the folder you selected as output. Now you can either copy the code from each file and paste in original file in our solution or just remove initial files and add these newly generated. I removed them from solution to make sure that everything is replaced correctly:
Now add generated files:
Remember to do one more thing if you decide to remove and add new files. Build action should be set for each file:
- For “ApiDefinitions.cs” it will be “ObjcBindingApiDefinition”
- For “StrustsAndEnums.cs” it will be “ObjcBindingCoreSource”
Operations with Objective Sharpie are done.
Normalize the API Definitions
Objective Sharpie sometimes has an issue translating Delegates and some parts of code so we have to adjust definition manually.
Look on below pictures. There are some errors we need to eliminate manually:
As you can see Objective Sharpie has annotated the binding with “Verify” attributes. These attributes indicate that you should verify that Objective Sharpie did the correct thing by comparing the binding with the original C/Objective-C declaration. This is sign that you have to verify assigned type. In our case there should be “NSArray” instead of “NSObject”. The best way to do such verification is to open native “.h” file and check.
There is also type error. In enum here should be “long” type, not “nuint”. Once you verify all errors you can remove “Verify” attribute. You can read more about verify attributes here.
Rebuild project and check if it builds. Go to “Bin” => “Debug” folder and see if there is “YLProgressBar.dll” file generated:
If its here you can use it! We are done. Now you can just add this dll to Xamarin.iOS application project or create NuGet package with this binding library.
I created sample project to check if I can normally use library using C#. I added YLProgressBar.dll as reference. Everything works fine!
What’s more – I can use C#.
YLProgressBar myBar = new YLProgressBar(); myBar.Type = YLProgressBarType.Flat; myBar.ProgressTintColor = UIColor.Blue; myBar.Progress = 20; myBar.HideStripes = false; myBar.StripesOrientation = YLProgressBarStripesOrientation.Left; myBar.Frame = new CoreGraphics.CGRect(0, View.Frame.Height / 2, View.Frame.Width, 30);
Creating binding libraries for Xamarin.iOS is more complicated and time consuming process than binding Android library but it is worth it.
Xamarin provides such great option so even if there is some interesting library written with Objective-C we can use it with Xamarin.iOS application too.