Hybrid OR Native App development with JavaScript

When I started planning to write this article the idea was to do some VS comparison of both Apache Cordova and NativeScript. But while I was writing notes for this article I figured that it’s not only the technical aspect for comparing any technology, sometime it’s business requirement which override the technical aspect, also when we are comparing 2 technology stack it’s really helpful if you knows what are the pros and cons of two different tech stack, sometimes it’s not the end result which matters more but the overall process using which you arrive at the end result. So I have changed it from VS to OR.

cordova ORphoto

When we talk about process for creating any kind of projects, it involves both business (all the entities who are involved in human resource process or money aspect of project), there are certain decision which are taken at business level so if they are more clear on problems involved upfront they can take better decision. Technical (developers who will bring the app/project to life), will go through some technical details so that developer can take a better decision on whether to go for Hybrid model using ApacheCordova or NativeScript based upon their strength and technical expectations from the project.

We can broadly define mobile app development in 4 different categories:

  1. Native Apps: using pure languages such as Java, Objective C, Swift or C#, entry level is high, cost of development is high and startup cost can be high based upon availability of good skilled developer in pure languages.
  2. Cross – Compiled: You create your application in language like C# and then using a tool it will compile the same code to native mobile code example Xamarin. (Not purely sure how this works you can visit this website to learn more about it).
  3. Web Based Hybrid model: It’s a kind of hybrid app development technology where app is deployed in container app not on Native Layer tools and languages you need to create web based hybrid apps are PhoneGap/Apache Cordova, Supersonic, Ionic, Bootstrap, AngularJS, jQuery Mobile. Basic requirement to create app is you should know HTML5, JavaScript and CSS (all the skills which a web site developer has to know). So this hybrid model of development brings down the entry level barrier for mobile app developers. Will discuss the pros and cons in detail later.
  4. Native App development using JavaScript runtime: Mobile app is created in JavaScript and runs in JavaScript Virtual Machine. Some of the examples are Appcelerator Titanium, Facebook react Native, Telerik NativeScript. Both Appcelerator Titanium and Facebook React Native use API wrapper to do the communication between App Code and Native Layer. NativeScript doesn’t use any wrapper and its open source as well. Will focus on only NativeScript in this model.

Focus of this article is last 2 ways of creating mobile apps. Both model of app creation requires a developer with good JavaScript knowledge i.e. the better you know how JavaScript works/behave the better app you can create on performance level).

Let’s first understand the technical aspect of both Hybrid and Native App development using JavaScript:

Hybrid App development or Web Based app development using JavaScript:

  • User interface is created and styled through HTML5 and CSS3 and JavaScript
  • An instance of platform-specific browser (Web View) is embedded within the application and is used to visualize the HTML and CSS
  • Cordova (Phone gap) provides a “native bridge” to access the native resources on the device.
  • Cordova plugins are used to provide access to some of the native APIs in the underlying platform, most of the dynamic functionality or plugin code is handled using JavaScript.
  • Resulting applications are hybrid meaning that they are neither truly native mobile application (as all layout rendering is done via Web views instead of platform’s native UI framework) nor purely Web-based (as they are not just web-apps, but packaged as apps for distribution and have access to native device APIs).
  • User Input is handled by the embedded browser instance and delegated further as JavaScript events to the currently focused DOM element.
    • WebView :-
    • WebView is a chromeless browser which is configured to run in full screen mode.
    • All hybrid mobile app frameworks rely on standard implementation of WebViews for the app presentation tier.
    • There are different types of WebViews available depending on the framework we choose.
    • The System WebView is a native component provided by the operating system to be able to load web content

