Takeaways From Migrating an Ember-Cli iOS App from Cordova to Capacitor

Recently, I replaced Cordova with Capacitor in an iOS application. Cordova was working well for the app’s need for the most part, however the Ember-cli library for Cordova hadn’t been updated in a while, and the dependencies began to conflict with others that had already been upgraded. Unfortunately, contributing to yet another open source project wasn’t an option – for this one, I needed to find a solution that was already built.

Thankfully, there is a relatively up-to-date library integrating Ember-cli and Capacitor, ember-cli-capacitor. Also, there were only a couple modules to migrate within the application, and there was full support for Capacitor in both of the modules required by the app – payment and push notifications. The implementations were straightforward; what wasn’t straightforward was learning the intricacies of Capacitor. I had to spend some time configuring Capacitor in order to get it to work, and I had to hunt for some of the configuration changes I made. My hope is that sharing some of the solutions I found will help save you some time, too.

1. You don’t need to use Ionic when using Capacitor

I see “Ionic + Capacitor” in various places, and before I had the Capacitor app working, I was unsure whether Ionic is needed in order for Capacitor to work. It isn’t. I haven’t used Ionic personally, but it seems like a framework that could potentially be used in conjunction with Capacitor (or another platform) to speed up cross-platform development.

2. By Default, Capacitor Intercepts Third-Party Push Notifications

Dictate.life uses OneSignal to deliver push notifications (Thanks, OneSignal!) After implementing the client library in Capacitor and sending a test notification, I wasn’t receiving anything. After setting up OneSignal’s verbose logging and confirming that the network requests for the notifications were being sent and received, I began to look elsewhere for some sort of conflict or error. I eventually stumbled upon ios.handleApplicationNotifications config option, a directive which tells Capacitor to not intercept push notifications. Once I set that value to false, the notifications came through.

3. The Configuration Parameter server Needs to be Disabled for Production Builds/Deploys

Capacitor configuration allows for a server parameter, which defines the web server that the Capacitor app will point to. This config parameter is incredibly useful when doing local development, since you can pass the URL of your local frontend server to Capacitor, run npx cap sync, and then you’ll be able to develop your frontend app in the iOS Simulator. If your frontend app has live reload enabled, Capacitor’s server parameter allows you to take advantage of that feature, too.

Setting the server parameter is great for development, but when I went to create my first build for TestFlight, the resulting application presented a white screen. I ended up realizing that I needed to make the config file smarter by omitting the server parameter when creating a build for TestFlight.

I did this by conditionally adding the server parameter if a DEV environment variable was present. Then, I prepended that environment variable to my run command for starting the Capacitor stack. If you’re using the json variant of the Capacitor config file, you’ll need to switch to either the .js or .ts version.

Sample Typescript config file:
import { CapacitorConfig } from "@capacitor/cli";

declare var process: {
  env: {
    [key: string]: string | undefined;
  };
}

const config: CapacitorConfig = {
  webDir: "dist"
}

if (process.env.ENV == "dev") {
  config["server"] = { url: "http://my.dev.url" };
}
Command for starting Capacitor stack:
ENV=dev ember serve

4. The Default Capacitor App is Named ‘App’ and Needs to be Renamed for Real Builds

The default name App needs to be renamed to your application’s name if you intend to deploy it to the Apple App Store. Changing the default app name is straightforward, but needs to happen in two places:

1. The Podfile located in ios/app:

2. The name in Xcode’s App > TARGETS:

After renaming, run npx cap sync to propagate the changes.

Conclusion

This migration was relatively easy. In fact, it was fun! Capacitor seems to be working well – I deployed the app (Dictate.life) to the App Store last week, and apart from a couple issues being fixed, it was hard to even notice that the migration occurred. This is the best case scenario! I hope you have similar luck migrating your app to Capacitor!

updated 10.11.2023

Mobile Application Development, Software Engineering