Crossing the native bridge to build apps with Javascript

We are building cross-platform apps with Titanium since it came out in 2009, it was a great journey and we contributed to the platform, the builder and also created some widgets.

For now, we found no real free and open-source competitors to Titanium (sorry @xamarin), but there are new players in town, so let’s see what they have to offer !

Hello NativeScript and React Native !

Why these two new libraries are coming out in 2015?

Well, the javascript ecosystem is evolving and we all know it (maybe too much). It is so popular that we try to build everything with it (website, server, IoT…) and mobile apps too ! Developers are looking for ways to share code across platforms or even between client and server, but it’s better if we can do it by using the same language and the same code !

Again, new tools? Seriously?

I know I know… but apart from Titanium, there was no competitors for building native apps with javascript and competition is always good, we will see what they come up to appeal the developers.

What is the challenge?

Nearly the same as building a mobile web app. Performance and UI. The rest is for the developer comfort.

Frameworks

Those three frameworks are SDK to build native apps for iOS, Android and Windows (only Titanium for now). They expose native api through the bridge that you can call via javascript. Your code is then interpreted at runtime via JSCore on iOS and V8 runtime on Android.

Titanium

Titanium

Platforms: iOS, Android, Windows phone (beta)

Company: Appcelerator

Titanium is the oldest framework of this post, since july 2015, it partially supports windows. The SDK is written in Objective-C, Java and C++ and exposes it via classes called Proxies. A proxy is a native classe exposing properties and methods to the javascript context.

It is actively maintained by Appcelerator, it is mature, has an IDE and a MVC framework called Alloy.

We built many apps with it and wrote some blog posts.

NativeScript

Native

Platforms: iOS, Android

Company: Telerik

Out since march 2015, NativeScript is an open source SDK and runtime. Build by Telerik, some paid solutions includes a web IDE or other service integration. SDK is written in javascript and the runtime is native.

Wait, what? SDK in Javascript? What kind of black magic is…

import enums = require("ui/enums");
import locationModule = require("location");
import common = require("location/location-common");

global.moduleMerge(common, exports);

class LocationListenerImpl extends NSObject implements CLLocationManagerDelegate {

    public static ObjCProtocols = [CLLocationManagerDelegate];
    static new(): LocationListenerImpl {
        return super.new();
    }

    private _onLocation: (location: locationModule.Location) => any;
    private _onError: (error: Error) => any   

    ...

Oh okay, this is just some Typescript class extending an Objective-C class.

enter image description here

We saw in the previous code sample that a common module was imported and merged with the exports, this is the official API exposed by the SDK and available for both platforms.

Metadatas for all native APIs are generated when building the app and it allows to call native functions from JS files.

React Native

React

Platforms: iOS, Android

Company: Facebook

Also released open source in march 2015 (and buzzing since). Based on the javascript library of the same name, React Native allows to develop iOS and Android mobile apps with the power of components.

Facebook posted twice on their blog, once for the iOS release (mars) and once for the Android release (september). I recommend these posts, it helps understanding the history and progression of the library.

Development architecture

MVC

With Titanium, we can use a framework on top of the SDK called Alloy. Generally speaking, it provides three paradigms:

  • Models provide the business logic, containing the rules, data and state of the application;

  • Views provide the GUI components to the user, either presenting data or allowing the user to interact with the model data;

  • Controllers provide the glue between the model and view components in the form of application logic.

Behind the scene, it uses Backbone for the models, views are built in XML from Titanium UI components and controllers are extending Backbone.Events. They also expose view components with an id in the var `$`. For example if you want to update a label with the id “title” you can write in your controller:

$.title.text = ‘My title’;

Controllers are just modules and can be exported and created from other controllers, which is great to split your app into reusable components.

NativeScript uses nearly the same architecture but with raw models, although they use quite a lot of the observable pattern in the examples.

var observableArrayModule = require("data/observable-array");

module.exports = new observableArrayModule.ObservableArray([
    { name : "Name 1"},
    { name : "Name 2"},
]);

React + Flux

React does not force any architecture as it is just a view library with components. I think that Flux is the way to go (see comparison of other flux-based library). The unidirectional data flow is great for the declarative programming approach used in React (because we only declare views with it’s own data).

More recently, Facebook introduced Relay and GraphQL, which seems nice but force to have a GraphQL server side implementation. I prefer the approach of React Transmit with promises. Both these libraries remove the gap between the component and its data by including the query.

Here is a sample code of a list view populated with data from a json webservice.

var AwesomeProject = React.createClass({
  getInitialState: function() {
    return {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
      loaded: false,
    };
  },

  componentDidMount: function() {
    this.fetchData();
  },

  fetchData: function() {
    fetch(REQUEST_URL)
      .then((response) => response.json())
      .then((responseData) => {
        this.setState({
          dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
          loaded: true,
        });
      })
      .done();
  },

  render: function() {
    if (!this.state.loaded) {
      return this.renderLoadingView();
    }

    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderRow}
        style={styles.listView}
      />
    );
  }
  
  ...
});

Dev tools and debug

IDE

Titanium has an official IDE called Appcelerator Studio integrated with many Appcelerator services such as Cloud Services (database as a service).

For all the frameworks you can use the IDE or text editor of your choice since they always provide CLI tools to build the app.

Live reload

Titanium has an official live reload implementation called LiveView, there is also a community one called TiShadow that we use quite a lot.

The NativeScript one is called LiveSync, unfortunately these tools are not really just reloading the code but restarting the app after syncing it. The React one is more efficient and transparent, we can also force the reload with cmd+R.

Debug

The three frameworks have at least runtime logs and console.log to debug variables.

Appcelerator Studio has a nice debug mode and handles breakpoints.

Appcelerator Studio debug

NativeScript and ReactNative use the chrome debug tools with remote debugging.

The implementation is more advanced for React as you can debug components individually with the help of React Developer tools or use the built-in UI inspector in the simulator.

React native debug menuReact native inspector

Styling

Titanium uses TSS (Titanium Style Sheet) which are file with a json like syntax. The property available are the properties of Titanium Object, for example if the view is a Button (Ti.UI.Button) with id login, you can set the styles like this:

"#login" {
  backgroundColor: red;
  width: 200;
  height: 50;
  title: "Login";
}

NativeScript try to be closer to CSS and handle a subset of rules that are next applied to the native object. A fancy feature is that we can import external files from a server.

Button {
    border-width: 1;
    border-style: solid;
    border-color: #034793;
    border-radius: 2;
}
TextField {
    background-color: #FAFAFA;
    border-width: 1;
    border-style: solid;
    border-color: #C7C7CC;
    border-radius: 2;
}

Same for React but implemented in plain javascript object (see the open source project css-layout). They choose to implement Flexbox for the layout, which is the future of layout in CSS. I struggled a bit with it but in the end it seems more powerful, and for @vdesdoigts our main designer, it was as easy as in CSS (see the doc on MDN).

StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  }
});

Conclusion

Write/learn once, deploy anywhere is valuable to a lot of people, it is valuable for us. There is no best solution and there is always tradeoffs, you have to find the one that fit the best to your needs. If you prefer stability, Titanium is the way to go, it is the first in place and has some great enterprise feature. If you value most the UI, React Native has already some awesome contributions since it’s mainly a view library adopted by developers and front end designers, it also has the biggest community right now. NativeScript is a nice alternative since Telerik is known to offer some great UI (but not for free), and if you plan to access a lot the bridge and connects to… native script :p.

Resources

blog comments powered by Disqus