NativeScript App development using JavaScript:  NativeScript is unique because it allows you to access the native elements of the host platform via JavaScript. It allows access to full operating system libraries and third-party libraries from JavaScript code.

  • User interface is created using XML based layout language and styled through stripped down version of CSS3.
  • All business logic such as data models, view models/controllers etc. can be written in JavaScript.
  • NativeScript has set of libraries that enables calling APIs in the Android and iOS framework using JavaScript code.
  • It’s built upon several major parts including a JavaScript virtual machine to interpret and execute the JavaScript code and a “bridge” module is included to translate the calls to underlying platform specific APIS or marshaling service that handles data transfer to/from JavaScript to/from native layer.
  • Virtual machine used on Android is Google’s V8 and on iOS 7.0+ is WebKit’s JavaScriptCore.

NativeScript Apps vs Hybrid Apps:

  • Major difference between the two types of app creation is UI Stack and User Input.
  • UI are instances of the corresponding native widgets/visual – e.g. android.widget.Button on android or UIKit.UIButton on iOS whereas in Hybrid apps UI instances are HTML5 instances which are rendered based upon HTML5 rendering in the WebView supported on particular platform.
  • NativeScript Runtime provides full access to the underlying native APIs such as Camera, Location, File System etc. In Hybrid app development access to native APIs is through Cordova Plugins and HTML5 feature set’s available in the particular WebView in the platform.
  • User Input is handled by native handlers (delegates) declared in JavaScript – e.g. View.OnClickListener or UIControl.addTarget implementation whereas in Hybrid mode it’s all being handled by the WebView.
  • Ease of development if you are more at ease with development for websites using desktop browser with your editor where you make a change in editor and you can see the changes using some livesync or real time update of view in browser, or you can use desktop browser to tweak/make UI changes with dev. tools in your Chrome/Firefox browser. You can do almost all those things if you are creating your app using phonegap.
  • With Nativescript you can debug your app and push the changes using a tns livesync command with –watch mode but the only problem I found was I was not able to make real time UI updates or I was not able to debug the app in browser view using chrome:\\inspect so you have to be bit extra careful with your UI design.
  • If you are familiar with MXML (UI schema used by adobe to create Flex) or XAML a schema used by Microsoft to for creating UI interface for Silverlight apps. You will be at ease with the XML schema Nativescript is using for its UI layer. Or if you have done any development for Android using Java then you are good. In Phonegap you can create your UI layer same way as you would do for your desktop website. The only catch from my existing experience with doing that is the markup schema for desktop web apps is bit complex and you should avoid mapping exact same schema for mobile app it drag down the speed.

Performance:

  • NativeScript implementation: NativeScript is adding additional layer of abstraction via JavaScript code interpretation and data marshaling. So it will definitely not going to be as fast as apps created in pure Native code directly. But it’s definitely faster than hybrid apps.
  • Hybrid implementation:
    • Use of web-based technologies leads some Apache Cordova applications to run slower than native applications with similar functionality.

Adobe Systems warns that application maybe rejected by Apple for being too slow or not feeling ‘native’ enough. This can be issue for some Apache Cordova Applications.

Business aspect of creating mobile apps using either of model: For business (especially small organizations) the call is always if more developers are available to work in particular technology or not. In this case both models (NativeScript or Hybrid model) requires good knowledge of JavaScript.

If you have some good UI developers who can create nice optimized mobile UIs in HTML/CSS3 for you then you have good combination of team which can create good looking apps for you using Apache Cordova/Hybrid model of mobile app development as the learning curve in moving from desktop website development to mobile app development is not that steep. Same team can create mobile apps using Apache Cordova framework but focus has to be on optimized UIs as my experience with Cordova apps suggest that most of the time it’s the bloated UIs which drags the performance of the app and make it unusable in most of the cases.

In case of NativeScript as the UI is not HTML5 based and you can’t use all the CSS3 functionalities your JavaScript developers has to be little more experienced with UI stuff as well so that they can use XML based UI and manage it through JavaScript. Learning curve here is bit high and your normal web development team can’t be put straight for mobile app development in NativeScript. Even if you are good with JavaScript as a Freelance consultant you have to learn a bit of XML based UI designing with all the stripped down CSS3 styling you can use, here if you are OLD Flex/Sliverlight developer or you have done some of mobile app development using XML UI creation for Android/iOS with native app development you are good to go for NativeScript mobile app development as well.

