In the recent NativeScript 6.0 release, we announced a new beta service called NativeScript AppSync, which allows you to update NativeScript-built iOS and Android apps without using the App Store or Google Play.
If you haven’t yet seen this feature in action, this 7-minute segment from the recent NativeScript 6.0 release webinar will get you up to speed.
In this article I want to go beyond the basic demo, and answer some common questions about using this service. Like, why would you want to do this in the first place, and, is Apple cool with me enabling this in my app?
Everyone that has updated an Android or iOS app hates the process. Each update involves a number of manual steps, like creating a new release build, changing a bunch of configuration like version numbers, writing release notes, and, on iOS, waiting for a review before your updates go live.
And the problems go beyond developer productivity. Suppose you discover an error in your production app that’s costing your business money. To fix that error with the normal deployment processes you have to: manually create a release build of your app, upload that build to the stores, wait for an approval (on iOS), and then wait for your users to download and install that update on their devices.
That process can easily take days, and in the meantime, your business is losing money, and your users are getting frustrated with your buggy app. With NativeScript AppSync you can speed up the update process considerably.
There are a few different components of the AppSync service, and I think explaining them individually is the best way to show how the service works as a whole.
AppSync CLI
The AppSync service has its own command-line interface, which you can install with npm install -g nativescript-app-sync-cli
. After installation, you’ll have a new nativescript-app-sync
command on your terminal that you can use to interface with the AppSync server.
The CLI allow you to register for the AppSync service, add the applications you want to use the service with, upload code updates for those apps, and a whole lot more.
AppSync Server
The NativeScript AppSync server provides a front-end for viewing the access keys and deployment keys needed to connect your application code with the AppSync server.
The server also hold your app updates (aka your actual app files) when you upload them through the AppSync CLI.
AppSync Plugin
The final piece of the puzzle is the AppSync plugin, which you can install into any NativeScript app using tns plugin add
.
tns plugin add nativescript-app-sync
With the plugin installed, you need to add a call to the AppSync plugin’s sync()
method in your app using the code below.
import { AppSync } from "nativescript-app-sync";
AppSync.sync({
deploymentKey: "..."
});
The AppSync.sync()
method calls the AppSync server to check if any new updates are available for your app. If it finds one, the plugin method silently downloads that update in the background, and applies the update the next time the user restarts the app.
Technically you can call AppSync.sync()
anywhere in your app, and call it as many times as you’d like. However, the most common approach is to place the below code snippet in your app.js
file (for NativeScript Core and NativeScript-Vue), or in your main.ts
file (for NativeScript Angular).
import * as application from "tns-core-modules/application";
import { AppSync } from "nativescript-app-sync";
application.on(application.resumeEvent, () => {
AppSync.sync(...);
});
This code attaches an event handler to your NativeScript’s application resume
event, which NativeScript triggers every time users resume your app.
NOTE: A
resume
event occurs when the user starts your app, or when they switch to your app from a different application on their device.
What’s important to note is that AppSync.sync()
only determines whether an update is available, and if so, downloads that update in the background. The sync()
method does not actually apply the update—after all, it’s the app’s source code itself that needs to change, which isn’t something you can do while the app is running.
That being said, there are some options you can use to confirm exactly how your application syncing works.
The AppSync plugin’s sync()
method allows you to configure how your application syncs work in a number of different ways. Here’s a quick look at some of the options available, along with their default values.
import { AppSync, InstallMode, SyncStatus } from "nativescript-app-sync";
import { isIOS } from "tns-core-modules/platform";
AppSync.sync({
deploymentKey: isIOS ? "your-ios-deployment-key" : "your-android-deployment-key",
installMode: InstallMode.ON_NEXT_RESTART,
mandatoryInstallMode: isIOS ? InstallMode.ON_NEXT_RESUME : InstallMode.IMMEDIATE,
updateDialog: {
updateTitle: "Please restart the app",
optionalUpdateMessage: "Optional update msg",
mandatoryUpdateMessage: "Mandatory update msg",
optionalIgnoreButtonLabel: "Later",
mandatoryContinueButtonLabel: isIOS ? "Exit now" : "Restart now",
appendReleaseDescription: true
}
});
Here’s some more information on how each of these options work.
deploymentKey
: The deploymentKey
is required as it’s what connects your application code to the AppSync server. You will have different keys for iOS and Android, so the sample code includes a ternary check that allows you to paste in both values.
installMode
: By default, AppSync only installs updates when the user restarts an app. AppSync does, however, have the ability to immediately apply updates, and will if you set your installMode
to InstallMode.IMMEDIATE
. There is one big caveat to this: InstallMode.IMMEDIATE
shows an installation prompt (see images below), which Apple does not allow for iOS apps distributed through the App Store. Therefore, only set InstallMode.IMMEDIATE
on iOS if you’re building enterprise-distributed iOS apps.
mandatoryInstallMode
: When you release AppSync updates through the AppSync CLI (nativescript-app-sync release
), you can optionally designate that release as mandatory using the --mandatory
flag. The mandatoryInstallMode
option gives you the ability to immediately install updates you mark mandatory, while allowing non-mandatory updates to wait until the next app restart. Once again, do not use immediate releases for App-Store-distributed iOS apps.
updateDialog
: The AppSync plugin has a built-in mechanism for displaying a dialog informing users about updates when using InstallMode.IMMEDIATE
. The gifs below show the dialogs in action on Android and iOS. On Android, the plugin is able to immediately restart your app with the new changes in place. On iOS, apps are not allowed to restart themselves, and as such, the plugin dialog instead instructs users to restart the app manually. (Presumably, this poor user experience is why Apple doesn’t want you to use this sort of workflow for App-Store-distributed apps.)
The AppSync plugin’s sync()
method also accepts a callback function as a second parameter, which reports on the sync()
method’s progress. Here’s what that looks like in action.
import * as application from "tns-core-modules/application";
import { AppSync, SyncStatus } from "nativescript-app-sync";
application.on(application.resumeEvent, () => {
AppSync.sync({
...
}, (syncStatus: syncStatus) => {
if (syncStatus === SyncStatus.UP_TO_DATE) {
console.log("No pending updates; you’re running the latest version.");
} else if (syncStatus === SyncStatus.UPDATE_INSTALLED) {
console.log("Update found and installed. It will be activated upon next cold reboot");
}
});
});
You might find this callback helpful during debugging or troubleshooting production apps.
Let’s start with the easy part of this question. If you’re developing iOS apps with an enterprise development certificate, you’re free to push updates however you’d like, and notify your users of those updates however you’d like; Apple really only cares about apps distributed through the App Store.
And when it comes to the App Store, Apple has this guideline in place.
Apps may contain or run code that is not embedded in the binary (e.g. HTML5-based games, bots, etc.), as long as code distribution isn’t the main purpose of the app, the code is not offered in a store or store-like interface, and provided that the software (1) is free or purchased using in-app purchase; ...
There’s a lot of legalese in the full text, but the key part is this.
Apps may contain or run code that is not embedded in the binary as long as code distribution isn’t the main purpose of the app.
Basically, as long as your app isn’t distributing code as a feature of the app itself, and as long as you don’t abuse AppSync to fundamentally change the purpose of your app (aka suddenly turn your calendar app into a gambling app), you should be good to go.
It’s worth noting that NativeScript AppSync is based on Microsoft’s CodePush project, which has been used by thousands of developers to deploy and update iOS applications.
NOTE: Apple has made it clear that they don’t want your apps showing update dialogs to users, so once again, make sure you’re not using
InstallMode.IMMEDIATE
on App-Store-distributed iOS apps.
There are a lot of other AppSync features you may want to incorporate into your development workflow.
For example, the AppSync CLI and server have the ability to handle staging and production releases separately, giving you the ability to test your updates locally before sending them out to your production users.
Additionally, both the AppSync CLI and Server offer data on your deployment history, including numbers on exactly how many users have received the updates you push out. You can even rollback updates using nativescript-app-sync rollback
if something goes wrong.
$ nativescript-app-sync deployment history ACMEAndroid Staging
┌───────┬──────────────┬─────────────┬───────────┬─────────────┬───────────────────────┐
│ Label │ Release Time │ App Version │ Mandatory │ Description │ Install Metrics │
├───────┼──────────────┼─────────────┼───────────┼─────────────┼───────────────────────┤
│ v1 │ 2 hours ago │ 1.0 │ No │ │ Active: 100% (1 of 1) │
│ │ │ │ │ │ Total: 1 │
└───────┴──────────────┴─────────────┴───────────┴─────────────┴───────────────────────┘
As you can see, there’s a lot that AppSync can offer to improve your development workflow, and help you get fixes out to your users faster. For more details, refer to the AppSync plugin’s documentation on NativeScript Marketplace, and if you have any other questions about using the plugin, feel free to reach out in the comments.