Ionic 2 : Issues and Challenges

In my last blog we have seen the key benefits of Ionic 2. It includes speedup factor, organized directory structure, easy navigation, generator commands, set of native API and support for cross platform.

However, if you are thinking of migrating from Ionic 1 to Ionic 2, you may face different challenges or issues. You may either find ready solutions to these or you may have to implement solution yourself. I have successfully upgraded my project and hence would like to share my experience, with the issues I faced, and the way I solved these. Hope it will help. Lets see the issues one by one :

Handle Hardware Back Button

Many times we stuck at particular problem, trying to find solution. But unfortunately we do not get anything. Same thing happened with me. I was trying to handle hardware back button manually with the help of method provided by Ionic 2 :

    this.platform.registerBackButtonAction((event) => {
     if(this.navCtrl.canGoBack()){
       this.navCtrl.pop();
     }
   }, 100);
  

But the problem was, whenever I press back-button for consequently two times, it was popping root page too. This should not happen. More over this was very annoying to the users. I tried with many options. But finally this issue is handled in updated version of Ionic 2 i.e RC5

The point is, whenever we failed to find any solution, check for updated versions of packages we are using and confirm if the issue is handled in updated version. If not, there is no other way but to fix it yourself & send a PR.

Camera opening issue in Android Version 7 (Nougat)

In my app, on a page I was taking image as a input from user. There are two options, choose image from gallery or click a picture using the camera. Everything was working perfectly. But when Nougat was released I did a test pass to ensure that nothing is broken. But to my surprise, the camera was not working. Then I checked for issues in plugin on git. I found one issue related to it was raised and also fixed in updated version of ‘cordova-plugin-camera’. So I did update my plugin version and everything started working.

So, before using any plugin, first check for the issues listed. If any of the issues is a concern, look for alternatives. Else go ahead and use the plugin. (NOTE: In my experience the cordova & ionic community is very active & helpful. The issues are usually addressed pretty quickly. So you may take that chance too, as I did ;-))

Change is payment plugin usage in Ionic 2

The payment gateway I am using is Razor-pay. I had already used Razor pay plugin in the Ionic 1 build and naturally took it forward to Ionic 2. I implemented the integration in the same way as in Ionic 1. But the plugin did not work.  After spending some time investigating I decided to contact the Razor-pay tech support team. They responded quickly and told me the way to use it in Ionic 2 and its working now. Thanks to them!

If you stuck with a problem in any library, package or plugin and do not find any demo example, then I would highly recommend contacting the contributors. They will surely help!!

Check my upcoming blog for details on how to do 🙂

Issue of opening downloaded file in Nougat

In my app, I was showing list of files, having type PDF and image. This was working earlier but then suddenly users started reporting that the file is not opening. I tried and could reproduce the issue. The file was getting downloaded in app storage but not opening. The error I was  getting was “Promise unresolved”. I had already upgraded to Nougat, but did not think that was the cause. And kept thinking that it was a regression bug. However, after testing on many android versions, I came to know that it is an issue with Android 7.

I checked File-Opener plugin to verify if its having issue with Android 7 and there it was. The File-Opener plugin team fixed that issue in newer version(2.0.7). I did update my app with new plugin version. But unfortunately I got different error after update. The error was,

“android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()”.

This was wired. But then after some digging I figured out the solution. This is how it goes – whenever we build the app, the latest installed SDK version is referred. I was facing this error because my app was build with SDK 24. The solution to resolve the exception above is to change android:targetSdkVersion to 23 in AndroidManifest.xml. Essentially, downgrade. This is required because, SDK 24 only supports the URI format as content:///… and not file://, and I my code was using file:// everywhere. A long term solution is to use the latest format, however, it depends on how much time you have. (Note: You can Google for “Nougat file URI” and you will find descriptive articles about this issue)

  # File path = '<app_name>/platforms/android/AndroidManifest.xml'

  <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23" />

After updating package, got lots of errors while building app

I updated my Ionic version to RC5. Before update everything was working good. After update, for the sake of testing, I started running app with ‘ionic run android’. I got hundreds of various @typing errors. I tried with different solutions including re-installation of package. But none of them succeeded. Finally I solved it with following steps :

1. Rename original project with different name
2. Clone new copy of it
3. Uninstall cordova and ionic globally
4. Install both again with latest version
5. Run ‘npm install’
6. Add platform as per requirement
7. Build app

Page pop not possible along with params

In Ionic 2, its not possible to pop page along with some data or params. Many times we need to transfer data back to source page after pop. In this case, I used events provided by Ionic CLI. The page from which I wanted to pop, was publishing event. And the page to whom I wanted to pass parameter, was registered for same event.