In this article I tried to cover the two mobile app development models and I tried to share my experience on how we should approach different model, it can vary from team to team or organization to organization. The idea is to know it better before you pick any tech stack  for development. All the Best with your mobile app development.

Phonegap/Cordova app– Reading and Listing phone contacts

This blog post is for explaining how we can list all the phone contacts in our android phonegap app. For details regarding phonegap phone-contacts API you can refer to npmjs page of plugin which list almost all the API’s you can use to extract out information from the device on which app is installed. I will try to cover some of the points which I wasn’t able to figure out from their help docs and had to debug my app in chrome using emulator.

In this sample app the targeted max Android SDK version is 23 and Cordova version to compile app is 6.1.1. With SDK version 23 and above it seems you have to prompt device user every time you want to fetch some information from his device. (If I will get some more information on that I will post how we can achieve that.)

Sample app for this app can be found here https://github.com/lakhanpalv/phone-contact-app. Assumption in this post are that you are already aware how to configure phonegap/Cordova app on windows machine with everything setup like android-sdk and Java sdk. (it’s possible the same code will work on iOS devices but never tried). Some of the 3rd party APIs I have used in my sample app which are not required for fetching contacts from android devices, such as Handlebar and jQuery. They are purely used for display and ease of manipulating markup or JavaScript objects you can do that with pure plain JavaScript.

Here are some learning:  You have to wait for deviceReady event being fired by app on launch and it is being handled by Cordova API. As I am using sample-phonegap app to start my project index.js file which comes with sample project already has that thing handled. You should initialize your application code only after deviceReady event is fired successfully. As there can be some plugins, and phonegap APIs which you want to use in your app code and if you don’t initialize your code after deviceReady event it’s possible that you will see some error and spend hours to figure out why it’s happening. Below is the listing code from index.js and I am initializing my application code in last line of function.

receivedEvent: function(id) {
        var parentElement = document.getElementById('get-phone-contacts');
        //var listeningElement = parentElement.querySelector('.listening');
        //var receivedElement = parentElement.querySelector('.received');

        //listeningElement.setAttribute('style', 'display:none;');
        parentElement.setAttribute('style', 'display:block;');

        console.log('Received Event: ' + id);
        MySampleApp.init(window, document, window.jQuery, Handlebars);
    }

 

code snippet of my sample application modular code. To make sure it doesn’t bleed any variable or method to global scope, and also this way it’s easier to initialize all of my application code when I want to, not when it get’s loaded in app in sequence.

var MySampleApp = {
    init: function (window, document, $, Handlebars) {

    }
};

Coming to real problem:

Phone contacts are stored under navigator object and you can supply some options parameters using ContactFindOptions() and supplying filter and other options as for example if you don’t want to fetch contacts which doesn’t have any number stored against them you can supply .hasPhoneNumber=true;(it seems to work only in Android, find detailed here) it will filter only contacts with number. You can limit extracted information for a contact by supplying information you you want to pick from contact for a user like in below snippet I am just trying to extract displayname, phonenumbers and emails of contact. You have to supply success callback and error callback methods in .find() method along with filter and options object.

var options = new ContactFindOptions();
      options.filter = "";
      options.multiple = true;
      //options.hasPhoneNumber = true;
      var filter = ["displayName", "phoneNumbers", "emails"];
      navigator.contacts.find(filter, onContactsFetchSuccess, onContactsFetchError, options);

Below is the code listing of success callback method, will explain it below code snippet:

