Ionic 5: an insightful appraisal

In this article, I would like to share with you my personal observations about Ionic. My subjective opinion presented below is based on a retrospective of developing a shop application for Android and iOS. In addition, I have put some code snippets at the bottom of this article that can help many developers in overcoming encountered issues and to build their apps faster. I learnt Ionic the hard way, facing many errors and issues, figuring out how to solve them, but at the end I finished and deployed the application in time and in consequence I gained a lot of experience, therefore I can proudly say: enjoy my finis coronat opus!

Photo by Austin Distel on Unsplash

If you stand before making a decision regarding the choice of technology for creating a mobile application, you should explore and analyse every possible option, that the IT world offers, very carefully. Every technology has an encouraging documentation that presents a straightforward concept, shows essential details about the tool and its ecosystem, highlights the structure and the development process in a good light and most importantly gives us a perspective how to build and deploy visually attractive, flexible applications with an ease. This perspective can delude you from the truth in the context of long-term development and production shipment. You can perceive it as a far-fetched idealism. The beginnings are usually easy, but when our application gets bigger we encounter many problems, we spend a lot of time fixing bugs and integrating new features, sometimes we need to apply a dirty solution to make something work, we tend to abandon a task, because it’s impossible to code it — given the fact that the technology we’re using does not provide required resources to complete this feature, we are frustrated when we don’t find something in the documentation or when we enter an error message, popped out by our compiler, in Google, and as a result we get zero information about the problem. Generally, we are forced to do a much more comprehensive overview of the tool that we are using. If you ask me, I don’t really read summaries or comparisons of technologies, because I don’t like unanswered questions. I prefer to read articles with a developer’s perspective that gently or strongly suggest to use or not to use a specific technology. I consider a perspective like this as a benefit and a decisive factor in use of a particular technology. This article will be one of them.

Exactly one year ago I developed a booking application in Ionic 4 (and Cordova) for a client. It was a very tedious work that lasted several days. The process of creating new features, adapting them to Android and iOS devices, deploying and testing was a really painstaking work, but I’ve learned, in order to find a needle in a haystack, you have to go through the hay. I examined the documentation from head to toes, have read a lot of articles, spent some time on Ionic forum, YouTube and StackOverflow and all of this work, motivation and accumulated knowledge led to synergistic actions like effortless feature integration, bug tracing, fixing and overall rapid development, which resulted in customer satisfaction. However, I wasn’t entirely happy and I wasn’t planning to come back to Ionic anymore, but I realised that the Ionic code-base still grows, new changes are constantly presented to the world and I believe that Ionic team is devoted to compete with other mobile technologies and bring their technology to highest standards. Why was I not happy?

At first glance, coding a mobile app in Ionic brought me a lot of joy. As an Angular developer, I started to build pages very fast with the help of directives, pipes and other Angular features. Ionic provides plenty of UI components, available and easy to use — cards, buttons, toggles, segments, modals, inputs, lists, row/column grid. I felt in love with some of the plugins like Camera, Push Notifications etc. Most of the time, I developed in the browser — except for native phone functionality, which I had to debug using a phone — and as a result of that it speeded up the work significantly. While the application was growing, I stumbled upon many bugs and errors, many pitfalls related to hybrid development which lead to performance issues and reducing my productivity. It turned out that many plugins are unstable and can conflict with other plugins, but it was necessary to use them, because Ionic did not provide any alternatives by default. Styling components was exhausting — although Ionic provided several CSS properties, they were still not enough. The longer the application was active, the slower it worked. Foreground and background handlers seemed to be buggy and caused app crashing. Unfortunately, the application, did not give any real native impression. There were many notorious situations where white screens appeared, making an impression that the user was actually browsing a website and not an application.

In spite of all the delightment I’ve experienced in the beginning of my journey with Ionic, the deployment process was extremely frustrating. I struggled with running Ionic on an emulator, so I gave up and used my phone to test my application. Making a minor change and deploying it back on my phone did cost me a lot of time. Figuring out how to deploy applications on Google Play took me some hours, but frankly speaking, it was not a hard job to generate some keys and build a APK file. However, deploying it on Apple Store was a completely disaster. Several dozen hours spent on configuring and praying to Xcode to spit out a success build message. I faced a lot of errors and I tried to Google them, but in the result, I did not receive any concrete and specific help to solve my problems. The deployment process described in the documentation was not intuitive, very minimalistic and the knowledge concluded was not useful for me, so I had to act on my own. End of ends, I was able to push the application to the store. This situation finished me off mentally, but me and my team eventually did it.

