Two people handshaking
Insights

Xamarin for Cordova – a bizarre idea?

By Robin Wiegand & Kerry Lothrop &

The main task set by the Cross-Platform .NET topic at the annual Zühlke Germany camp in Barcelona was how to combine Cordova and Xamarin to use them together. At first, this idea seems to be a bit crazy because both frameworks are trying to solve the same problem.

Xamarin as well as Cordova are cross-platform frameworks which were created to simplify the development of multi-platform mobile apps. Both with fundamentally different approaches but the same result: a native app bundle. 

Insight in brief

  • It is possible to use Xamarin and Cordova together to get the best of both worlds.
  • However, the tooling for this needs to be created on a per-project basis since there is no general solution available at this time.

While Xamarin offers the ability to write apps completely using the C# (or F#) programming language, Cordova is more of a native wrapper for web apps. If you want to learn more about both approaches, you can follow these links: Xamarin | Cordova.
But we identified use cases where there can be a real benefit to combining the two technologies. For example, if a customer wants to integrate his existing C# business logic into an existing Cordova app. One option would be the rewriting of all exisiting code in the common language. That would be mean investing substantial effort. A combination of Xamarin and Cordova could solve this problem really well.

Additional use cases could be:

  • Cordova projects, which need a lot of plugin code benefit directly from the possibility to write the code in C#, because the plugins can be reused over the different platforms.
  • Existing Xamarin plugins like Xamarin.Essentials can be reused in Cordova projects. Xamarin plugins are typically maintained better than Cordova plugins.
  • Easy porting of existing web apps with C# backends to mobile.
  • Using the possibilities of the entire .NET framework in a Cordova app.

That means?

Cordova provides two main features for app development:

  • A native web view, that presents the included web app to the user.
  • It allows the access and usage of native code snippets (e.g. Objective-C for iOS and Java for Android). These so-called Cordova plugins are used to access native platform features from the web app code. This allows accessing platform functionality that would normally not be accessible using web technology only. An example would be the fingerprint scanner plugin.

Some advantages of a combined solution of Xamarin and Cordova are:

  • Existing Cordova apps can be executed as usual.
  • Plugins can be written in C# (or F# and even Visual Basic .NET).
  • Existing C# code can be used together with the Cordova app.

Our approach

There are two approaches which can be used to accomplish the goal:

  • Including the existing Cordova library for iOS and Android through bindings into the platform-specific Xamarin projects.
  • Creating an own web view in Xamarin (maybe even with Xamarin.Forms) and "reimplementing" the Cordova functionality.

We chose the first attempt, because it is possible to reuse existing Cordova plugins this way. If you're interested in the other approach, you can read about it in Shawn Anderson's blog.
 

Architecture

To build apps based on Cordova the according libraries are needed. Those libraries are written in the native programming language of the respective platform and provide the Cordova functionalities to the app. We now want to include the Android as well as the iOS library into a Xamarin project. To accomplish this, a Xamarin binding project is needed for each platform. The binding project gets included into the respective Xamarin project afterwards (Android and iOS). The following image shows the proposed architecture:
 

Architecture

Cordova for Xamarin.Android

Cordova-Android binding

To create an Android binding project, the Cordova Android library is needed as a jar file. To build the needed jar file execute the following steps.
Install ant with the help of Homebrew:

Zühlke Engineering AG
1    brew install ant

Clone the Cordova Android project:
https://github.com/apache/cordova-android/tree/master/ 

Switch to the cordova-android/framework folder and execute:
Zühlke Engineering AG
1    ant jar

Through this, the file cordova-xxx.jar gets created. The built jar file can be found in the following directory: cordova-android/framework.

Now create a Xamarin Binding Project and use the previously created jar file. Change the build-action to EmbeddedJar as described in the documentation.

Xamarin.Android project

After the binding project has been created successfully, another project can be added to the solution. This time it's the Xamarin.Android project. To get access to the necessary Cordova functions, the previously built binding library gets referenced by the newly created Xamarin.Android project.
 

Screenshot of MainActivity and Project

Update the MainActivity.cs to the following code snippet, so the Cordova view gets executed on app start.

Zühlke Engineering AG

1

2

3

4

5

6

7

8

9

[Activity(Label = "app.droid", MainLauncher = true, Icon = "@mipmap/icon")]

public class MainActivity : CordovaActivity

{

   public override void OnCreate(Bundle savedInstanceState)

   {

       base.OnCreate(savedInstanceState);

       LoadUrl("file:///android_asset/www/index.html");

   }

}

After Cordova has been installed, the command:

Zühlke Engineering AG
1    cordova create hello com.example.hello HelloWorld

