Category: ionic

Improve performance of Hybrid apps to achieve almost native performance

native performance

Native performance

In this post we are going to cover how to achieve almost native performance with APIs that the web provides, to make fast hybrid apps. Every day the browsers evolves and improve at a very accelerated speed, so in my personal opinion we are going to get to a point were the performance of a web page running on a browser and the performance of a native applications is going to be seamless, if we are not there already.

What is a hybrid app

Hybrid applications are, websites packaged into a native wrapper (WebView). Basically, a hybrid app is a web app built using HTML5 and JavaScript, wrapped in a native container

Improving for the web

The goal of this article is to expose how to improve the HTML, CSS and javascript performance for mobile hybrid applications, is in deed to show some of the techniques used by the hybrid frameworks to boost the performance on mobile applications

Rendering Performance

To write performant sites and apps you need to understand how HTML, JavaScript and CSS is handled by the browser, and ensure that the code you write (and the other 3rd party code you include) runs as efficiently as possible. Read more here

requestAnimationFrame()

How it works:

  • When you call requestAnimationFrame, it request that your function be called before next paint
  • The function is called ~60 times/sec or throttled in the background
  • Animations optimized into single reflow/repaint
  • Smooth animations without jank

Example of requestAnimationFrame()

function animate() { 
  requestAnimationFrame(animate) 
  myElement.style.transform = `translateX(${x}px)`; 
  X++; 
} 
requestAnimationFrame(animate) 

requestAnimationFrame() Browser support

requestAnimationFrame availability

Reference: https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame

Avoiding Layout Thrashing with DOM batching

What is layout thrashing:

Layout Thrashing is where a web browser has to reflow or repaint a web page many times before the page is ‘loaded’. In the days before JavaScript’s prevalence, websites were typically reflowed and painted just once, but these days it is increasingly common for JavaScript to run on page load which can cause modifications to the DOM and therefore extra reflows or repaints. Depending on the number of reflows and the complexity of the web page, there is potential to cause significant delay when loading the page, especially on lower powered devices such as mobile phones or tablets.

Reference: https://blog.idrsolutions.com/2014/08/beware-javascript-layout-thrashing/

This can be avoided by batching the writes and reads with fastdom (it relies on requestAnimationFrame())

How it works?

FastDom works as a regulatory layer between your app/library and the DOM. By batching DOM access we avoid unnecessary document reflows and dramatically speed up layout performance.

More info here

fastdom example for native performance

Reference: https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing

ionappfullplus - promo-banner

Efficient style modifications

Skip layout and paint by only modifying composite-only
properties.

Modern browsers can animate four things really cheaply: position, scale, rotation and opacity. If you animate anything else, it’s at your own risk, and the chances are you’re not going to hit a silky smooth 60fps.

html cheap operations

Reference: https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/

Passive Event Listeners

Indicate touch events won’t block scrolling, Run event listener without holding up scrolling, provides smooth touch and scroll animations and gestures

Example:

passive event listener

Passive Event Listeners browser availability

passive event listeners availability

Reference: https://developers.google.com/web/updates/2016/06/passive-event-listeners

will-change

Indicates to the browser certain properties will change
frequently (ex: scrolling, animations, gestures, etc.)

Browser promotes element to it's own layer, smoother animations with less CPU usage (though
possibly higher RAM usage).

Note: Use with caution: If everything is optimized, nothing is

will-change example:

will-change: auto;
will-change: scroll-possition;
will-change: contents;
will-change: transform;
will-change: opacity;
will-change: left, top;

Fallback:

transform: translateX(0)

will-change - availability

will-change availability

Reference: https://developer.mozilla.org/en/docs/Web/CSS/will-change

CSS containment

The new CSS Containment property lets developers limit the scope of the browser's styles, layout and paint work. learn more here

In other words, it indicate isolated elements, browser optimizes, limiting recalculation paint/layout/size/style to sub-tree, deriving in fast component updates

Example of CSS containment

containment example

css containment example speed

This layout is 1425x faster!

CSS containment browser availability

css containment availability

Reference: https://developers.google.com/web/updates/2016/06/css-containment

Conclusions

We highly recommend you to use a framework, frameworks do this stuff for you, for example in ionic all the components are optimized with these performance improvements.
In other words if you want to create a hybrid a mobile application try to avoid direct DOM manipulation, always use a framework, with that you'll be more close to achieve native performance .

14 ionic Hacks to improve hybrid app development

ionic hacks

I've been working with ionic for a while now, and I decided to make a compilation of useful ionic hacks, tips, tricks or simply useful information from my journey, so I can have a centralized place where I can find easy and fast this useful compilation, and also share it with all the ionic developers, I hope this might be of some help for others.

I divided it into 3 categories (for now), I think this is going to be an on-going process of keeping adding ionic hacks, Every time I find something interesting I'll update this post.

Ionic hacks index

- UI/UX

  • Hide Scrollbar when using Chrome
  • Configure your Ionic App Config
  • Making Text Selectable

- Debugging

  • Remote Debug on Android and iOS
  • Debugging faster on iOS & Android with -livereload
  • Targeting Platforms and Operating Systems
  • Target platform from url

- Practical solutions

  • ngZone, include into angular cycle digest
  • Declare globally window's var for plugins with no wrappers
  • @ViewChild only works when the view is loaded
  • Use Crosswalk for Android
  • Improving Loading Times and performance of your Ionic App
  • Using Custom Scripts
  • Add cordova-android-support-gradle-release to resolve gradle version conflict issues (Android)

