Native HTML Apps with Xamarin

At the tail end of last year, my team was working on an Ember.js app. That would usually be pretty normal, but in this case, it was an Ember app running in Cordova utilizing massive amounts of offline support. We needed to support Android and iOS. Offline support meant downloading most of our users’ data, processing it, and storing it—all while keeping the UI responsive using the single Javascript thread. As the project progressed, it became clear that this tech stack was not going to hold up.

So how did we get to that point? Why Cordova? Well, Cordova is a cross-platform framework for building mobile web apps hosted in a native shell. And our client had an existing Cordova application. To avoid a major rewrite and overhaul of the design, and because we’d worked with Cordova in the past, we went with it.

Pain Train Arrives at the Station

Cordova does a decent job of building apps for both Android and iOS, but there are still a lot of rough edges. Cordova takes a very WebView-centric approach. The main vehicle for any app logic is the JavaScript run-time in the native web view.

In our app, we had to support full offline functionality. We needed to download, process, and store most of our users’ data locally on the device. Even if we used an SQLite plugin to offload the data storage, processing became unbearably slow. Because Cordova is so browser-centric, JavaScript and its one thread are your only option for computation. Any native pieces are mostly bolt-ons that need to be completely implemented in each of the native platforms you wish to support.

Next Stop, Xamarin Town

Using Xamarin with the HybridWebView addressed all of our problems with Cordova:

  • It’s performant; it compiles down to native, supports multiple threads.
  • It’s DRY; it implements most logic in C# with minimal amounts of native code specific to Android or iOS.
  • It gets access to C# and its features (Rx.net, async/await, type checking/inference).
  • It can still use our Ember.js app with very few changes to our JS/HTML/CSS.

What Does the Code Look Like?

Once set up, the interfaces to execute native code and subscribe to observables from JavaScript are quite nice. Here are a couple quick examples and all the code required to set it up on your own Xamarin project.


  [HasWebCallbacks]
  public class ExampleServiceObject
  {
    [WebCallback]
    public async Task<Point> GetCoord(JToken someArg)
    {
      // ... native integration / DB look up / etc
      
      // This will automatically be serailized back as JSON
      return new Point{X = 5, Y = 8};
    }

    [WebEvent]
    public async Task<IObservable<Point>> OnPositionChanged()
    {
      // ...
    }
  }

 


  import Xamarin from 'my-project/utils/xamarin-interop';

  // direct call
  var p1Point = Xamarin.call('ExampleServiceObject.GetCoord', "player1");

  // subscribe for updates
  this._positionSub = Xamarin.subscribe('ExampleServiceObject.OnPositionChanged',
    point => {
      console.log(point.x, point.y);
    }
  );
  // later on, destroy the subscription when you're done
  this._positionSub.destroy();

There is quite a bit of glue needed to make this setup work. There is a sample repo showing how to make this interoperability happen.

So, How’s the Project Doing?

After switching to Xamarin, the application’s performance problems disappeared. We’ve were able to use shared C# code for our new native components in the app, and development and testing became quicker and less frustrating. I’m glad we made the switch, and I’ll continue to use Xamarin for native mobile web apps in the future.

Conversation
  • Parashuram says:

    Did you try using web workers with Cordova when using the SQLite plugin ? That makes processing so much faster. Also note that you could use something like ACE (http://github.com/microsoft/ace) to make the UI native where you need it.

    • Shawn Anderson Shawn Anderson says:

      Hi Parashuram, thanks for the comment.
      The use of SQLite was only one of the native pieces we needed. We chose this approach to simplify future development of upcoming native plugins that we were schedule to write as well. Using the Cordova SQLite plugin with web workers would have definitely helped.

      I had not seen Ace; looks quite nice. It is very similar to the UI capabilities we ended up with (a blend of native and HTML). I’m curious: is all the code is still basically bound to the single Javascript thread?

  • Parashuram says:

    The default mode is still the main thread, but it can run on webworkers too. I am currently experimenting with running ReactJS on a worker with ACE, so that everything is offloaded to a worker thread – http://web-perf.github.io/react-worker-dom/

  • Abhay Mneon says:

    Hi Shawn,

    Thanks a ton for this great tutorial and git showing the a good-way to use HTML5 and Xamarin, which till now I though was only Cordova’s way to go.

    I have been looking into you solution, but I have few queries :-
    1) I am trying to make the HTML5 (JS) code interact with the Xamarin.Forms(XF) UI (for example, PopUp screen using XF ContentPage on click of HTML5 Button). It seem there is a need of some work around to make this call open a XF based Popup window, as there is no access to Xamarin ContentPage on the SampleService. Is there some common solution available?

    2) I see you have use SimpleInjector instead of the Xlabs.IoC.SimpleContainer/Autofac/Ninject is there any reason or performance improvement that you had seen over other DI’s?

    Thanks a ton (again).

  • Did you try using web workers with Cordova when using the SQLite plugin ? That makes processing so much faster. Also note that you could use something like ACE (http://github.com/microsoft/ace) to make the UI native where you need it.

  • Cal says:

    Hi Shawn,

    Do you think ReactJS will work with Xamarin as well in your opinion?
    Thanks for taking the time in answering this question.

    Regards,
    Cal

    • Hi Cal,

      React would work just as well. There was nothing in this setup that was particularly ember-specific. The key thing was having a web app to point the HybridWebView at and a JavaScript API for calling into the C# code – this could be done just as easily in react, such as in a redux saga.

      Drew

  • Comments are closed.