Xamarin Bindings Libraries, working example part (3/3)

Xamarin Bindings Libraries, working example part (3/3)
February 23, 2022
So now, after we’ve described the Bindings Libraries creation process for Android and iOS, it’s time to show a real working example creation.

1.    Usercentrics SDK working example

In SABOMobile IT, we use Xamarin Bindings for Usercentrics In-App SDK. Usercentrics is a Consent Management Platform to get GDPR, CCPA, LGPD compliant for websites and apps. Our goal was to show consent dialog to user in our Xamarin.Androidand Xamarin.iOS app and store it in central consent storage for both web and apps.

Usercentrics guys offer their SDK written  on the Kotlin multiplatform, so the Android package is in the Kotlin language and the iOS package is in the transformed Objective-C language.

 

1.1.                 

Android bindings library

After all the steps described above, we’ve ended up with a Xamarin bindings library of Usercentrics for Android (Picture)

Whereas in the Additions folder, there are almost all missing empty implementations of interfaces created during transformation (which is performed during the build process), Usercentrics.cs is a partial class extending the original transformed com.usercentrics.sdk.usercentrics object with C# implementation of the Kotlin native IFunction event.  

Picture 8 – Structure of final Xamarin Android
bindings library for Usercentrics SDK

In the Jars folder,  you can see the original usercentrics-1.12.0.aar Android package (with Build Action set to “LibraryProjectZip”) and necessary third party .jar references used internally by the .aar package.For all .jar files, build action in Visual Studio is set to“EmbededReferenceJar”.

In the Transforms folder, EnumFields.xml and EnumMethods.xml are empty. Metadata.xml needs the following transformation to compile the Android Bindings Library without build errors:

{% c-block language="xml" %}
<metadata>
<attrpath="/api/package[@name='com.usercentrics.sdk.models.tcf']/class[@name='TCFFirstLayerDescription']/constructor[@name='TCFFirstLayerDescription' and count(parameter)=3 and parameter[1][@type='java.lang.String'] andparameter[2][@type='java.lang.String'] and parameter[3][@type='java.lang.String']]/parameter[@name='default']" name="name">defaultStr</attr>
<attrpath="/api/package[@name='com.usercentrics.sdk.models.tcf']/class[@name='TCFFirstLayerDescription']/method[@name='copy' and count(parameter)=3 and parameter[1][@type='java.lang.String'] andparameter[2][@type='java.lang.String'] andparameter[3][@type='java.lang.String']]" name="name">defaultStr</attr>
<attrpath="/api/package[@name='com.usercentrics.sdk.models.tcf']/class[@name='TCFFirstLayerDescription']/method[@name='defaultStr' and count(parameter)=3 and parameter[1][@type='java.lang.String'] and parameter[2][@type='java.lang.String'] and parameter[3][@type='java.lang.String']]/parameter[@name='default']" name="name">defaultStr</attr>
<attrpath="/api/package[@name='com.usercentrics.sdk']/class[@name='CategoryProps']/constructor[@name='CategoryProps' and count(parameter)=2 and parameter[1][@type='com.usercentrics.sdk.models.settings.UCCategory'] and parameter[2][@type='boolean']]/parameter[@name='checked']" name="name">isChecked</attr>
<attrpath="/api/package[@name='com.usercentrics.sdk']/class[@name='CategoryProps']/method[@name='copy' and count(parameter)=2 and parameter[1][@type='com.usercentrics.sdk.models.settings.UCCategory'] and parameter[2][@type='boolean']]/parameter[@name='checked']" name="name">isChecked</attr>
<attrpath="/api/package[@name='com.usercentrics.sdk']/class[@name='PurposeProps']/constructor[@name='PurposeProps' and count(parameter)=3 and parameter[1][@type='boolean'] and parameter[2][@type='boolean'] and parameter[3][@type='com.usercentrics.sdk.services.tcf.interfaces.TCFPurpose']]/parameter[@name='checked']" name="name">isChecked</attr>
<attrpath="/api/package[@name='com.usercentrics.sdk']/class[@name='PurposeProps']/method[@name='copy' and count(parameter)=3 and parameter[1][@type='boolean'] and parameter[2][@type='boolean'] and parameter[3][@type='com.usercentrics.sdk.services.tcf.interfaces.TCFPurpose']]/parameter[@name='checked']" name="name">isChecked</attr
<attrpath="/api/package[@name='com.usercentrics.sdk']/class[@name='SpecialFeatureProps']/constructor[@name='SpecialFeatureProps' and count(parameter)=2 and parameter[1][@type='boolean'] and parameter[2][@type='com.usercentrics.sdk.services.tcf.interfaces.TCFSpecialFeature']]/parameter[@name='checked']" name="name">isChecked</attr
<attrpath="/api/package[@name='com.usercentrics.sdk']/class[@name='SpecialFeatureProps']/method[@name='copy' and count(parameter)=2 and parameter[1][@type='boolean'] and parameter[2][@type='com.usercentrics.sdk.services.tcf.interfaces.TCFSpecialFeature']]/parameter[@name='checked']" name="name">isChecked</attr
<attrpath="/api/package[@name='com.usercentrics.sdk']/class[@name='StackProps']/constructor[@name='StackProps' and count(parameter)=2 and parameter[1][@type='boolean'] and parameter[2][@type='com.usercentrics.sdk.services.tcf.interfaces.TCFStack']]/parameter[@name='checked']" name="name">isChecked</attr>
<attrpath="/api/package[@name='com.usercentrics.sdk']/class[@name='StackProps']/method[@name='copy' and count(parameter)=2 and parameter[1][@type='boolean'] and parameter[2][@type='com.usercentrics.sdk.services.tcf.interfaces.TCFStack']]/parameter[@name='checked']" name="name">isChecked</attr
<attrpath="/api/package[@name='com.usercentrics.sdk']/class[@name='VendorProps']/constructor[@name='VendorProps' and count(parameter)=3 and parameter[1][@type='boolean'] and parameter[2][@type='boolean'] and parameter[3][@type='com.usercentrics.sdk.services.tcf.interfaces.TCFVendor']]/parameter[@name='checked']" name="name">isChecked</attr>
<attrpath="/api/package[@name='com.usercentrics.sdk']/class[@name='VendorProps']/method[@name='copy' and count(parameter)=3 and parameter[1][@type='boolean'] and parameter[2][@type='boolean']and parameter[3][@type='com.usercentrics.sdk.services.tcf.interfaces.TCFVendor']]/parameter[@name='checked']" name="name">isChecked</attr>
<remove-node path="/api/package[@name='com.usercentrics.sdk']/class[@name='ApiLabels']/constructor"/>
</metadata>
{% c-block-end %}

 