Lets get started

Hide Scrollbar when using Chrome

When debugging your ionic app, using for example ionic serve lab on Chrome, One thing that is really annoying is the visible scrollbar:
chrome hide scroll bar

Go to app/apps.css to add the following css globally (this will hide all the scrollbars inside your application):

* ::-webkit-scrollbar {
  display: none;
}

Configure your ionic app config

We can modify the general behavior of components through the root configuration of our app. The Config lets you configure your entire app or specific platforms. You can set the tab placement, icon mode, animations, define back button icon, and more. This is one of the most interesting ionic hacks.

@NgModule({
  declarations: [ MyApp ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp, {
      backButtonText: 'Go Back',
      iconMode: 'ios',
      modalEnter: 'modal-slide-in',
      modalLeave: 'modal-slide-out',
      tabsPlacement: 'bottom',
      pageTransition: 'ios-transition'
    }, {}
  )],
  bootstrap: [IonicApp],
  entryComponents: [ MyApp ],
  providers: []
})

You could also configure these values at a component level

<ion-tabs tabsPlacement="top">
  <ion-tab tabTitle="Dash" tabIcon="pulse" [root]="tabRoot"></ion-tab>
</ion-tabs>

Any value can be added to config, and looked up at a later in any component.

config.set('ios', 'favoriteColor', 'green');

// from any page in your app:
config.get('favoriteColor'); // 'green' when iOS

Making Text Selectable

You might have noticed that you can’t select the text inside your ionic app, which is especially challenging if you want to enable the user to be able to select text (to be able to copy/paste for example) inside of your application.

You can apply this css rule to all the application, or just to a specific element container:

body {
    -webkit-user-select: text;
    user-select: text;
}

Remote Debug on Android and iOS

Android

For this you'll need to have Android Studio installed on your computer

Connect your device to the computer and make sure it is correctly detected, by running adb devices (Android SDK is required).

Build your app and install it on your device by running ionic cordova run android --device command. Once your app is launched on the device, open the Chrome browser on your computer, in a new tab's url bar type: chrome://inspect/#devices, and inspect your device.

ionic device remote debug

iOS

Plug in your device and make sure it is correctly detected by your computer. Build your app and install it on your device by running ionic run ios --device.

Once your app is launched on device, open Safari dev tools (on your computer) by clicking on Develop > Your iPhone > Your app:

ios safari remote debugging

Debugging faster on iOS & Android with -livereload

This is very simple, when you run the command to deploy to your device, add parameter -l ionic cordova run android --device -l you can also add parameter -c to display console logs in your command prompt:

--livereload, -l Spin up server to live-reload www files
--consolelogs, -c Print out console logs to terminal

Targeting Platforms and Operating Systems

Some times we need to detect in which platform the code is running, some scenarios are when you are using cordova plugins that are only supported by for example Android and iOS, and you are planning to deploy your app also to Windows app store, and as a PWA. A specific plugin with this characteristics is the admob free plugin.

ionic-platform

Here is a list of the platforms supported:

ionic platforms

Target platform from url

This is a useful functionality, when you run your application in the browser ionic serve you can use the --lab parameter to open your app into an iframe wrapper that call for specific platform, in that way is easy for you to visually select what platform target you want the ionic application to run.

But what happen when you deploy your application to firebase hosting for example, maybe to demo a prototype for your clients, or in our case to demo our latest starter on the ionic market

In our case wecalled the application with the following parameters: http://< path to your app >?production=true&ionicplatform=android

For Android:
https://www.jomendez.com/ionappfullplus/?production=true&ionicplatform=android

For iOS:
https://www.jomendez.com/ionappfullplus/?production=true&ionicplatform=ios

ngZone, include into angular cycle digest

As the official angular documentation states:

The most common use of this service is to optimize performance when starting a work consisting of one or more asynchronous tasks that don't require UI updates or error handling to be handled by Angular. Such tasks can be kicked off via runOutsideAngular and if needed, these tasks can reenter the Angular zone via run.

In other words, it allows you to either execute code outside angular cycle digest (to improve performance) or, to include code executed asynchronous (outside angular) inside the angular cycle digest.

A practical example is when we created the dialogFlow (chatbot) showcase, since the cordova plugin didn't have the ionic wrapper, it executes the code outside of angular:

      this.ngZone.run(() => {
        console.log(response);
        let text = this.processCommand(response.result.fulfillment.speech);
        this.messages.push({
          text: text,
          sender: "api"
        });
        this.content.scrollToBottom(400);
      });

ionappfullplus - promo-banner

Declare globally window's var for plugins with no wrappers

This is the same scenario as before, we needed to call the dialogflow plugin's api via window variable window.ApiAIPlugin if try to call ApiAIPlugin from the window variable, Typescript will complaint about ApiAIPlugin not been a property of window

We have 3 solution for this, one is what the title of this category suggest, declare the window variable globally:

import { DatabaseProvider } from "../../../providers/database/database";

declare var window;

@IonicPage()
@Component({
  selector: 'page-dialogflow-chat',
  templateUrl: 'dialogflow-chat.html',
})
export class DialogflowChatPage {
//...
}

Second is to cast the window variable to "any" type

(<any>window).ApiAIPlugin

And the third one is to call the property using the arry call format:

window["ApiAIPlugin"].requestText

@ViewChild only works when the view is loaded

I remember faced this issue before, but the last time I did was crating the signature-pad showcase where I needed to reference a canvas within the HTML, and call the method clear() as soon as the page is loaded.