creates an example app. The command creates a raw Cordova app, which is missing the according platforms. Add your desired platform with the following commands:

Zühlke Engineering AG
1    cordova platform add android --save
Zühlke Engineering AG
1    cordova platform add ios --save

Copy the assets folder into the Xamarin project. This folder contains the whole web app (e.g. *.html and *.js files).

Zühlke Engineering AG
1    HelloWorld/platforms/android/assets

After importing the assets folder, change the BuildAction to AndroidAsset. This is the correct way to tell Xamarin how to handle imported files.
 

Screenshot of how to tell Xamarin how to handle imported file

Now it should be possible to build and run the project. The Cordova logo should be visible on the smartphone also.

Xamarin.Android Plugins

Plugins are used to communicate between C# code and the Cordova logic. With the plugin concept you could simply use any of your C# business logic inside of a Cordova app. If you want to access native features like the camera or file system, plugins are useful, too. The next steps will show you how to implement a simple Xamarin.Android Plugin.

Create a new class inside the Xamarin.Android project and inherit from CordovaPlugin.

Zühlke Engineering AG

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

public class TestPlugin : CordovaPlugin

{

    protected override void PluginInitialize()

    {

        base.PluginInitialize();

    }

 

    public override bool Execute(string action, CordovaArgs args, CallbackContext p2)

    {

        if (action.Equals("echo"))

        {

            var message = args.Get(0).ToString();

            Echo(message, p2);

            return true;

        }

        return false;

    }

 

    private void Echo(String message, CallbackContext callbackContext)

    {

        if (message != null && message.Length > 0)

        {

            callbackContext.Success(message + " from Xamarin!");

        }

    }

}

The Execute method can be invoked from the Cordova code afterwards.

Create a new file called testplugin.js in the folder Assets/www. This file is the Cordova equivalent to the previously created C# class and manages the communication from the Cordova side.

Zühlke Engineering AG

1

2

3

4

5

6

7

8

var TestPlugin = function() {

};

 

TestPlugin.prototype.echo = function(str, callback) {

    cordova.exec(callback, function(err) {

        callback();

    }, "TestPlugin", "echo", [str]);

};

The plugin can be used with the following sample code snippet:

Zühlke Engineering AG

var testPlugin = new TestPlugin();

 

testPlugin.echo("Good Evening", function(echoValue) {

    alert(echoValue);

});

The plugin is dedicated to transport the string Good Evening to the Xamarin code. The Xamarin code attaches from Xamarin to the string and returns the result. Afterwards the Cordova app shows an alert window with the respective result.

Cordova for Xamarin.iOS

Cordova iOS Binding

The iOS project needs a Cordova binding as well. To achieve that, the original iOS Cordova library is needed. Clone that library from the source https://github.com/apache/cordova-ios. To make things work with Xamarin.iOS we need to accomplish a tiny customization here. So open the file CDVCommandQueue.m and find the following line if (!([obj isKindOfClass:[CDVPlugin class]])) {. Change the code to:

Zühlke Engineering AG

if (!([obj isKindOfClass:[CDVPlugin class]]))

{

    NSLog(@"ERROR: Plugin '%@' not found, or is not a CDVPlugin. Check your plugin mapping in config.xml.", command.className);

}

After changing the code, a static library (which is needed for the binding project) can be built. Xamarin's documentation explains pretty well how to do that. Finally use the following makefile to create a fat binary:

Zühlke Engineering AG

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

XBUILD=/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild

PROJECT_ROOT=.

PROJECT=$(PROJECT_ROOT)/CordovaLib.xcodeproj

TARGET=CordovaLib

TARGETMV=Cordova

 

all: libCordova.a

 