One year passes and the cycle repeats. We received a huge task to create a shop application with payment integration. The time required to complete the application turned out to be very short, so we had to act immediately. Given that we know Angular, we decided to give Ionic a last chance. It turned out that Ionic released a new version, so it got me thinking that the dev team has fixed many bugs, introduced new features and updates to mainly improve development work. I also noticed that Ionic started to use a new native runtime, called Capacitor — so I had to choose: Cordova (stick to the thing I already have used) or Capacitor (try something new). I have chosen Capacitor having in mind that this is technology is completely new and that I can find myself in a hopeless and impossible situation, that would prevent further development due to poor or missing functionality of this technology. One month has elapsed and the application has been completed. The choice of the Capacitor was a bull’s-eye, but the disgust for Ionic remained. Here is my list of pros and cons based on my experience with Ionic:

  • Neat and clean UI components that you can easily put in your page. In version 5, Ionic team focused specifically on iOS components, which in my opinion, was a good move, because iPhones are being seemingly everywhere and recently became a very popular product. Just place a component in your template, set style properties, bind events to it and you’re ready to go. You can build your view fundamentals in a blink of an eye, thanks to Ionic, providing us with native toolbar, tab selector, and swipe-able side menu. The next step of your work is just adding more elements to your view (like cards or lists) and positioning them at its sole discretion.
  • Wide range of plugins makes it almost impossible not to be able to pick a desired plugin and use it in your project. A big plus is also examples included for each plugin saying how to install and use the most important functionalities of a particular plugin. You can integrate a local storage and hold your data there, use the Camera functionality to make photos directly on your phone and send them to your API, additionally you can set up Push Notifications, so users can receive alerts even when the application is turned off. Moreover, it has things like Finger Scanning, QR Code, device management etc. The list is very long, you might burn your mouse scroll when examining all of them. An important point to highlight is that Capacitor supports a wide range of Cordova plugins, so your app will be compatible with old plugins.
  • Keep your learning curve still at the same point. What does that actually mean? It means that if you know Angular, you automatically know Ionic. There are some additional things you have to be aware of (eg. page lifecycle) but concepts like these are obvious and simple to grasp. Ionic runs and displays applications on mobile devices thanks to WebView, so you don’t have to idly your head over and change your thinking process to adapt to this framework, because there is no difference considering the technical requirements and environmental conditions. Think and feel like you are still coding a website in Angular. All services, directives, pipes, forms, interceptors work flawlessly on Android and, of course, iOS devices.
  • Test your application in three possible ways: browser, emulator or your mobile phone. Ionic provides hot-reload feature, so your app is automatically restarted when file changes in your project are detected. With Capacitor you can now test your app in emulator using the live-reload mode with greater ease than in Cordova. I was able to run my emulator in Android Studio with no errors thrown by Capacitor and I didn’t have to downgrade my Java SE to version 8. Personally, I recommend to start working with an emulator right after you create your project, because you can test native functionalities provided by the plugins and most importantly see how added components interact with each other and how they are arranged on the screen. This obviously reflects the actual appearance of your application. The third option is also a good way to check your application, but remember to enable the developer mode on, so Android Studio will build and run your app on your physical device with no problems.
  • Easy deployment process: Capacitor embraces the idea of “Code once, configure everywhere”. This means configuration is managed in one file for both platforms. On top of that, Capacitor uses Android Studio and Xcode to build respectively Android and iOS bundles. With Android Studio, you can generate keys to sign your app, build an Android App Bundle, sign this bundle with your newly generated keys and push it to Google Play. Whereas for iOS, you have to create a certificate on Apple Store for you app, import it on Xcode and lastly build your app and push it to Apple Store.
  • UI components indeed look good, especially on iOS devices thanks to new updates in Ionic 5, but styling them can sometimes lead you to agony. There are CSS properties that are not working correctly, meaning setting them does not update the view of your component. If you want to style something that is not provided by Ionic, you have to override the styles in a global CSS file with the usage of !important declaration. When building a chat, I was going to create a loader that after scrolling up, it would fetch another portion of messages and display them correctly. Ionic allows to place such a loader on top of the screen. Unfortunately, the events built in the component, fired up immediately after page initialisation. While fixing this, I encountered other bugs, so I gave up and used refresher instead with a help of a scroll position listener. When I was building a list, I used refresher to refresh the data of listed elements. On iOS devices, there is a bounce effect, that you can achieve by swiping up or down the page. This very bounce effect caused an undesirable behaviour — saying more clearly, the refresher could not be triggered or the loading icon was not displayed. Next, when your mobile keyboard pops out, it pushes the text-area component out of container. If you want to display multiple elements per slide (using slide component) you are forced to do some dirty tricks, like setting a negative margin spacing, calculate an appropriate width of these containers etc. I was happy when I saw that you can now swipe modals, but my happiness did not last long, because from time to time swiping the modal turned the display into inoperative and uncontrolled state (modal suddenly closes after opening). There are more issues and bugs I’ve encountered, but I’m not going to list all of them, most of them are minor problems, but still, those problems make the application lose its charm.
  • Ionic offers a wide a range of plugins, but half of them are deprecated, not developed and not updated. Some of them may break your code, some of them do not work exactly as supposed to be. I remember when I wanted to add a file handler to my application. Everything was working good until I built my project in Android Studio. I had to dig into a Java class file and change the implemented interface to make it work. Such a workaround like this should never exist!
  • Splash screen is an essential key of our application. It is typically used to enhance the look and feel of our application, hence it is often visually appealing — yet I feel that splash screen in Ionic is still underdeveloped. If you want to use an image of your desire, you have to use a Cordova tool to generate multiple sizes of your image for each platform. Even though, you have multiple variants of your welcome image, the picture may not be displayed properly (in my case the logo of my picture exceeded the viewport of the screen). Later, when the screen loads, there is a bumpy and chompy transition to a loader which is a picture (the same as the welcome picture) with a spinner on top of it. In my case, the sizes of the welcome picture and the loader picture are different. It makes the app look bad.
  • Application may look slightly different or work different on Android and iOS. The reason behind this is that your app runs on two different browsers (Android Browser on Android, Safari on iOS). Be careful when using a new CSS property or a JS feature, because it might be not supported on Safari. You have to check the compatibility of the things you want to include in your application and if something is not compatible, you need to find an alternative solution. I remember when I added a slide animation to an element hidden behind a button. I used z-index to achieve this effect. Quite astonishingly, the effect did not look great on iOS device. Instead of sliding underneath out of the parent element, it appeared on top of the parent element and then slided out. After some time, it turned out that adding transform: translate3d(0, 0, 0) fixes this issue.
  • Absence of notch detector means that developers have to write their own functionality to detect the device cut-out and adjust the content of the page accordingly. Functionality like this should be included in the Platform service already!
  • Documentation of Ionic plugins seems to be kept to a minimum, but in fact, it is poorly written. We only see one or two sentences about the plugin, from which we are unable to learn or deduce anything meaningful. Each plugin has an example, but it doesn’t really cover the majority of what the plugin offers. Documentation of UI components is better, but still it lacks additional information about the advanced usage of particular component. I am convinced that you will spend more time browsing forums than on the documentation itself. To be honest, you’ll usually find more key information out there than in documentation. Example: My goal was to remove the focus in search-bar component right after someone clears the text. I couldn’t find such information in the documentation. Later on, I found out that I had to use querySelector to find the input element which was embedded in the component and then execute the blur function on this element.
  • Performance: you have to take into consideration that Ionic is a hybrid application, which is a web app built using HTML5 and JavaScript, wrapped in a native container called WebView. Hybrid apps are much slower than native apps. When I launched my app for the first time, it took like 1–2 second(s) to load my assets displayed on the home page. This is because Ionic loads most of the information on the page as the user navigates through the application. I have to admit that functions written by me, e.g: product adder, cart shop viewer, order creator and payment handler run very fast and there is nothing I can fault with. However, I remember when I added few animations, a blur effect on modal open and some other CSS magic things together with Cordova functionality, then the app started to slow down. Also, I recall a situation where I created a timer and I wanted the timer to work when the application was in background mode. When I returned back to my app, it crashed, every time. It was very hard to trace this problem. I gave up on that. I finally realised that things like CSS animations, DOM updates and Cordova callbacks can bog down the UI with Ionic on mobile devices and performance bottleneck can occur unexpectedly. This is the price you have to pay.