Once you declare the @ViewChild() if you try to use the reference on the constructor method you'll get an error:

Uncaught TypeError: Cannot read property 'clear' of null

One potencial solution is to call it when ionViewDidEnter lifecycle event is called:

 ionViewDidEnter() {
    this.signaturePad.clear();
 }

Use Crosswalk for Android

Every Android version runs a different WebView (a browser that runs your ionic application). The performance is different across devices, and it can be really bad on the old Android devices. To get a consistent performance and experience with fluidity and responsiveness on every Android device, you can install Crosswalk. It basically embeds the latest Chromium browser into your application, it will add around 20Mb per APK, both ARM and X86.

To install the plugin you can run the following command:


ionic plugin add cordova-plugin-crosswalk-webview

Note:

Knowing this was very useful, when created our multi-platform Gamefy (which is a bundle of 5 games in 1), because the performance of the games was poor on android low-end devices, and crosswalk fixed the issue.

Improving Loading Times and performance of your Ionic App

As every developer should know you are first of all responsible for writing good code that performs. It’s not the fault of Ionic (most of the time) if the app loads slowly, but there are few thinks you can do to improve your performance.

Make sure to use lazy load for your page, although when you generate a page from the ionic CLI it generate the page ready for lazy loading, the starters templates doesn't use lazy load.

By doing this, your app won’t load all the pages on startup immediately but rather prolong the loading a bit so the first page will appear a lot faster. This is very important if you are creating PWA, this is one of the key points for PWA.

To learn more about how to use lazy load on an Ionic app, visit here

As mentioned before, another improve is to use ngZone to execute tasks that don't require UI updates or error handling to be handled by Angular to improve performance.

As mentioned in the previous point, install crosswall can be a boost in your performance specially for low-end android devices.

Finally, what can make a huge change for all Ionic apps is building for production. This will remove any debugging plugin, minify, uglify the code, etc. Simply run:


ionic cordova build ios --prod --release

That command will activate Angular’s production mode, and run a full production build so you can get the smallest bundle possible.
It will take a while, especially the first time you run it, but it is worth it.

Using Custom Scripts

You'll notice that in the package.json file of your project there are a property called scripts

"scripts": {
"clean": "ionic-app-scripts clean",
"build": "ionic-app-scripts build",
"lint": "ionic-app-scripts lint",
"ionic:build": "ionic-app-scripts build",
"ionic:serve": "ionic-app-scripts serve",
}

Here you can define your own tasks, for deployment, android app signing, zipalign, etc. So every time you need to sign your apk to deploy to the app store, you can define a task to do that

"scripts": {
"clean": "ionic-app-scripts clean",
"build": "ionic-app-scripts build",
"lint": "ionic-app-scripts lint",
"ionic:build": "ionic-app-scripts build",
"ionic:serve": "ionic-app-scripts serve",
"jarsign": "jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore release-key.jks path\\platforms\\android\\app\\build\\outputs\\apk\\release\\app-release-unsigned.apk jomendez",
"zipalign": "\"C:\\Users\\user\\AppData\\Local\\Android\\sdk\\build-tools\\27.0.3\\zipalign\" -f -v 4 path\\platforms\\android\\app\\build\\outputs\\apk\\release\\app-release-unsigned.apk path\\platforms\\android\\app\\build\\outputs\\apk\\release\\name.apk",

}

Then you just have to run:


npm run jarsign
or
npm run zipalign

Add cordova-android-support-gradle-release to resolve gradle version conflict issues (Android)

In my experience sometimes cordova plugins throw errors regarding packages versions vs environment versions, or the most common for me has been conflict between two or more cordova plugin, the most recent was when I was trying to integrate the barcodescanner plugin on ionAppFullPlus together with the facebook plugin.

I received the following error:

Build fails with error
> java.lang.RuntimeException: java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Unable to merge dex

The problem was that the two plugin uses different versions for the supporting libraries

After few hours trying everything I found this plugin for Android (cordova-android-support-gradle-release) that aligns various versions of the Android Support libraries specified by other plugins to a specific version, which fixed the issue.

Installation:

cordova plugin add cordova-android-support-gradle-release

Conclutions

I will try to update this post often with new ionic hacks, I think there is a lot of things that can be done to to improve the performance of ionic applications, or the developer's productivity, etc.

How to combine tab navigation with side menu in Ionic

menu banner

With ionic CLI you can quickly and easy create a Side Menu app just by running the command

ionic start sidemenu

or create a tab application by running:

ionic start tab

Those are common navigation patterns used by a lot of mobile applications today:

In this short tutorial we are going to try to combine those different navigation patterns into one application.

We will keep things simple, we are going to make the transition from a login page (Taken from ionappFull4Pro starter) to our inside navigation, which combines side menu and tabs.

Login workflow

This is a module that is part of the ionappFull4Pro starter, and with it you'll be able to:
- Login with Firebase username and password
- Login with Google (native) //for this the app must run on a device or an emulator
- Login with Facebook (native) //for this the app must run on a device or an emulator
- Login with Google (PWA mode) //this mean that you can login with google running the app on the browser
- Recover your password
- Register new user
- User editable profile

Prerequisites

We assume you are familiar with ionic, and you have set up your ionic environment, if not you can follow this tutorial: https://ionicframework.com/getting-started#cli

We are going to start creating a blank ionic template using the CLI commandand then we are going to create all the needed pages:

ionic start convinedNavigation blank
ionic g page menu
ionic g page tabs
ionic g page tab1
ionic g page tab2