libCordova-i386.a:

 $(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphonesimulator -configuration Release clean build

 -mv $(PROJECT_ROOT)/build/Release-iphonesimulator/lib$(TARGETMV).a $@

 

libCordova-armv7.a:

 $(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphoneos -arch armv7 -configuration Release clean build

 -mv $(PROJECT_ROOT)/build/Release-iphoneos/lib$(TARGETMV).a $@

 

libCordova.a: libCordova-i386.a libCordova-armv7.a

 xcrun -sdk iphoneos lipo -create -output $@ $^

 

clean:

 -rm -f *.a *.dll

Then, the binding project can be built. To make the binding work, we need the ApiDefinition.cs and Struct.cs. Both files were modified by us to fit the requirements.

Xamarin.iOS project

Like with the Android project, an iOS project is needed as well. So create a new Xamarin.iOS project and reference the previously built binding library. The steps are almost equivalent to the Android project.

In preparation, we already built a Cordova iOS app. Like before, we need the assets folder in our Xamarin.iOS project. This folder contains the web app inside the Xamarin project. The steps are just like in the Android project.

After the assets folder is copied into our project and the binding library is referenced, the class ViewController.cs needs some logic to run the Cordova app. So change the ViewDidLoad method as seen here:

Zühlke Engineering AG

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

public override void ViewDidLoad ()

{

     base.ViewDidLoad ();

 

     _cdv = new CDVViewController ();

     _cdv.Init();

     _cdv.WwwFolderName = "www";

     _cdv.StartPage = "index.html";

 

     var constraints = new []

        { NSLayoutAttribute.Top, NSLayoutAttribute.Bottom, NSLayoutAttribute.Left, NSLayoutAttribute.Right }

        .Select(attr => NSLayoutConstraint.Create(_cdv.View, attr, NSLayoutRelation.Equal, View, attr, 1, 0)).ToArray();

 

     Add(_cdv.View);

     View.AddConstraints(constraints);

}

If we run the project now, we should see the Cordova initial screen on the iOS device.

Xamarin.iOS plugin

To create a plugin, a respective class is needed inside the Xamarin.iOS project. This class needs to inherit from CDVPlugin, so Cordova can recognize it as a plugin. You can view our sample plugin below:

Zühlke Engineering AG

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

[Register("Sample")]

public class Sample : CDVPlugin

{

    [Export("SayHello:command")]

    public void SayHello(CDVInvokedUrlCommand command)

    {

        string commandParam = string.Empty;

        if (command.Arguments.Any())

        {

            commandParam = command.Arguments.First();

        }

 

        var res = CDVPluginResult.ResultWithStatusMessagesArray(CDVCommandStatus.Ok, (NSString)"Hello", (NSString)commandParam);

 

        var cdvVC = (CDVViewController)ViewController;

        var myNewCmd = new CDVCommandDelegateImpl(cdvVC);

        myNewCmd.SendPluginResult(res, command.CallbackId);

    }

}

This plugin will simply transport the Hello string, but it's sufficient as a proof of concept.

To register the plugin, add the following lines to your ViewDidLoad() method, inside the ViewController.cs class. Just after the line: _cdv.StartPage = "index.html";.

Zühlke Engineering AG

var samplePlugin = new Sample();

_cdv.RegisterPluginClassName(samplePlugin, "Sample");

 

var demo = _cdv.PluginObjects;

demo.Add((NSString)"Sample", samplePlugin);

 

samplePlugin.PluginInitialize();

samplePlugin.CommandDelegate = _cdv.CommandDelegate;

Code
Apache Cordova - device is ready

Conclusion

The main finding: It's definitely possible to use Xamarin and Cordova together. Although the integration is not very intuitive (we didn't expect it to be, either), the integration seems to be not very clean and a lot of manual adjustments are needed. Significant effort is still needed to make cordova platform add xamarin-android and cordova platform add xamarin-ios work out of the box. If you stick to the steps we showed you above you will likely still have to invest a lot of effort for each platform update. This should defintely be considered before planning a real world project.

But against all the odds, the combination of both technologies is possible and works pretty well in the end. Whether it is a good choice for your project depends on a lot of factors, including your team know-how, the platforms you are trying to support, any legacy code you might have, and other factors.
Please feel free to contact us if you have any questions!


 

Robin Wiegand Zühlke

Robin Wiegand

Expert Software Engineer
Contact person for Germany

Robin Wiegand is Expert Software Engineer in the field of mobile applications. Since 2014 he has been involved in the development of apps for iOS and Android devices. Since mobile apps rarely operate independently, his experience in cloud technologies benefits the entire development process. Due to his many years of experience with Xamarin projects (Xamarin.Native & Xamarin.Forms), Robin's current focus is in this area. Robin Wiegand holds a Master of Science degree in media computer science.

robin.wiegand@zuehlke.com +49 6196 777 54 356
Kerry Lothrop Zühlke

Kerry Lothrop

Principal Consultant

Kerry W. Lothrop is a Principal Consultant at Zühlke with over 20 years of experience in software development. He joined Zühlke in March 2008. As a consultant and tech lead, he accompanies projects from the initial vision to market success. His project projects typically connect things with each other, from cloud backends to mobile phones to IoT devices. Kerry holds a degree in Aerospace Engineering and was recognized as a Microsoft MVP (Most Valuable Professional) by Microsoft and as a Xamarin MVP by Xamarin. He is a frequent public speaker and runs a tech blog and podcast.

Kerry.lothrop@zuehlke.com +49 6196 777 54 770