For example, suppose I have two pages named as home page and contact page. First I will go to home page, then contact page. Here I want to transfer some data from contact page to home page with pop operation. I need to import Events class from ionic-angular library. Then My home page should be subscribed to event like ,

    this.events.subscribe('contact:data', (data) => {
      console.log("Data got from contact page : ", data);
    });
  

Now in contact page, at the time of pop operation I will publish event (with data) of same name. So that while popping, event will be captured in home page along with data. Here contact page will publish event like:

    this.events.publish('contact:data', {contact_info: "9988776655"});
    this.navCtrl.pop();
  

Unable to build app

In between app development, newer version of typescript was available. So I thought, It will be helpful if I update version of typescript. So I did update. But while building app, I was not able to do it. The problem was my typescript version(2.0.3) was not compatible with ionic app-script version. So I installed compatible version of app-script, then it worked.

So, while updating any particular package version, first check for compatibility issues, then only update.

Adding storage support in app

We may require to store data within app, basically offline storage. The main purpose of storing data is apparent,  to optimize load time. Ionic 2 provides more convenient way for storage support. It gives SQLite ionic native class to access cordova-sqlite-storage. Through this class we can easily apply CRUD operations on database. Its more easier to have storage support in Ionic 2 than Ionic 1.

Check my next blog for details on how to do 🙂

SQLite storage in Ionic 2

In my last blog I promised to provide step by step guide to implement on phone storage. The main purpose of storing data on phone (offline support) is to reduce load time and optmize the downloads.

In this blog I am sharing my experience on how to use storage with Ionic 2.

There are different ways of integrating storage with Ionic 2 app. I was considering two options, 1) use local-storage which is collection of key-value pairs within application or 2) use SQLite database. I chose SQLite database. The reason being,  local storage has key value structure, its hard to query large amount of data, plus it has limitation of 10 MB data.

There are few other blogs, but I found them little convoluted. So, I tried to simplify steps. Let’s see, step by step procedure.

Step 1: Basic Installation

Install latest version of Ionic and Cordova by –

       npm install -g ionic
       npm install -g cordova
     

Step 2: Start Project

Lets start new project with

     ionic start SampleProject blank --v2
   

Here by specifying v2 in command, I am referring it as Ionic 2 project. Also SampleProject will be created with blank theme.

Now add platform to project. Basically here I am just focusing on android platform. If you want it for IOS too, then you can add it with same command but replace android with IOS.

     ionic platform add android
   

Step 3: Add SQLite plugin

Next step is to add SQLite plugin. This plugin is provided by Cordova SQLite Storage. After installing add package name in package.json file. I am adding it with following command :

    ionic plugin add cordova-sqlite-plugin
  

Step 4: Add database provider

There are many ways, In which you can manage various operation over a SQLite database. Here is my plan, I am creating one provider which will be called as ‘Database Provider’. In that I will be specifying all CRUD operation including database open and destroy function. After that I will access that provider wherever I want. So, lets do it.

Add provider with,

     ionic g provider DatabaseProvider
   

Add following content in database-provider.ts file :

     import { Injectable } from '@angular/core';
     import { Http } from '@angular/http';
     import { SQLite } from 'ionic-native';

     @Injectable()
     export class DatabaseProvider {
       db = new SQLite();

       constructor(public http: Http) {
       }

       public openDatabase() {
         return this.db.openDatabase({name: 'user_data.db', location: 'default'});
       }

       public createUserTable(){
         let query = "CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT)";
         this.db.executeSql(query, {}).then(() => {
           console.log("user table created");
         });
       }

       public insertIntoUser(user_details){
         let insert_query = "INSERT INTO user VALUES (?, ?)";
         this.db.executeSql(insert_query, [user_details.first_name, user_details.last_name]).then((data) => {
           console.log("user inserted");
         });
       }

       public fetchFromUser(){
         return this.db.executeSql('SELECT * FROM user', []);
       }

      public dropDatabase(){
        console.log("dropping database");
        SQLite.deleteDatabase({name: 'user_data.db', location: 'default'});
      }
    }
   

Here I am first importing SQLite native class provided by Ionic CLI and then making its instance. While opening database we should provide database name and location to store it. Same parameters will be passed while dropping database.

Step 5: Open database in app.component.ts file

Now its time to open database, I want to open database when my app is started, so I will do it in app.component file as,

    import { Component } from '@angular/core';
    import { Platform } from 'ionic-angular';

    import { HomePage } from '../pages/home/home';
    import { DatabaseProvider } from '../providers/database-provider';

    @Component({
      templateUrl: 'app.html'
    })
    export class MyApp {
      rootPage = HomePage;

      constructor(platform: Platform, public dbService: DatabaseProvider) {
        platform.ready().then(() => {
          Promise.all([this.dbService.openDatabase()]).then((data) => {
            console.log("database is open now");
          });
        });
      }
    }
  