Spoiler alert

We are going to use lazy loading, for load our pages, so after create the new project feel free to remove the home page generated by default.

Now use your favorite IDE to open the project (mine is Microsoft Code) and change the rootPage variable to a string "LoginPwaPage", this will tells ionic LoginPwaPage is the entry point page.

ionic app component

Lest import the login component from ionappFull4Pro into our project. First, copy the login-workflow folder (which contains login, profile,recover and register pages) into our project (also you can follow the instructions on the instructions.txt file included in the folder).

import login from ionappfull4pro

This is how it should looks like:

login workflow in our local project

Follow the instructions on the instructions.txt file to complete the import of this component:

Instructions.txt

Instructions to import this component in your existing ionic app.

If you don't have an ionic app created, create one, by running the following command on the command console, on the folder you want your app to be created, 
once your app is created locate the pages folder and copy this component (folder) in the page folder:

$ ionic start MyAppName blank

for more info go to the ionic official documentation.


Once you have your app created Install the components dependencies (see what is imported on the ts files on this component):

On your firebase dashboard enable the email/password methods, see documentation: http://jomendez.com/ionAppFull4Pro-documentation/#!/firebase_configuration
	
Notes:
For this component to work you need to setup firebase and also import the DatabaseProvider from "/providers/database/database" included on the ionAppFull4Pro project.
Also import /providers/auth-data provided from ionAppFull4Pro to your app
Also copy src/config/config.ts from this project (ionAppFull4Pro) to your application, and enter your firebase information.

Remember to install the firebase dependencies and add the providers to the app.module.ts file:

npm i --save angularfire2 firebase

For this tutorial we are going to use the login-pwa component to keep it simple and be able to test the app in the browser without having to run it on a device or emulator. Login pwa component uses some "fancy" animation library called VegasJs (this is not required for the login component to work, you can easily remove it). In order to include it and make it work you'll need to copy few thinks from ionappFull4Pro starter, first include the jquery library and vegas dependencies on index.thml and then copy the files into assets folder:

add jquery and vega index
images on the assets folder

This is how the app.modules should looks like after all the imports and declaration:

app modules dependencies

One last thing it we have to redirect to menu page after login, so we will have to modify the login() method, lets comment the code that redirects to the profile page and add the code that set the MenuPage as the root:

redirect to menupage

Build the menu navigation:

Add this menu code to your src/pages/menu/menu.html


<ion-menu [content]="content">
  <ion-header>
    <ion-toolbar>
      <ion-title>Menu</ion-title>
    </ion-toolbar>
  </ion-header>
 
  <ion-content>
    <ion-list>
      <button ion-item menuClose *ngFor="let p of pages" (click)="openPage(p)">
          <ion-icon item-start [name]="p.icon" [color]="isActive(p)"></ion-icon>
          {{ p.title }}
        </button>
    </ion-list>
  </ion-content>
</ion-menu>
 
<!-- main navigation -->
<ion-nav [root]="rootPage" #content swipeBackEnabled="false"></ion-nav>

Add this code to src/pages/menu/menu.ts
Notice that in page one of the pages that can be called is the Profile page that comes with the login component from ionappFull4Pro starter

 pages = [
    { title: 'Tab 1', pageName: 'TabsPage', tabComponent: 'Tab1Page', index: 0, icon: 'home' },
    { title: 'Tab 2', pageName: 'TabsPage', tabComponent: 'Tab2Page', index: 1, icon: 'shuffle' },
    { title: 'Profile', pageName: 'AfterLoginPage', icon: 'contacts' },,//notice we cal here the Profile that comes with the login workflow component on ionAppFull4Pro starter
  ];
import { Tab2Page } from './../tab2/tab2';
import { Tab1Page } from './../tab1/tab1';
import { TabsPage } from './../tabs/tabs';
import { Component, ViewChild } from '@angular/core';
import { IonicPage, NavController, Nav } from 'ionic-angular';
 

@IonicPage()
@Component({
  selector: 'page-menu',
  templateUrl: 'menu.html',
})
export class MenuPage {
  // Basic root for our content view
  rootPage = 'TabsPage';
 
  // Reference to the app's root nav
  @ViewChild(Nav) nav: Nav;
 
  pages = [
    { title: 'Tab 1', pageName: 'TabsPage', tabComponent: 'Tab1Page', index: 0, icon: 'home' },
    { title: 'Tab 2', pageName: 'TabsPage', tabComponent: 'Tab2Page', index: 1, icon: 'shuffle' },
    { title: 'Profile', pageName: 'AfterLoginPage', icon: 'contacts' },
  ];

  constructor(public navCtrl: NavController) { }
 
  openPage(page) {
    let params = {};
 
    // The index is equal to the order of our tabs inside tabs.ts
    if (page.index) {
      params = { tabIndex: page.index };
    }
 
    // The active child nav is our Tabs Navigation
    if (this.nav.getActiveChildNav() && page.index != undefined) {
      this.nav.getActiveChildNav().select(page.index);
    } else {
      // Tabs are not active, so reset the root page 
      // In this case: moving to or from SpecialPage
      this.nav.setRoot(page.pageName, params);
    }
  }
 
  isActive(page) {
    
    let childNav = this.nav.getActiveChildNav();
 
    if (childNav) {
      if (childNav.getSelected() && childNav.getSelected().root === page.tabComponent) {
        return 'primary';
      }
      return;
    }
 
    // Fallback needed when there is no active childnav (tabs not active)
    if (this.nav.getActive() && this.nav.getActive().name === page.pageName) {
      return 'primary';
    }
    return;
  }
 
}