Let me go straight to the point. If you plan to create a dummy, small or a basic application, Ionic is the perfect technology to use for this specific task. On the other hand, if you want to create a complex application with lots of features and integrations, I recommend using a native technology, e.g. Flutter. However, if you still prefer to use Ionic, I prepared for you some code snippets that will definitely help you in developing your application better and faster.

Snippets

If occasion arises to use a third-party library or other node built-ins (e.g. AWS SDK), you have to create global environment in your Ionic project. If you are using a library that assumes these globals are present, you can try manually shimming it inside your polyfill.ts file:

(window as any).global = window;

In tsconfig.app.json set:

"types": [
"node"
]

Refer to this article.

In your modal view, add a reference to the container in which you want to enable and listen for swipe events.

In your TS file add the swipe functionality:

Functionality provided above allows you to close a modal on condition that you have to touch the screen with your finger in the area starting from the top up to 175px down and you must slide your finger down and the length of your finger’s travel must be more than or equal to 50px.

In your view:

In your stylesheet (change it as you wish):

Add this functionality in your .ts file:

If you create a slider in a modal component, you may encounter an error that causes our slides to move incorrectly. To fix this, place this line of code in ionViewDidEnter method:

this.imgSlider.update().then();

In your component:

In global.css file:

In your template:

In your component:

Never use Subjects as a source of truth in guards, because when you minimize your app and open it back, your guard will fire again and it can take some time for the user object to be emitted by your subject (http call to validate user). In consequence, you can see a blank white screen for few seconds. Reach to a storage directly.

In your template:

Jump to TS file:

Mobile phones with notch (e.g: iPhone X) has a screen that covers the entire face of the phone. The design you’ve prepared and created can differ between devices with no notch included. With the new constants of CSS, browsers can detect the area of the screen that was covered by the notch and move content around appropriately. If your view has been pushed up because of notch, you can use this code to correct your view:

body {
margin-top: constant(safe-area-inset-top); // or padding
margin-top: env(safe-area-inset-top); // or padding
}

There are few ways to unsubscribe from a source when leaving a component. One of them is using a RxJS operator called takeUntil and I assume some of you are using it systematically like so:

Speaking of Ionic, it looks a bit different and you can easily fall into a trap. You must pay attention on how Ionic page life-cycles work. In this case, the source will be unsubscribed only once, after the next entry a new observable will be created and when you leave the page, the takeUntil operator will not fire. Similarly, any next entry will create a new observable. It can considerably worsen the performance of your application. Why is that happening? Because the constructor and ngOnInit method is executed only on the first visit of the page. In order to unsubscribe properly, you have to move this.destroy$ = new Subject<void>(); from constructor to ionViewWillEnter() method. Remember also to move the unsubscribe part from ngOnDestroy() to ionViewWillLeave() method.

You have to pay close attention to the matter related with switching between accounts in your application. Let’s say, you go to your profile page, then you logout from your account, log-in to another one and go to the profile page. There is a high probability that you’ll see data fetched for your previous account. This is happening, because the profile page has been initialised already in the application life-cycle and in addition, you have stored your data in an object and you haven’t cleared data or unsubscribed from a source on page leave. The best way for avoiding a behaviour like this, is removing all the pages, the user has visited, from the page stack on logout. You will be guaranteed that the user will see correct data connected with his account.

Use NavController to set a new root page of your application. First:

constructor(public navCtrl: NavController) { }

Now, in your logout method:

this.navCtrl.setRoot(LoginPage);

The second approach (personally, I prefer this way) is using Angular’s routing option replaceUrl so we can replace the current state in history with the new state:

this.router.navigate([‘/login’], { replaceUrl: true });

If you use a FileOpener library or any file-related libraries provided by Cordova and you come across a build error in Android Studio, you have to get your hands dirty a little bit. Find FileProvider.java file and manually change

public class FileProvider extends android.support.v4.content.FileProvider { }

to the following:

public class FileProvider extends androidx.core.content.FileProvider { }

If you don’t know how to open files in Capacitor this piece of code can help you:

From time to time, I will come back to this article and update the snippet section with new tips and tricks.

That’s it. Thanks for reading. If you liked the post, please give me an applause. If you have any questions, feel free to ask them in the comments!

Full-stack JavaScript developer