Here I am just importing database provider, which we have already created. Once platform gets ready I am opening it by just calling openDatabase() method.

Step 6: Apply operations on database

Now I am going to do all insert and fetch operation on database in home page. So now, I am again importing database-provider in home page. After that I will create table USER, add data to it and fetch user from it. In this example I am adding static data. In real-time we could store data coming from server side in SQLite DB. So here is the magic,

     import { Component } from '@angular/core';
     import { NavController } from 'ionic-angular';

     import { DatabaseProvider } from '../../providers/database-provider';

     @Component({
       selector: 'page-home',
       templateUrl: 'home.html'
     })
     export class HomePage {
       first_name:any = 'Kevin';
       last_name:any = 'Watson';
       user_list:any;
       constructor(public navCtrl: NavController, public dbService: DatabaseProvider) {
       }

       insert(){
         this.dbService.insertIntoUser({first_name: this.first_name, last_name: this.last_name});
       }

       refresh(){
         Promise.all([this.dbService.fetchFromUser()]).then((data) => {
           this.user_list = data;
         });
       }
    }
   

Now update the code in home.html file. Here I am showing two buttons and list of users. Add button will add record ( first name and last name ) in User table. Refresh button will take updated data from User table and store it to user_list. We can do any opration on DB. Just write that method in provider and access it in component.

     <ion-header>
       <ion-navbar>
         <ion-title>
           User List
         </ion-title>
         <ion-buttons start>
           <button (click)="refresh()">Refresh</button>
         </ion-buttons>
         <ion-buttons end>
           <button (click)="insert()">Add</button>
         </ion-buttons>
       </ion-navbar>
     </ion-header>

     <ion-content padding>
       <ion-list>
          <ion-item *ngFor="let user of user_list">
             {{user.firstname}} {{user.lastname}}
          </ion-item>
       </ion-list>
     </ion-content>
  

In this example, the data we are storing is static. In real time, we fetch data from server and store it in database. Periodically we need to have watch on server db to get updated data. There are two ways, either we could use tools available for auto sync of a database or on some action we will manually check for update.

Tools like PouchDB can be used with SQLite. It needs URL of remote database server to sync with. Second way is to check for the updated data on some action/ event. If data is updated then we will update our local data with same.

In this way, We can store data in application and also retrieve it whenever needed. For example we could load data from local DB when we have no internet connection. This will serves our purpose of offline support.

Benefits of Ionic 2

Ionic is world’s most popular cross platform mobile development technology. Basically its hybrid mobile app development framework. Build on top of Cordova, which enables us to build app along with web technologies. The goal behind developing Ionic is to give web developers a way to use their skill-set to build mobile applications.

Recently Ionic 2 has been launched. Conceptually Ionic 2 is similar to Ionic 1. In Ionic 2 controller hold all logical part and view is handled by template, except controllers are classes. Like this there are many key differences. Ionic 2 is build on the base of Angular 2 which uses typescript. All controllers are written in .ts file. Hence while building a app, these files get converted into .js files. This is the process of ‘Transpiling’. Transpiling does not allow logic to be directly available through debugging.

Now a days Ionic 2 is top choice for fast development. There are some reasons why it is more preferable than Ionic 1. Followings are some key points why we should migrate from Ionic 1 Ionic 2 :

Speed :

In Angular 2 change detection in model is very quick. So, Ionic 2 app is faster than  Ionic 1.

Organized directory structure :

In Ionic 1, there is no standard way to organize our files. User can arrange file as per his/ her convenience. But Ionic 2 comes with its own directory structure. Every page is referred as component. Each component comes with its own folder. It contains .ts file as class/ controller, .css file and .html file as a view. For e.g. suppose we have two pages as home and about. Then structure be like :

Ionic 1 :

  • /www
    • /js
      • home.js
      • about.js
    • /templates
      • home.html
      • about.html
    • /css
      • home.css
      • about.css

Ionic 2 :

  • Home
    • home.ts
    • home.html
    • home.css
  • About
    • about.ts
    • about.html
    • about.css

Navigation :

In Ionic 1, navigation is done by routing through states. But in Ionic 2, more easier approach is used that is ‘stack’. When we want to redirect to particular page, we will just push that page onto navigation stack. We can come back to previous page by popping the current page from stack. As simple as that. Lets see difference between Ionic 1 and Ionic 2 navigation :