Adding the Tab

Open your src/pages/tabs/tabs.html and change it to:

<ion-tabs [selectedIndex]="myIndex">
  <ion-tab [root]="tab1Root" tabTitle="Tab 1" tabIcon="home"></ion-tab>
  <ion-tab [root]="tab2Root" tabTitle="Tab 2" tabIcon="contacts"></ion-tab>
</ion-tabs>

This tab bar will be pointing to the 2 tabs pages we created at the beginning.
Now change your src/pages/tabs/tabs.ts

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
 
@IonicPage()
@Component({
  selector: 'page-tabs',
  templateUrl: 'tabs.html',
})
export class TabsPage {
 
  tab1Root: any = 'Tab1Page';
  tab2Root: any = 'Tab2Page';
  myIndex: number;
 
  constructor(navParams: NavParams) {
    // Set the active tab based on the passed index from menu.ts
    this.myIndex = navParams.data.tabIndex || 0;
  }
}

Now we only need to add a menu button to all of our pages so we can toggle the menu from everywhere. Go through these 2 files (tab1, tab2) and change it, add the following html code:

Tab1

<ion-header>
  <ion-navbar>
      <button ion-button menuToggle>
      <ion-icon name="menu"></ion-icon>
    </button>
    <ion-title>Tab1</ion-title>
  </ion-navbar>
</ion-header>
 
<ion-content padding>
  This is tab 1 content
</ion-content>

Tab2

<ion-header>
  <ion-navbar>
      <button ion-button menuToggle>
      <ion-icon name="menu"></ion-icon>
    </button>
    <ion-title>Tab2</ion-title>
  </ion-navbar>
</ion-header>
 
<ion-content padding>
  This is tab 2 content
</ion-content>

This is the result:

Try it yourself:

Conclusions

With this article we got two navigation pattern working together, it could be a little bit tricky, but it is possible, also we were able to integrate/import the login module very easy, from the ionAppFull4Pro ionic starter into our applications, saving us a lot of time and development effort, and just with that we were able to implement an app with firebase login, google login, recover password, register user, and profile page.

Cheers,
Jose

How To Debug White Screen Ionic error on devices

How To Debug White Screen Ionic error on devices

Debug White Screen Ionic error on devices

This type of error occurs when your Ionic app is working fine in your desktop browser (ionic serve) and then you deploy it to a device or emulator and it doesn't work at all! What you see is a white screen when you open the app.
It is easy to debug your ionic app when doing `ionic serve` you just open the browser's console, but once you deploy to a device or emulator, it is easy to know what is causing the error since the device doesn't have a console to see JavaScript errors.

There are two ways to figure out what is happening one is using the cli livereload with consolelogs when using run or emulate command, and the second one is using the chrome developer tool.

Ionic livereload with consolelogs

note:
You can only use --consolelogs if you also use --livereload.

The --livereload option will instantly reload/update the app on your device or emulator it is similar to when you run ionic serve for testing your app on the desktop browser, the --consolelogs will log on the cmd console all the JavaScript error, etc. providing feedback of what is happening.

ionic run android --livereload --consolelogs

You can use the shorthand version:

ionic run android -l -c

Chrome Developer Tool

You need to configure your device to enable developer mode. For more information go here: https://developers.google.com/web/tools/chrome-devtools/remote-debugging/

Now you need to run your app on an Android device, you can attach the Chrome Developer Tools to the app and see the error in the Console, you need to connect your device with a USB cable to your computer, open your desktop Chrome browser and type chrome://inspect on the address bar.

You should see your device listed, click on inspect and from there you can use the Developer Tools the same way you use it for your desktop browser when do ionic serve. You should see the error causing the white screen, and any other relevant information like the console.log() that you placed on your ionic app code.

This doesn't work for iOS and Safari web inspector, the most universal way is to use the --livereload option, which will work for both iOS and Android.

Happy Coding,
Cheers

Add to Home Screen your ionic PWA

Add to Home Screen your ionic PWA

add-to-home-screen

One of the feature of Progressive Web Apps is the ability to add to home screen your application, in a mobile phone or on a desktop computer, if using Chrome (recommended) it handles most of the heavy lifting for you, and on Android, Chrome will generate a WebAPK creating an even more integrated experience for your users.

Starting on Chrome 68 (beta in early June 2018), Chrome will not automatically show the Add to Home Screen banner, instead, you must show it by calling prompt() on the beforeinstallprompt event.

Criteria to fire the "beforeinstallprompt" event

- The web app is not already installed
- Meets a user engagement heuristic (currently, the user has interacted with the domain for at least 30 seconds)
- Includes a web app manifest that includes:
   short_name or name
   icons must include a 192px and a 512px sized icons
   start_url
   display must be one of: fullscreen, standalone, or minimal-ui
- Served over HTTPS (required for service workers)
- Has registered a service worker with a fetch event handler

Note: Other browsers may have different criteria to trigger the beforeinstallprompt event, check their respective sites for full details: Edge, Firefox, Opera.

Show the add to home screen prompt

In order to prompt to the users the Add to Home Screen prompt, you need to:

1- Listen for the beforeinstallprompt event
2- Notify the user your app can be installed with a button or other element that will generate a user gesture event.
3- Show the prompt by calling prompt() on the saved beforeinstallprompt event.

let deferredPrompt;

