I have a modest mobile game called Bravo! It’s written in TypeScript using the Ionic Framework version 3 and Apache Cordova. It is currently only available for the iPhone. A few weeks ago I started receiving emails from Google warning me that I was out of compliance with their ad policy, and the ultimately decided to remove it from the Play Store. As it turns out, it was a simple configuration issue, but in order to get reinstated, I have to submit a new APK. I decided to take this opportunity to convert the app to Ionic 4 with Capacitor. This post describes my experience during the process
New Project
From everything I have read, the easiest thing to do it create a brand new Ionic project, which I did with ionic start
. I made sure to tell it not to add Cordova.
From there, it was a simple thing to re-create each page with the Ionic CLI. With the latest CLI version, you can use either the ionic
or ng
commands (Yes, I’m using Angular also).
ng g page game
ng g page instructions
ng g page game-over
ng g page new-game
The Ionic CLI provides Ionic-specific schematics for the Angular CLI. This allowed me to create Ionic pages, as opposed to Angular components, which follow the Ionic recommendations for how pages should be initialized. My original game only had two pages, but I decided to add a couple of extras here: game-over and new-game. The CLI sets up routing and lazy loading by default. This is the routing provided for me automatically.
const routes: Routes = [
{
path: '',
redirectTo: 'home',
pathMatch: 'full'
},
{
path: 'home',
loadChildren: './home/home.module#HomePageModule',
canActivate: [InstructionGuard],
},
{
path: 'game',
canActivate: [InstructionGuard, InProgressGuard],
loadChildren: './game/game.module#GamePageModule'
},
{
path: 'instructions',
loadChildren: './instructions/instructions.module#InstructionsPageModule'
},
{ path: 'game-over', loadChildren: './game-over/game-over.module#GameOverPageModule' },
{ path: 'new-game', loadChildren: './new-game/new-game.module#NewGamePageModule', canActivate: [NewGameGuard] },
];
The canActivate
members are something I decided to add as a new feature, and I’ll get into those later. Everything else was provided automatically by the Ionic scaffolding.
Ionic 3 vs 4 Markup
The next thing I wanted to do was make adjust the HTML markup for my game page. This is where I needed to make the most changes, but I am sure you will agree the changes are not that drastic.
My game displays a series of random cards, one card at a time, with a word or topic, and a rule. The rule is something like, “Sing a song containing the word…. dog … in the lyrics.” In the v3 version, the card markup looked like this.
<ion-card (swipe)="onSwipe($event)">
<ion-card-header [ngClass]="card.class+'-bg'">
<h1></h1>
</ion-card-header>
<ion-card-content>
<div class="item-text-wrap item text-center">
<h2> </h2>
<h1></h1>
<h2> </h2>
<h1></h1>
</div>
</ion-card-content>
<ion-row>
<ion-col col-4></ion-col>
<ion-col col-4></ion-col>
<ion-col col-4>
<button ion-button block clear (click)="getNextCard()">Draw Card</button>
</ion-col>
</ion-row>
</ion-card>
This is the new v4 version.
<ion-card>
<div [ngClass]="card.class" class="bg"></div>
<ion-card-header>
<ion-card-subtitle>
</ion-card-subtitle>
<ion-card-title>
</ion-card-title>
</ion-card-header>
<ion-card-content>
<div class="item-text-wrap item text-center">
<p> ""</p>
</div>
</ion-card-content>
</ion-card>
I took the opportunity to make some stylistic changes, but otherwise the card format is similar. The biggest difference on the game page was the markup for keeping score, which consists of 6 buttons of different colors, each showing the score for that particular color.
This is the v3 markup for the scores.
<ion-footer>
<ion-toolbar color="dark">
<ion-row>
<ion-col col-2>
<button ion-button block clear class="blue-bg score" (click)="addScore(0)"></button>
</ion-col>
<ion-col col-2>
<button ion-button block clear class="red-bg score" (click)="addScore(1)"></button>
</ion-col>
<ion-col col-2>
<button ion-button block clear class="green-bg score" (click)="addScore(2)"></button>
</ion-col>
<ion-col col-2>
<button ion-button block clear class="yellow-bg score" (click)="addScore(3)"></button>
</ion-col>
<ion-col col-2>
<button ion-button block clear class="pink-bg score" (click)="addScore(4)"></button>
</ion-col>
<ion-col col-2>
<button ion-button block clear class="brown-bg score" (click)="addScore(5)"></button>
</ion-col>
</ion-row>
</ion-toolbar>
</ion-footer>
And here is the adjusted markup for v4.
<ion-footer>
<ion-toolbar color="dark">
<ion-grid>
<ion-row>
<ion-col size="2">
<ion-button expand="block" fill="clear" class="blue-bg score" (click)="addScore(0)"></ion-button>
</ion-col>
<ion-col size="2">
<ion-button expand="block" fill="clear" class="red-bg score" (click)="addScore(1)"></ion-button>
</ion-col>
<ion-col size="2">
<ion-button expand="block" fill="clear" class="green-bg score" (click)="addScore(2)"></ion-button>
</ion-col>
<ion-col size="2">
<ion-button expand="block" fill="clear" class="yellow-bg score" (click)="addScore(3)">
</ion-button>
</ion-col>
<ion-col size="2">
<ion-button expand="block" fill="clear" class="pink-bg score" (click)="addScore(4)"></ion-button>
</ion-col>
<ion-col size="2">
<ion-button expand="block" fill="clear" class="brown-bg score" (click)="addScore(5)"></ion-button>
</ion-col>
</ion-row>
</ion-grid>
</ion-toolbar>
</ion-footer>
The primary change here is the markup for the buttons. Ionic made a breaking change between v3 and v4. The original buttons were <button>
elements with ion-button
, block
, and clear
attributes. The new v4 syntax replaces that with an <ion-button>
element, with the more standard-looking HTML attributes expand="block"
and fill="clear"
.
Not only is the new syntax more HTML-standard, using attributes in this way makes it far simpler for developers to bind these values to variables on your component class.
Component Code
Quite frankly, there is nothing to report here. The component code did not change during the upgrade. I did make some game-play changes, but these were incidental to Ionic 4.
Page Modules
With Ionic 4, each page comes with its own module, which helps make lazy-loading easier. My v3 version of the game did not have any modules other than the app.module.ts file.
Cordova vs Capacitor
This is where the biggest changes occurred. The game does not make use of any device-specific features. Cordova was primarily the means to load and run it on the devices.
In fact, it runs just fine as a progressive web app (PWA). When it is finished, I am seriously considering shipping a PWA version as the primary means of distribution. Using Capacitor to create iOS and Android versions will be mostly an excercise.
Adding Capacitor
So far, I have not done anything with Capacitor. Sure, I looked at it, read about, etc. This was my first attempt at using it on a real project. I started by making sure I had all of the required dependencies, as explained here.
This meant having installed:
- CocoaPods
- Xcode Command Line Tools
- Android Studio
- JDK 8
Next, I added capacitor to my existing Ionic project with
ionic integrations enable capacitor
The Ionic docs differ at this point from the instructions that display at the end of the above command. The docs say to run
npx cap init [appName] [appId]
However, it appears that command was already done. I ran it anyway, and it updated the values in capacitor.config.json
. So I guess it does not hurt to run it.
Build App
Next, the docs warn that you need to build the app once before adding any platforms.
ionic build
This built my www
folder, which will be used by Capacitor.
Creating iOS App
My primary development machine is a Mac Mini, so building the iOS app seemed to be the natural next step. Capacitor is installed as a development dependency of your Ionic project, which means it is not available globally. To execute any Capacitor command, you must prefix it with npx
.
npx cap add ios
As it warned, this step took several minutes: 424 seconds, to be exact. It created an Xcode project and installed a bunch of dependencies through CocoaPods. It also provided me with a few dozen images to use as icons and splash screens. I already had a bunch of those for Bravo, so they will need to be replaced.
Opening the iOS project in Xcode is a simple command.
npx cap open ios
It did not go smoothly at first. Xcode did not have my developer account; apparently I had not used it since buying the new Mac. In order to download my developer certificates, I first had to log into my account online and accept the latest developer agreement. That out of the way, I was able to continue.
Xcode recognized everything properly. Building and deploying the app to my iPhone 7 was a single click. The app launched and I could immediately begin to play the game.
All in all, it was a very smooth experience.
Creating Android App
Next up was the Android app. I have a Samsung Galaxy S7 sitting here collecting dust, so that was to be my target.
Similar to the iOS app, it is a single command to add Android as a Capacitor target.
npx cap add android
This command completed many orders of magnitude faster than the iOS version. It barely took 13 seconds to add Android.
Similarly, opening the project in Android Studio was another simple command.
npx cap open android
Android Studio opened the project without complaint. Selecting the Run | Run 'app'
menu, it recognized the Galaxy S7 I had connected. From there I selected the device and clicked the Run
button. A few seconds later, Bravo appeared on the phone.
Conclusion
Quite frankly, that was smoother than I expected. Running on the Android was a slight easier than the iPhone, but neither experience was painful. Given that I had not done this before, and was making multiple changes to a project at one time, I am pleased with the overall experience.
I can honestly recommend this to anyone with an Ionic v3 app thinking about upgrading. Granted, my game was a modest one, with only a few pages, so if your app is larger, you may not get it done in one afternoon as I did here.
Next Steps
There are still a few things left for me to do.
Icons and Splash Screens
As I mentioned above, adding Capacitor provided me with a folder of various-sized images to use for icons and splash screens. I need to make sure my existing Bravo! icons and splash screens can be dropped in before going further.
Ad Changes
My v3 code used a Cordova plugin for displaying AdMob ads. I deleted that with my new v4 code, and I have not yet decided how or even whether I want to display them in this app. Given my recent experience with free, ad-supported applications, I may make it a paid app and let things run their course.
Deployment
Of course, I still need to sign and deploy the apps to the respective stores. Depending on the complexity of that process, I may write another post after the fact. At this point, I am happy with the progress I have made in only a few hours.
References
Do you have any comments, questions, or just want to see more? Please follow me on Twitter and let me know.
Did I make any mistakes in this post? Feel free to suggest an edit.