Ionic 1 : We can specify routes like this,

  $stateProvider
   .state('home', {
     url: '/home',
     templteUrl: 'templates/home/index.html',
     controller: 'HomeController'
   })
   .state('about', {
     url: '/about',
     templateUrl: 'templates/about/index.html',
     controller: 'AboutController'
   });

Whenever we want to go from home state to about state we just give,

  $state.go('about');

Ionic 2 : Similarly, suppose we have home page as,

  import { Component } from '@angular/core';
  import { NavController } from 'ionic-angular';
  import { About } from '../about/about';

  @Component({
    selector: 'page-home',
    templateUrl: 'home.html'
  })
  export class Home {
    constructor(public navCtrl: NavController){}
    loadAbout(){
     // load about page
    }
  }

To go to about page, we just push page onto navigation stack,

  loadAbout(){
    this.navCtrl.push(About);
  }

To remove page from stack or to go to previous page,

  this.navCtrl.pop();

Generators :

In Ionic 1, whenever we need to add new controller, we just define it in some file. Then associate controller with relative template file with help of state-provider. This process is very simple in Ionic 2. With Ionic 2 CLI, we can easily generate pages, providers, pipes, etc. Ionic 2 provides generator command for it. Lets have a look,

1. Pages :

These are main component of app. When we create new page, it comes with 3 files as mentioned above. To generate page command is :

  ionic generate page Home or ionic g page Home

The page comes with default structure. Its looks like :

  import { Component } from '@angular/core';
  import { NavController } from 'ionic-angular';

  @Component({
    selector: 'page-home',
    templateUrl: 'home.html'
  })
  export class HomePage {

    constructor(public navCtrl: NavController) {}

    ionViewDidLoad() {
      console.log('Hello HomePage Page');
    }

  }

2. Provider:

Providers are similar to service in Ionic 1. Every provider is injectable. Means we can import or inject service, provided by provider. The command to generate providers is :

  ionic g provider ApiServiceProvider

The default structure of provider is :

  import { Injectable } from '@angular/core';
  import { Http } from '@angular/http';
  import 'rxjs/add/operator/map';

  @Injectable()
  export class ApiServiceProvider {

    constructor(public http: Http) {
      console.log('Hello ApiServiceProvider Provider');
    }

  }

3. Pipe:

Pipe is one of the decorator. The main goal of pipe is to transform the given set of values. Pipe is also injectable. The default method in each pipe class is ‘transform’. Our input transformation logic comes here. The command to generate Pipe :

  ionic g pipe Lowercase

The default structure of pipe is :

  import { Injectable, Pipe } from '@angular/core';

  @Pipe({
    name: 'lower'
  })
  @Injectable()
  export class LowerCase {
    transform(value, args) {
      value = value + ''; // make sure it's a string
      return value.toLowerCase();
    }
  }

We can use this pipe in template as,

  <label>{{name | lower}}</label>

Set of API :

To build dynamic app, we may need various cordova plugins. In Ionic 1, when we use any plugin we need to inject related cordova service in relavant controller. Then only we can access plugin feature. For example, suppose we want to use cordova plugin to access contact from mobile. For it we will install plugin ‘cordova-plugin-contacts’. Once done, we can use it like this in Ionic 1:

  angular.module("app")
    .controller('ContactController', function($scope, $cordovaContacts, $cordovaToast){
      $scope.findContact = function(){
        Contacts.find(['*'], {filter  : "TPN whatsapp number"}).then(function(contactFound){
           if(contactFound.length >= 1){
             $cordovaToast.showShortBottom('Contact Found!!!');
           }
        });
      }
    })

This scenario looks clean in Ionic 2. It provides large set of Native API classes. Using these classes, we can easily use any  plugin with app. To integrate contact plugin, Ionic 2 provides native class names as ‘Contacts’. We can use it like this:

  import { Component } from '@angular/core';
  import { ToastController } from 'ionic-angular';
  import { Contacts } from 'ionic-native';

  @Component({
    selector: 'page-home',
    templateUrl: 'home.html'
  })
  export class Home {
    constructor(public toastCtrl: ToastController){}
    findContact(){
      Contacts.find(['*'], {filter  : "Support Contact Number"}).then((contactFound) => {
        if(contactFound.length >= 1){
          let toast = this.toastCtrl.create({
            message: "Contact Found!!!",
            duration: 2000,
            position: 'bottom'
          });
          toast.present();
        }
      });
    }
  }

Support for cross-platform :

Ionic 1 comes with support to only 2 platforms, i.e. Android and IOS. Ionic 2 allows us to build app which is compatible with Android, IOS as well as Windows phone.

In next blog we will see what major issue we can face while developing an app in Ionic 2 and solution to it.