window.addEventListener('beforeinstallprompt', (e) => {
  // Prevent Chrome 67 and earlier from automatically showing the prompt
  e.preventDefault();
  // Stash the event so it can be triggered later on the button event.
  deferredPrompt = e;
// Update UI by showing a button to notify the user they can add to home screen
  btn.style.display = 'block';
});

//button click event to show the promt
btn.addEventListener('click', (e) => {
  // hide our user interface that shows our button
  btn.style.display = 'none';
  // Show the prompt
  deferredPrompt.prompt();
  // Wait for the user to respond to the prompt
  deferredPrompt.userChoice
    .then((choiceResult) => {
      if (choiceResult.outcome === 'accepted') {
        console.log('User accepted the prompt');
      } else {
        console.log('User dismissed the prompt');
      }
      deferredPrompt = null;
    });
});

You can only call deferredPrompt.prompt() on the deferred event once, if the user dismissed it, you'll need to wait until the beforeinstallprompt event is fired on the next page navigation.

How to determine if the app was installed

To determine if the application was successfully added to the user's home screen once they accepted the prompt, you need to listen for the appinstalled event.

window.addEventListener('appinstalled', (event) => {
 console.log('installed');
});

Detecting with JavaScript if you app is launched from the home screen

if (window.matchMedia('(display-mode: standalone)').matches) {
  console.log('display-mode is standalone');
}

Safari

if (window.navigator.standalone === true) {
  console.log('display-mode is standalone');
}

Easiest way to test if the beforeinstallprompt event will be fired

Easiest way is to use Lighthouse to audit your app, and check the results of the User Can Be Prompted To Install The Web App test.

Practical example with ionic

For this example I going to use ionic, we are going to generate a PWA with ionic, for this example I assume you are familiar with ionic and you have installed it on your computer, if not, then you can read the here

Lets generate our ionic app:

After you create the ionic blank app, lets run ionic serve:

cd pwa
ionic serve

You should see something like this on your browser:

Open your application using google chrome, open the dev tool, go to audit and run the Progressive web app audit:

You'll notice that the app is only 45% PWA out of the box, we should make some changes first to comply with the requirements to trigger the beforeinstallprompt event:

Registering the service worker

Open the ionic app on your preferred IDE (I'll use Microsoft code) and go to ./src/index.html and uncomment this line:

<!-- un-comment this code to enable service worker
  <script>
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('service-worker.js')
        .then(() => console.log('service worker installed'))
        .catch(err => console.error('Error', err));
    }
  </script>-->

The another requirement is to have a manifest.json, fortunately ionic generates one for us:

{
  "name": "Ionic",
  "short_name": "Ionic",
  "start_url": "index.html",
  "display": "standalone",
  "icons": [{
    "src": "assets/imgs/logo.png",
    "sizes": "512x512",
    "type": "image/png"
  }],
  "background_color": "#4e8ef7",
  "theme_color": "#4e8ef7"
}

Now lets provide a fallback when JavaScript is not available by just adding a < noscript > tag on the index:

<noscript>
This application needs JavaScript to work, please enable JavaScript on your browser
</noscript>

With just that, we were able to go from 45 to 82:

Now we need to deploy our app to firebase hosting to be able to have HTTPS, and a 100 score on the lighthouse.

Install firebase tools

npm install -g firebase-tools

Initialize your site

$ firebase init

To deploy your site, run the following command from your project's root directory:

$ firebase deploy

For more info go to https://firebase.google.com/docs/hosting/deploying

Now you can sun the lighthouse tool and obtain a 100 score:

Capturing the event

Go back to the ionic app code, locate the pages folder on src/pages/home and edit the home.html and home.ts as follow:

home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  The world is your oyster.
  <p>
    If you get lost, the <a href="http://ionicframework.com/docs/v2">docs</a> will be your guide.
  </p>
  <button class="btn" ion-button full (click)="add_to_home(event)" *ngIf="showBtn" >Install</button>
</ion-content>

home.ts

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

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  showBtn: boolean = false;
  deferredPrompt;
  constructor(public navCtrl: NavController) {
    
  }

  ionViewWillEnter(){
    window.addEventListener('beforeinstallprompt', (e) => {
      // Prevent Chrome 67 and earlier from automatically showing the prompt
      e.preventDefault();
      // Stash the event so it can be triggered later on the button event.
      this.deferredPrompt = e;
      
    // Update UI by showing a button to notify the user they can add to home screen
      this.showBtn = true;
    });
    
    //button click event to show the promt
            
    window.addEventListener('appinstalled', (event) => {
     alert('installed');
    });
    
    
    if (window.matchMedia('(display-mode: standalone)').matches) {
      alert('display-mode is standalone');
    }
  }

  add_to_home(e){
    debugger
    // hide our user interface that shows our button
    // Show the prompt
    this.deferredPrompt.prompt();
    // Wait for the user to respond to the prompt
    this.deferredPrompt.userChoice
      .then((choiceResult) => {
        if (choiceResult.outcome === 'accepted') {
          alert('User accepted the prompt');
        } else {
          alert('User dismissed the prompt');
        }
        this.deferredPrompt = null;
      });
  };

}

Test in your browser