var onContactsFetchSuccess = function(contacts) {
            for (var i = 0; i < contacts.length; i++) {
                if(contacts[i].displayName !== null){
                    if(contacts[i].phoneNumbers !== null){
                        for(var j=0; j<contacts[i].phoneNumbers.length;j++){
                            //select number only if it's mobile number
                            if(contacts[i].phoneNumbers[j].type === 'mobile'){
                                if(contacts[i].emails !== null){
                                    for(var k=0; k < contacts[i].emails.length; k++){
                                        phoneContactsData.push({'name' : contacts[i].displayName, 'number': contacts[i].phoneNumbers[j].value, 'email':contacts[i].emails[k].value});
                                        break;
                                    }
                                } else {
                                    phoneContactsData.push({'name' : contacts[i].displayName, 'number': contacts[i].phoneNumbers[j].value});
                                }
                                break;
                            }
                        }
                    }else{
                        if(contacts[i].emails !== null){
                            for(var k=0; k < contacts[i].emails.length; k++){
                                phoneContactsData.push({'name' : contacts[i].displayName, 'number': '', 'email':contacts[i].emails[k].value});
                                break;
                            }
                        }
                    }
                }
            }           
         };
var onContactsFetchSuccess = function(contacts) {
            for (var i = 0; i < contacts.length; i++) {
                if(contacts[i].displayName !== null){
                    if(contacts[i].phoneNumbers !== null){
                        for(var j=0; j<contacts[i].phoneNumbers.length;j++){
                            //select number only if it's mobile number
                            if(contacts[i].phoneNumbers[j].type === 'mobile'){
                                if(contacts[i].emails !== null){
                                    for(var k=0; k < contacts[i].emails.length; k++){
                                        phoneContactsData.push({'name' : contacts[i].displayName, 'number': contacts[i].phoneNumbers[j].value, 'email':contacts[i].emails[k].value});
                                        break;
                                    }
                                } else {
                                    phoneContactsData.push({'name' : contacts[i].displayName, 'number': contacts[i].phoneNumbers[j].value});
                                }
                                break;
                            }
                        }
                    }else{
                        if(contacts[i].emails !== null){
                            for(var k=0; k < contacts[i].emails.length; k++){
                                phoneContactsData.push({'name' : contacts[i].displayName, 'number': '', 'email':contacts[i].emails[k].value});
                                break;
                            }
                        }
                    }
                }
            }           
         };

 

Phone contacts and emails are stored as an array of objects in returned object, when you call contacts.find().

In my use case I am interested in getting all contacts who has either their emailid or mobile phone number and name as must.  Name of user is stored as “displayName” so I have started filtering the looped object at top with if name exist and then moved to phone number which is stored as “phoneNumbers” array of phone number objects. phone number object has “type” attribute which store information if number is ‘mobile’ or not. Similarly “emails” are also stored as array of email objects. I just restricted my information to one email so stopping my loop as soon as I get first email. Just ignore the phoneContactsData.push() line if you are not interested in storing and displaying the information as it’s specific to my use case i.e. not related to contacts extraction from device.

some of other lessons learned during this sample project creation and not related to phone contacts information extraction:

  • I was trying to limit plugin inclusion in config.xml for this sample project only to phone-contacts, but found some interesting conversation on the phonegap Google group (Removing unneeded features/plugins doesn’t have any effect on app performance https://groups.google.com/forum/?fromgroups=#!topic/phonegap/VBsWphi8ydA) not sure how authentic this information is though.
  • Create your application JavaScript code as a separate modular code so that you can initialize it when you receive onDeviceReady event and doesn’t bleed your code to global scope as well at the same time.
  • Best way to debug your phonegap/Cordova is to use android emulator and launch chrome://inspect/#devices in chrome browser where your launched emulator app will appear as soon as you compile your project and run it in emulator.
  • Using above way you can debug through your application code by stepping through each line and use console.log or all other features which you will normally use with your web app in chrome way better then weinre in which you simply can just put console.log() message to trace out the outputs at different steps, can’t really step through your code with that.

I hope this post is of some help in if not for phone contact listing, there are some other options I have explored in the code listed at github. Where I have tried to display the contacts as you can see in your Contacts options in phone with filter contact information option. I have used Handlebar to loop through my contacts object and then I have used jQuery/JavaScript to filter contacts and update contacts display accordingly.