Written by Bilgehan Işıklı, iOS Developer at Blesh
The idea of cross platform development tools is very appealing, I get it. Instead of dealing with two different development processes, you get to work on only one and Voilà, you have your Android and iOS applications. I mean, who doesn’t want that, you get your product for half the price. However, that is not the case most of the time: yes, for not-that-complex projects, you can accomplish this at a certain level, but things start to get ugly when you get your feet wet enough.
One of these ugly things, and probably the first thing which comes to mind of a native developer, is third party library usage. Nowadays, almost all native applications, competitive ones anyway, have several third party libraries used at some point during their development. Of course, all cross platform tools support third party library integration. You know how i know this? Because promoters say so. Yeah sure, there are some documentation, in some cases they are outdated for a few years, but how much things can change in a few years in mobile development, no worries… Kidding aside, let me tell you, third party library integration is definitely not a walk in the park, especially for the platform in scope of this blog post: Xamarin.
Framework Binding
As you know (probably) Xamarin is based on C#, so the first thing we need to do is to provide some kind of a bridge between C# and Objective-C so that Xamarin can understand and compile our framework. Xamarin named this all process as ‘binding’ and even provided a standard template in Xamarin/Visual Studio to begin with.
Upon creation, project structure looks like this:
There are two files here that should draw our attention first: ApiDefinition.cs and Structs.cs. These two files will introduce our Objective-C framework to C# world. In ApiDefinition.cs file, we should translate our Header file, all the methods and properties and in Structs.cs file, we should translate our Enumerations and stuff, if they exist. So how to translate? Xamarin documentation -supposedly- shows how to do that, however the extend of their examples and explanation is very limited and you may found yourself dumbfounded not knowing how to translate Objective-C specific notations like blocks etc. Thankfully, they offer a tool for this translation: Objective Sharpie. (https://developer.xamarin.com/guides/cross-platform/macios/binding/objective-sharpie/)
Objective Sharpie is basically a command line tool and Xamarin states that they did not put a UI on top this because that might give a wrong impression about this translation process is easy. But not to worry, you will not need Objective Sharpie more than one command (hopefully anyway).
After installing Objective Sharpie, open terminal and go to the directory your framework is located and run bind command with the parameters below. Note that you should use iOS sdk version installed on your mac.
Bilgehans-MacBook-Pro:~ $ cd Desktop/Xamarin/
Bilgehans-MacBook-Pro:Xamarin $ sharpie bind -sdk iphoneos10.3 BleshSDK.framework/Headers/Blesh.h -scope BleshSDK.framework/Headers/ -c -F .
If successful, you should see an output like this along with some warnings, if there are any:
Parsing 1 header files...
/Users//Desktop/Xamarin/BleshSDK.framework/Headers/Blesh.h:48:42: warning:
'UILocalNotification' is deprecated: first deprecated in iOS 10.0 - Use
UserNotifications Framework's UNNotificationRequest
[-Wdeprecated-declarations]
- (void) bleshReceivedLocalNotification:(UILocalNotification *) notification;
^
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/UIKit.framework/Headers/UILocalNotification.h:17:12: note:
'UILocalNotification' has been explicitly marked deprecated here
@interface UILocalNotification : NSObject<NSCopying, NSCoding>
^
Binding...
[write] ApiDefinitions.cs
Binding Analysis:
Automated binding is complete, but there are a few APIs which have been flagged with [Verify] attributes. While the entire binding should be audited for best API design practices, look more closely at APIs with the following Verify attribute hints:
MethodToProperty (1 instance):
An Objective-C method was bound as a C# property due to convention such as taking no parameters and returning a value (non-void return). Often methods like these should be bound as properties to surface a nicer API, but sometimes false-positives can occur and the binding should actually be a method.
Once you have verified a Verify attribute, you should remove it from the binding source code. The presence of Verify attributes intentionally cause build failures.
For more information about the Verify attribute hints above, consult the Objective Sharpie documentation by running 'sharpie docs' or visiting thefollowing URL:
http://xmn.io/sharpie-docs
1 warning generated.
Submitting usage data to Xamarin...
Submitted - thank you for helping to improve Objective Sharpie!
Done.
Now, you should be able to see a new file named ‘ApiDefinitions.cs’ created in the same directory with your framework. Ok, great. Objective Sharpie translated our header file for us, but what to do with this file? Should we copy this file directly into our binding project? Wait, the original file in the project is named ‘ApiDefinition.cs’, NOT ‘ApiDefinitions.cs’. Hmm, so do we copy the contents of this newly created file and paste into the original one? And to what extent? Everything? Including ‘using’ statements? The documentation does not really answer these questions. It turns out that we do have to copy the contents and paste into the original file, including ‘using’ statements with one exception: Objective Sharpie adds a ‘using’ statement with the name of framework, but this creates an error in binding project, delete this statement and it works.
For the second step, we should add our framework to the binding project. As you know, a framework file is just a file containing the library file with header files and etc. Extract the lib file inside the framework and add an ‘.a’ extension at the end before adding this into binding project so that Xamarin can get what it wants. In binding project, right click Native References > Add Native Reference and select your lib file. Run and check to see you have no errors.
If your framework has dependencies to other frameworks…
Mine has and i didn’t get any heads-up until i try to run and compile my Xamarin.iOS project with a device that something is missing. (By the way, if you only test with simulator, there is a good chance that you will not be notified at all, since during linking for simulator, compiler automatically ignores the references not founded.) Looking back at the documentation, there is little and less information about this topic. As it turns out, in binding project, there should also be a third file named ‘yourFramework.linkwith.cs’ with the following structure:
using ObjCRuntime; [assembly: LinkWith("BleshSDK.a", LinkTarget.Simulator | LinkTarget.ArmV7 | LinkTarget.ArmV7s, ForceLoad = true, Frameworks = "CoreTelephony Accelerate SystemConfiguration AudioToolbox CoreBluetooth CoreLocation AdSupport CoreText QuartzCore CoreGraphics")]
Examples in the documentation shows a case of only one referenced framework and i had to figure out the notation by myself after trying with several coma/semi colon combinations The curious case here is, the only references i know required by my framework was the first seven, but compiler kept giving missing references errors until i added the last three. If you encounter a similar situation with endless missing references errors, I think you don’t have any other option rather than to search which frameworks those missing references belong to.
How to use Binding Project
Binding project produces a .dll file, which (I think) can be imported into a Xamarin.iOS project to be able to use our framework. However, in this document, I will explain to use this binding project as a reference in Xamarin.iOS project.
In your Xamarin.iOS project, right click on your project, Add > Add Existing Project and select your binding project. After adding binding project to your project, double click on ‘References’ and in Projects tab, you should be able to see your binding project. Toggle this and click OK. From now on, you should be able to use your framework throughout the project.
How to use BleshSDK in Xamarin.iOS Project
From this part on, I will be explaining BleshSDK specific integration details. However you may keep on reading to see a first hand comparison of Objective-C and C# usage of the same library.
Our SDK uses local notifications so we will deal with AppDelegate to implement notification specific callbacks.
Objective-C:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. [Fabric with:@[[Crashlytics class]]]; [[Blesh sharedInstance] initBleshWithAPIUser:@"bilgehan" APIKey:@"dFdvrApH1R" integrationType:@"M" integrationId:@"" pushToken:@"" optionalKey:@""]; [[Blesh sharedInstance] setDidCloseCampaignView:^(NSString *actionValueType, NSString *actionValue) { }]; UILocalNotification* notification =[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]; if(notification){ [[Blesh sharedInstance] bleshReceivedLocalNotification:notification]; } return YES; } - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { [[Blesh sharedInstance] bleshReceivedLocalNotification:notification]; } - (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler { [[Blesh sharedInstance] bleshReceivedLocalNotification:notification]; }
C#:
publicoverridebool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { Blesh.SharedInstance.InitBleshWithAPIUser("bilgehan", "dFdvrApH1R", "M", "", "", ""); Blesh.SharedInstance.DidCloseCampaignView = ((actionValueType,actionValue)=>{ } ); if (launchOptions != null) { // check for a local notification if (launchOptions.ContainsKey(UIApplication.LaunchOptionsLocalNotificationKey)) { var localNotification = launchOptions[UIApplication.LaunchOptionsLocalNotificationKey] asUILocalNotification; if (localNotification != null) { Blesh.SharedInstance.BleshReceivedLocalNotification(localNotification); } } } returntrue; } publicoverridevoid ReceivedLocalNotification(UIApplication application, UILocalNotification notification) { Blesh.SharedInstance.BleshReceivedLocalNotification(notification); //base.ReceivedLocalNotification(application, notification); } publicoverridevoid HandleAction(UIApplication application, string actionIdentifier, UILocalNotification localNotification, System.Action completionHandler) { Blesh.SharedInstance.BleshReceivedLocalNotification(localNotification); //base.HandleAction(application, actionIdentifier, localNotification, completionHandler); }
One thing to note here is that when ReceivedLocalNotification and HandleAction is overriden, Xamarin automatically puts super class invocations which causes application to crash during run time, so make sure to delete or comment them out.
You can download binding project through this link:
https://drive.google.com/open?id=0BwcNi0RzIzXiM0VMZ0JqeGMxblk