To test the functionality, we could make some changes on chrome to force to trigger the event (taken from here https://stackoverflow.com/a/50626433/4320602)

For security reasons, as others have written as well, browsers don't allow you to manually trigger the install event.

However, there is a way you can test it yourself. Go to chrome://flags and enable "Bypass user engagement checks"

This will kick off the prompt so you can test.

now run your app and click on the install button, you should see something like this:

Now the app will run as standalone app:

Happy coding,
Cheers

ionShopping

ionShopping is a intuitive, clean and professional hybrid app, perfect for e-commerce, online shopping, etc. It is a completely functional template/starter for ionic developers to create a multi-platform shopping app with shopping cart, custom powerful push notifications, and a custom Admin panel to manage the orders, the list of products, the images, etc.

What am I getting?

When you download ionShopping you'll obtain the source code of the ionic app and the angular dashboard admin panel, plus an awesome documentation that will walk you through the setup process, step by step, the firebase setup, the setup of the environment, etc. The process should be smooth, but if you encounter any issue with the setup of the project, it will be my pleasure to assist you via email, my email is on the documentation.

Features:

ionic app:
- Login (with email/pass and social login with facebook and google plus)
- Recover password
- Sign up
- Tabs system to provide a more clean experience to the user
- Product list (manageable from the admin panel)
- Items details section
- Awesome order system/Shopping cart + checkout with paypal integrated
- Call number native and send SMS from the landing page to contact the store
- Push notifications (with custom dashboard) for promotions with promo landing page
- Editable profile

Admin dashboard:
- Manage all the items, details, images, prices, etc from the amazing custom admin panel included
- Tracking Order (shopping cart), from the admin panel you'll see and manage the status of the order (purchased, processing, delivered).
- Powerful and custom Push Notification system to send notifications to user with rich text and images, to inform or promote offers discounts, etc. You can run a promotion from an start date to an end date, you can also create and save notification to be sent in the future
- Technologies used on the dashboard: Angular 4, Cloud firestore

Ionic App screenshots










Admin dashboard demo

The admin dashboard source code is included with the ionic code

Try before you invest

Note:
The notification and the social login will only work on an emulator or a cellphone

Get it from here:
https://market.ionicframework.com/starters/ionshopping/

ionRestaurant

ionRestaurant is a intuitive, clean and professional hybrid app, perfect for Restaurant, Bar, Bakery, etc. It is a completely functional template/starter for ionic developers to create a multi-platform restaurant app with food ordering system (shopping cart), custom powerful push notifications, and a custom Admin panel to manage the orders, the menu, the images, etc.

What am I getting?

When you download ionRestaurant you'll obtain the source code of the ionic app and the angular dashboard admin panel, plus an awesome documentation that will walk you through the all setup process, step by step, the firebase setup and the setup of the environment, etc. The process should be smooth, but if you encounter any issue with the setup of the project, it will be my pleasure to assist you, my email is on the documentation.

Features:

ionic app:
- Login (with email/pass and social login with facebook and google plus)
- Recover password
- Sign up
- Tabs system to provide a more clean experience to the user
- Restaurant Menu (manageable from the admin panel)
- Items details section
- Awesome order system/Shopping cart + checkout with paypal integrated
- About us + contact section (With maps, gps, call number native)
- Push notifications for promotions with promo landing page
- Profile editable

Admin dashboard:
- Manage all the menu, images, plates, prices, etc from the amazing custom admin panel included
- Tracking Order (shopping cart), from the admin panel you'll see and manage the status of the order (purchased, processing, delivered).
- Powerful and custom Push Notification system to send notifications to user with rich text and images, to inform or promote offers discounts, etc. You can run a promotion from an start date to an end date, you can also create and save notification to be sent in the future
- Technologies used on the dashboard: Angular 4, Cloud firestore

Ionic App screenshots

Admin panel screenshots

Try before you invest

Note:
The notification and the social login will only work on an emulator or a cellphone

Get it from here:
https://market.ionicframework.com/starters/ionrestaurant

ionPushemPro

You can use this starter to create your application on top of it and send and receive notifications with your own admin dashboard backend.
If you are a freelance developer, you can offer the dashboard backend to your clients as a plus, when you develop applications like e-commerce, for restaurants, anything where you need to communicate to the users about new features, products, promotions, etc.

The admin backend source code is included with this starter

With this starter you will be able to:

  • Send notifications to individual users
  • Create user groups and send notifications to group of users
  • Create channels/topics for the ionic user to opt-in/out and receive notifications via channesl
  • There is an option for the ionic user to create local notification (this is a generic functionality you should adapt it to your own needs)
  • Firebase and social login (google and facebook) with the ability to update the user's profile

Watch the following video for more info:

Live ionic demo

Note:
The notification and the social login will only work on an emulator or a cellphone

Get it from here:
https://market.ionicframework.com/starters/ionpushempro

Ionic CRUD Application with Cloud Firestore

Ionic CRUD Application with Cloud Firestore

ionic crud
In this tutorial we are going to create the most simplest ionic CRUD(Create, Read, Update and Delete) using Cloud firestore.

At the end of this tutorial you'll be able to:
-Setup a firebase account.
-Configure firestore database permissions.
-Create an ionic app using ionic cli (command line tool).
-Create, Read, Update and Delete to a firestore database from an ionic app.

What is cloud firestore?

Lets set up a firebase account.

Go to https://console.firebase.google.com and login with your google account or create one.

Enter the name of the project and click on Add Firebase to your web app.

We are going to use this data later, this will allow us to connect to firebase.

Next step is to configure Cloud Firestore, follow the following steps in the image, and start in test mode for now, to keep it simple.

For more rules you can visit here: https://cloud.google.com/firestore/docs/security/rules-query

Before we get into the ionic part, lets see how a firestore database structure looks like:

This example was taken from the ionAppFull4Pro ionic starter.
In this example you can see that the firestore data model is based on collections and documents, and you can nest collection within documents:

for more details you can visit here https://firebase.google.com/docs/firestore/data-model.

Lets create the ionic CRUD project.

First, install Node.js. Then, install the latest Cordova and Ionic command-line tools in your terminal. Follow the Android and iOS platform guides to install required tools for development. For more info go here: https://ionicframework.com/getting-started.

npm install -g cordova ionic

Now lets create the ionic app

ionic start crud blank

When the cli finish to create the files, do:

 > cd crud
 > ionic serve

To go to the new folder created, then run "ionic serve" to make sure that everything when correctly. This is how it should looks like:

Now, open the source code on your favorite editor, I prefer VS code. The source code should look like this:

Run the following command to install firebase dependency:

npm install firebase --save

We need to configure firebase on our app, for this we are going to need the config variable from previous step.

  var config = {
    apiKey: "<your key>",
    authDomain: "<your key>",
    databaseURL: "<your key>",
    projectId: "<your key>",
    storageBucket: "<your key>",
    messagingSenderId: "<your key>"
  };
firebase.initializeApp(config);

Insert the config variable on the app.module.ts for that lets add the firebase dependency.

...
import * as firebase from 'firebase';

const config = {
  apiKey: "<your key>",
  authDomain: "<your key>",
  databaseURL: "<your key>",
  projectId: "<your key>",
  storageBucket: "<your key>",
  messagingSenderId: "<your key>"
};
firebase.initializeApp(config);

...

Go to the home.html and insert this code.

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Crud example
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
<ion-list>
    <ion-item>
        <ion-label floating>Message title :</ion-label>
        <ion-input type="text" value="" [(ngModel)]="model.title"></ion-input>
      </ion-item>            
      <ion-item>
        <ion-label floating>Message body :</ion-label>
        <ion-textarea   type="text" row="10" [(ngModel)]="model.text"></ion-textarea>
      </ion-item>
      <ion-item>
          <button type="button" (click)="addMessage()" ion-button full >Submit</button>
      </ion-item>
</ion-list>

  <ion-card>
    <ion-card-header>
      Swipe item to the left to delete or edit
    </ion-card-header>
  </ion-card>
  <ion-list>
    <ion-item-sliding *ngFor="let message of messages">
      <ion-item>
        <h3>title: {{message.title}}</h3>
        <p>body: {{message.text}}</p>
      </ion-item>
      <ion-item-options>
        <button ion-button color="danger" (click)="deleteMessage(message.$key)">
        <ion-icon name="ios-trash"></ion-icon>
        delete
      </button>
      <button ion-button color="success" (click)="updateMessage(message)">
          <ion-icon name="ios-create"></ion-icon>
          edit
        </button>
      </ion-item-options>
    </ion-item-sliding>
  </ion-list>

</ion-content>

We are using an *ngFor to generate all the items on the list, we are creating sliding items so we can set a button to delete the item.

We need to add the methods for the ionic CRUD operation (got it from the ionAppFull4Pro project).

  getAllDocuments(collection: string): Promise<any> {
    return new Promise((resolve, reject) => {
        this.db.collection(collection)
            .get()
            .then((querySnapshot) => {
                let arr = [];
                querySnapshot.forEach(function (doc) {
                    var obj = JSON.parse(JSON.stringify(doc.data()));
                    obj.$key = doc.id
                    console.log(obj)
                    arr.push(obj);
                });

                if (arr.length > 0) {
                    console.log("Document data:", arr);
                    resolve(arr);
                } else {
                    console.log("No such document!");
                    resolve(null);
                }


            })
            .catch((error: any) => {
                reject(error);
            });
    });
}

deleteDocument(collectionName: string, docID: string): Promise<any> {
  return new Promise((resolve, reject) => {
      this.db
          .collection(collectionName)
          .doc(docID)
          .delete()
          .then((obj: any) => {
              resolve(obj);
          })
          .catch((error: any) => {
              reject(error);
          });
  });
}

addDocument(collectionName: string, dataObj: any): Promise<any> {
  return new Promise((resolve, reject) => {
      this.db.collection(collectionName).add(dataObj)
          .then((obj: any) => {
              resolve(obj);
          })
          .catch((error: any) => {
              reject(error);
          });
  });
}

updateDocument(collectionName: string, docID: string, dataObj: any): Promise<any> {
  return new Promise((resolve, reject) => {
      this.db
          .collection(collectionName)
          .doc(docID)
          .update(dataObj)
          .then((obj: any) => {
              resolve(obj);
          })
          .catch((error: any) => {
              reject(error);
          });
  });
}

Add the methods used by the view to add, edit,list and delete items:

  loadData(){
    this.getAllDocuments("messages").then((e)=>{
      this.messages = e;
  });
  }

addMessage(){
    if(!this.isEditing){
    this.addDocument("messages", this.model).then(()=>{
      this.loadData();//refresh view
    });
  }else{
    this.updateDocument("messages", this.model.$key, this.model).then(()=>{
      this.loadData();//refresh view
    });
  }
  this.isEditing = false;
  //clear form
  this.model.title = '';
  this.model.text = '';
}

updateMessage(obj){
  this.model = obj;
  this.isEditing = true;
}

deleteMessage(key){
  this.deleteDocument("messages", key).then(()=>{
    this.loadData();//refresh view
    this.isEditing = false;
  });
}

Now you can run:

ionic serve

The result should be something like this:

You can download the full source code from here:
https://github.com/jomendez/ionic-firestore-crud-example

Happy coding!!