1.2. iOS Bindings library

We used this command to run the Sharpie Utility, which transforms the Objective-C header .h file to the corresponding ApiDefinitions.cs file (Picture).

{% c-block language="xml" %}
sharpie bind -sdkiphoneos14.5 ./Usercentrics.framework/Headers/Usercentrics.h -namespace Usercentrics -scope Usercentrics.framework/Headers -c -F
{% c-block-end %}

This means that we’ve  bound the library corresponding to the iPhone OS 14.5 version, specifying namespace to “Usercentrics”.

The resulting Structs.cs file is empty and ApiDefinitions.cs is a pretty huge file with almost 12 thousand lines of code definition.

Picture 9 – Structure of Xamarin iOS bindings library for Usercentrics SDK
Picture 10 – Preview of final ApiDefinitions.cs

 

1.3. Usercentrics SDK usage in Xamarin application

After we produce Xamarin bindings libraries both for Android and for iOS, we just wrap the SDK calls into platform specific services which implement the interface defined in the shared Xamarin.net standard project.

After this, we create our own UI for user consent interaction  which calls the Usercentrics SDK in the background for storing and refreshing consents in the cloud.  

 

Picture 11 - Final Usercentrics UI in Xamarin.iOS app

1.5. Conclusion

What we’ve learned in this article: Creation of a Xamarin Bindings Library gives us the freedom to use  .NET for native mobile apps, even if we have to use some native dependencies. The creation itself is a complex problem with platform / language specific pitfalls, but achieving this ability means a lot for Xamarin / MAUI developers. It raises their potential to work on any mobile apps related problem.

In SABOMobile IT, creation of the Xamarin Bindings Library enables us to offer requested functionality for our customers and allows us to fulfill all clients’ needs to the fullest.


Thank you for reading!
Luboš Brát, Senior software developer @ SABO Mobile IT

References used in article

1)     https://www.mono-project.com/docs/advanced/runtime/

2)     https://docs.microsoft.com/en-us/xamarin/get-started/what-is-xamarin

3)     https://www.xamboy.com/2020/07/20/creating-a-xamarin-binding-library-for-ios-and-android-part-1/    

4)     https://docs.microsoft.com/en-us/xamarin/android/platform/binding-java-library/

5)     http://java-decompiler.github.io/

6)     https://docs.usercentrics.com/#/in-app-sdk

7)     https://www.w3schools.com/xml/xpath_syntax.asp

8)     https://docs.microsoft.com/en-us/xamarin/cross-platform/macios/binding/objective-sharpie/

9)     https://debruyn.dev/2016/creating-a-xamarin.ios-binding-project-for-dummies/

10)  https://docs.microsoft.com/en-us/xamarin/xamarin-forms/deploy-test/hot-restart

 

Read previous: Xamarin Bindings Libraries, an Android part (1/3)

Read previous: Xamarin Bindings Libraries, and iOS part (2/3)

Share:
Luboš is an experienced .NET developer (web and mobile apps) very familiar with SQL and DevOps. He developed a lot of web applications used worldwide by industry and the academical world. He holds a bachelor’s degree in applied physics and astrophysics. Likes bowling, rides e-mountainbike, skis and plays the violin.

Article collaborators

SABO Newsletter icon

SABO NEWSLETTER

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

About SABO Mobile IT

We focus on developing specialized software for our customers in the automotive, supplier, medical and high-tech industries in Germany and other European countries. We connect systems, data and users and generate added value for our customers with products that are intuitive to use.
Learn more about sabo