Testing Phonegap/Cordova applications fast as hell in the device (with ionic framework)


Normally when we work with Phonegap/Cordova applications we work in two phases. First we develop the application locally using our browser. That’s “fast” phase. We change something within our code, then we reload our browser and we see the outcome. It isn’t different from a “traditional” web developing process. Normally I use the ionic framework. Ionic is great and it also provides us a good tool to run a local server. We just type:

ionic serve

And ionic starts a local server on port 8100 with our Cordova application, ready to test with the browser (it also opens the browser). That’s not the cool part. Ionic also starts a live reload server at http://0.0.0.0:35729 and adds the following snippet at the end of our index.html

<script type="text/javascript">//<![CDATA[
document.write('<script src="' + (location.protocol || 'http:') + '//' + (location.hostname || 'localhost') + ':35729/livereload.js?snipver=1" type="text/javascript"><\/script>')
//]]></script>

With this snippet our application will be reloaded when we add/remove something in our file tree (it runs a filesystem watcher in background).

But as I said before it’s the “fast” phase and sooner or later we will need to run the application in the real device. OK we’ve got emulators, but they are horrible. Android emulator is incredible slow. IOS one is faster but we need to redeploy the application again and again with each change. For example when we correct a silly bug we need to run the following command to see the application running on the device:

cordova run android --device

And it takes time (around 10 seconds). We’ve gone from the “fast” phase to the “slooooow” one. That means that I tried to avoid this phase until no remedy.

If you don’t use plugins you can let this “slow” phase to the end, only to see the behaviour in the device and fix customizations, but ir we use plugins (camera plugin, push notifications or things like that) we really need to test on the real device. Those kind of things doesn’t work in the browser or even with the emulator.

This “slow” phase droves me crazy, so I started to think a little bit about it. One Cordova app has two parts. The native one (java code in android and objective-c in ios) and the html/js part. We need to tell to our Cordova application where is the initial index.html. We usually do it in config.xml

<content src="index.html" />

But we can change this initial file and use a remote one. That’s the way to create a “native” app from and existing web application.

<content src="http://gonzalo123.com" />

According to this we can start a local server in our host and use this local web server. Even in our LAN (if our android/ios device is the LAN of course)

<content src="http://192.168.1.1:8100/index.html" />

But, what happens with the plugins? Plugins needs cordova.js file and this file isn’t in www folder. This file is generated when we build the application to a specific platform

platforms/android/assets/www/cordova.js

So, what’s the idea. The idea is:

  • Run a local server with (inoic serve for example)
  • Enable the fs watcher to restart the application when we change one file in the filesystem (inonic serve do it by default)
  • Build the application and install it in the real device
  • Use our local server to serve static files instead of build again and again the application with each change.

With this approach we only need to deploy the application to the real device when we want to add/remove one plugin. If we change anything in the static files (html, js, css) our app will be reloaded automatically. The “slow” phase turns into a “fast” phase.

How can we do it? It’s easy. In this example I suppose that we’re using one android device. If we use on iPhone we only need to change “adroid” to “ios”.

First of all we need to prepare our index.html to enable auto-reload. “ionic serve” do it automatically but it thinks that we’re going to use it with your host browser. Not with the “real” device. We can change it manually adding to our index.html (this snippet suppose that your host is 192.168.1.1 if it’s a different one use your local IP address):

    <script type="text/javascript">//<![CDATA[
    document.write('<script src="http://192.168.1.1:35729/livereload.js?snipver=1" type="text/javascript"><\/script>')
    //]]></script>

Now we change our config.xml to use our local server instead of device’s files:

<!--<content src="index.html" />-->
<content src="http://192.168.1.105:8100/index.html" />

Now we need to deploy the application to our device:

cordova run android --device

Each time we add/remove one plugin we need to redeploy to the device. But we need to keep in mind that our device will use the cordova.js from our local server, and not from its filesystem. “cordova run android –device” will generate the file to the platform and deploy them to the real device, but as well as we’re going to use this file from our local server (in www), we need to create a set of symlinks in our www folder.

(I’ve got one setUp.sh file with this commands)

cd www
ln -s ../platforms/android/assets/www/cordova.js
ln -s ../platforms/android/assets/www/cordova_plugins.js
ln -s ../platforms/android/assets/www/plugins
cd ..

Now can start the application’s server in our host with:

ionic serve --nobrowser

notice that we’re using –nobrowser. We’re using this parameter to not to open our local browser. We’re going to use de device’s Cordova’s Webkit one, and also if we open our browser it will crash because cordova.js is present now and our local host isn’t a real device.

Each time we need to redeploy the application to the device (new plugin for example) we need to remember to quit the symlinks, and redeploy.

(I’ve got one tearDown.sh file with this commands)

rm www/cordova.js
rm www/cordova_plugins.js
rm -Rf www/plugins 

And that’s all. I now that this little hack may looks like something difficult but we need less than a minute to set up the environment and we will save thousand of seconds in the development process. I we work a little bit we can automate this process and turn it into a trivial operation, but at least now I feel very comfortable.

Of course you need to remember to clean the project when you finish and use the device’s files. So we need to remove the auto-reload snippet in the index.html, remove symlinks and restore config.xml.

UPDATE:

ionic framework comes now with this feature out-of-the box. Just follow the instructions explained here:
http://ionicframework.com/blog/live-reload-all-things-ionic-cli/

Advertisements

About Gonzalo Ayuso

Web Architect. PHP, Python, Node, Angular, ionic, PostgreSQL, Linux, ... Always learning.

Posted on July 21, 2014, in AngularJS, cordova, phonegap, Technology and tagged , , , , , . Bookmark the permalink. 21 Comments.

  1. what is the difference between your solution and the application that provide phonegap for development http://app.phonegap.com/?, is the same behavior??

  2. Just to let you or anyone else knows who might come across this later – Ionic actually updated their tools to add livereload capabilities to device and emulator deploys.

    It’s as simple as ‘ionic run android -l’ or ‘ionic emulate ios -l’.

    http://ionicframework.com/blog/live-reload-all-things-ionic-cli/

    • Yes. Ionic cli upgrade is great. It doesn’t work properly (at least for me) and I’m still using this technique. Probably is my fault. New ionic cli version is awesome.

  3. Hi Gonzalo

    Thx for a great idea. This will save lots of (idle) time for me. I gave it a first try and for some reason the app didn’t reload (though the live reload triggers).

    I created a gulp script to simplify the process you described and eliminate the need to add and remove links. You can find it here:

    https://github.com/meiriko/gulpForCordovaQuickDebug

    If you type “gulp help”, you will even see your name mentioned 🙂

  4. Hi guys, great convo:

    Related question: On Android, with Ionic/Cordova, I’m changing my.com/app.js which is pulled in index.html

    However, it seems index.html itself doesn’t bother re-loading the index.html every time (even after app restart)

    How can I force index.html or otherwise get Ionic/AngularJS/JS to use latest from my.com/my.js — eg, controller.js?

    Thanks!

  5. Gonzalo, don’t know how to thank you. This is a very useful post. Ionic is extremely good. It now does all that you mentioned in this post. Many thanks.

  6. Hi i try , but in console log i received this message “liveReload disabled because the browser does not seem to support web sockets” you know how i fix that?

  7. in my app cordova not working in device , ho to create symbolic link in windows(CLI) for solving this error

  8. in device the content of cordova.js not getting and getting mock cordova (like in browser, response of cordova.js)

    plz help me to solve this error

  9. Marcelo de Oliveira

    Hi Gonzalo,

    Congratulations for your article… I will try do this here.

    But I have a question. I have a web server running in my windows (Wamp).

    My app has a back end webservice, so it is mandatory access my back end to do any test on my device.

    My localhost has php and mysql running at 8080 port and my local address is 192.168.1.102.

    I want to debug my app directly in my device, so I am using the command below:

    ionic run -c -l –address 192.168.1.102 –port 8080

    And I received those messages on mi cli:

    Setup Live Reload
    The port 8080 was taken on the host 192.168.1.102 – using port 8081 instead
    Running live reload server: http://192.168.1.102:35729
    Watching: 0=www/**/*, 1=!www/lib/**/*
    Running dev server: http://192.168.1.102:8081 <<<<<——————– ANOTHER PORT
    Ionic server commands, enter:
    restart or r to restart the client app from the root
    goto or g and a url to have the app navigate to the given url
    consolelogs or c to enable/disable console log output
    serverlogs or s to enable/disable server log output
    quit or q to shutdown the server and exit

    See that it is pointing to another port (8081). So, I can´t reach my backend service.

    If I change localhost port, the "ionic run" command steps 1 unit forward… If I define 8081, he goes to 8082 and so on…

    So, the question is how to set the correct port to make my app see back-end running in my device ?

    I suppose that I will need to configure a proxy but I have tried the fiddler and unfortunatelly without success.

    Could you tell me how can I do to do all those steps that you showed here and accessing my web server ???

    I am introducing audio capture in my app and I need to do a debug directly in my device.

    I will appreciate any help.

    Best regards,
    Marcelo Oliveira.

    • Marcelo de Oliveira

      Hi Gonzalo,

      After all your steps done, this is my situation:

      $ ionic serve –nobrowser

      Running live reload server: http://192.168.1.102:35729
      Watching: 0=www/**/*, 1=!www/lib/**/*
      Running dev server: http://192.168.1.102:8100
      Ionic server commands, enter:
      restart or r to restart the client app from the root
      goto or g and a url to have the app navigate to the given url
      consolelogs or c to enable/disable console log output
      serverlogs or s to enable/disable server log output
      quit or q to shutdown the server and exit

      This command starts my app on device and there I get the error:

      The connection to the server was unsuccessful. (http://192.168.1.102:8100/index.html)

      My localhost server is at port 8080.

      Isn´t necessary to configure a proxy ???

      Thanks,
      Marcelo.

      • Gonzalo Ayuso

        Yes. Proxy is the best solution. Otherwise you need to handle with CORS. This is a good article to explain how to it: http://blog.ionic.io/handling-cors-issues-in-ionic/

        Anyway you should use ionic run -l (with your backend via ionic’s proxy and your device attached with USB cable) this command does everything for you. Start server, start proxy, start live reload, change your config.xml to point to your server compile and pass the application to your device (with live reload). You only need to inspect devices with chrome and have fun 🙂

      • Marcelo de Oliveira

        Hi Gonzalo,

        Thanks for your last answer.

        In this article about CORS, there is this example:

        Set up your ionic.project file to be something like:

        {
        “name”: “proxy-example”,
        “app_id”: “”,
        “proxies”: [
        {
        “path”: “/api”,
        “proxyUrl”: “http://cors.api.com/api”
        }
        ]
        }

        And this one:

        angular.module(‘starter’, [‘ionic’, ‘starter.controllers’, ‘starter.services’])
        .constant(‘ApiEndpoint’, {
        url: ‘http://localhost:8100/api’
        })

        But I do not have this situation in my project. In my project, I need to call just my local webserver (192.168.1.102:8080/…./wsfile.php) from my device, debugging with the command line “ionic run -l”. My app only uses its back-end without any other api.

        Here is the way I am calling my webservice, when debugging with browser:

        $http({
        url: $scope.ws_url,
        method: ‘POST’,
        data: dataToSend
        }).success(function(response, status, headers, config) { …..

        where the variable $scope.ws_url is something like http://129.168.1.102:8080/…./wsfile.php.

        My problem is that I am not reaching my local webserver from my device.

        So, my doubts are:

        – How can I configure my ionic.project file ? (about CORS article)
        – Is it necessary to configure the angular constant ? (about CORS article)
        – Is it necessary configure a proxy in my Android device ? I am using a local wifi (about your article)
        – Is it necessary to configure the MainActivity.java file ? (about your article)
        – Is it necessary to configure the config.xml file ? (about your article)

        I have read your article here (Testing Phonegap/Cordova applications fast as hell in the device (with ionic framework) and made all the changes, but I am still getting error.

        I have tried using Proxy Fiddler and still the same error…

        So, if possible, could you show me the step by step (using proxy now), to resolve this problem ?

        Best regards,
        Marcelo.

      • Gonzalo Ayuso

        To avoid CORS problems when you run ionic serve (or ionic run android -l) you cannot use your real backend. Your constant ApiEndpoint is wrong. you must use /api. Thats means your application will use the ionic server for every request (aka remote === origin => no CORS problems). You only need to set up proxyUrl within your ionic.projet with your backend url and use only /api in your application.

        Angular constants are very useful here to set up the api endpoint. You only need to remember to use the real api endpoint instead of /api when you build the release application. You never need to change MainActivity.java.

        You also don’t need to change config.xml (at least to change api endoint). ionic cli does this job for you.

  10. Gonzalo,
    If I’m understanding you correctly, when running “ionic serve” or “ionic run android”, it uses the “proxyUrl” set in the ionic.config.json file, but once I move the apk file to the android device, it will use an external API url ? I have set my “proxyUrl” to my external API url and it runs fine on my emulator, but when I attempt to get the apk file onto the device, it doesn’t seem to work. Any thoughts or gotchas I should be aware of?
    Thanks,
    John

    • ionic serve creates a http server in your host, then modifies your config.xml () pointing to your host’s server instead. When you set up proxyUrl your api is served by the same host than the application and because of that you don’t need to handle with CORS issues. When you run your application in the real device your origin for the app is the local filesystem and when the origin is local filesystem CORS don’t apply. If you want to to debug your app in the real device (ionic run android -l) your origin isn’t device’s local filesystem your origin is your computer localhost, so if your api isn’t server by the proxy (same origin), you need to face CORS

  1. Pingback: Debugging android cordova/phonegap apps with Chrome | Gonzalo Ayuso | Web Architect

  2. Pingback: Testando aplicações Phonegap/Cordova muito rapidamente no dispositivo (com o framework ionic) -

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: