Skip to content

How Live Updates for Capacitor work

One of the biggest advantages of Capacitor over other runtimes is the ability to deliver updates in real-time, without having to resubmit the app to the app stores. This feature is referred to as Live Updates or Over-the-Air (OTA) Updates. There already exist several plugins for this, such as our Capacitor Live Update plugin, which can be integrated into your app within minutes. But how do these plugins work in detail? That's exactly what we'll look at in this blog post.

Concept

Live Updates are a powerful feature that allows you to deliver minor bug fixes and updates to your Capacitor app in real time. To understand exactly how Live Updates work, it is important to first understand the different layers of a Capacitor app and how they interact with each other.

Layers Layers

Capacitor App Layers

As you can see from the image above, a Capacitor app consists essentially of a web layer and a native layer. The web layer consists of the HTML, CSS, and JS files that are loaded into the web view. The native layer consists of the native code, such as Java or Swift.

Capacitor now allows you to replace the web layer of the app at runtime since those files are not compiled into the app binary. For this purpose, the new HTML, CSS, and JS files only need to be downloaded from a server and stored at a specific location in the app's file system. Let's take a closer look into the mechanics of this process in the following sections.

Implementation

Capacitor provides various interfaces to instruct the WebView which files should be loaded. Here we distinguish between the current server path and the next server path.

The current server path is the path to the files that are currently loaded in the WebView. Changing the current server path results in a refresh of the WebView. The files are immediately loaded from the new path and displayed in the WebView.

The next server path, on the other hand, is the path to the files that should be loaded at the next restart of the app. Changing the next server path does not result in a refresh of the WebView.

Android

First, let's take a look at the implementation for Android. Under Android, the next server path is stored in the SharedPreferences. To change the server path, the serverBasePath preference in the CapWebViewSettings preferences file of the Capacitor WebView needs to be updated:

private void setNextCapacitorServerPath(String path) {
  SharedPreferences.Editor webViewSettingsEditor = getContext().getSharedPreferences("CapWebViewSettings", Activity.MODE_PRIVATE).edit();
  webViewSettingsEditor.putString("serverBasePath", path);
  webViewSettingsEditor.commit();
}

The current server path, on the other hand, is set directly via the Capacitor Android Bridge. For this, the setServerBasePath method must be called, and the WebView must be reloaded:

private void setCurrentCapacitorServerPath(String path) {
  getBridge().setServerBasePath(path);
  getBridge().reload();
}

To reset the server path to the default value, public must be passed as the path. This is especially useful when you want to implement a fallback mechanism in case the new files cannot be loaded.

iOS

Under iOS, the next server path is stored in the UserDefaults. For this purpose, Capacitor provides the KeyValueStore class, which is used to store key-value pairs in the UserDefaults. To change the server path, the value for the key serverBasePath must now be updated:

private func setNextCapacitorServerPath(path: String) {
  KeyValueStore.standard["serverBasePath"] = path
}

It should be noted that only the last path component of the path is relevant. This is because Capacitor under iOS only supports server paths that are located in the /Library/NoCloud/ionic_built_snapshots directory. A valid path would be, for example, /Library/NoCloud/ionic_built_snapshots/my-bundle.

The current server path is again set via the Capacitor iOS Bridge. To do this, you only need to call the setServerBasePath method:

private func setCurrentCapacitorServerPath(path: String) {
  self.bridge.viewController.setServerBasePath(path: path)
}

Also under iOS, the server path can be reset to the default value by passing public as the path.

Security

An important aspect of Live Updates is security. After all, you don't want your app to be compromised by an attacker. Therefore, it is crucial that the downloaded files are checked for their authenticity and integrity - also known as code signing.

Authenticity means that the files have not been tampered with and come from a trusted source. Integrity means that the files have not been corrupted during the download process.

To ensure the authenticity and integrity of the downloaded files, these files must be digitally signed. For this, a private key is used to sign the files, and a public key is used to verify the signature. The private key must be kept secret and must not be shared with anyone. The public key, on the other hand, is stored in the app and used to verify the signature.

This is exactly the security mechanism we use in Capawesome Cloud to securely deliver Live Updates. Therefore not even we are capable of manipulating our customers' files, as we do not have access to the private keys. Feel free to check out our documentation on Code Signing.

Compliance

Now that we understand how Live Updates for Capacitor work, the question arises whether they are compliant with the guidelines of the app stores. And the answer is yes!

Apple App Store

Live Updates are fully compliant with the Apple App Store policies. The Apple Developer Program License Agreement states that interpreted code may be downloaded to an application as long as it does not change the primary purpose of the application and does not bypass signing, sandbox, or other security features of the OS:

Interpreted code may be downloaded to an Application but only so long as such code: (a) does not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store, (b) does not create a store or storefront for other code or applications, and (c) does not bypass signing, sandbox, or other security features of the OS.

So as long as you do not change the primary purpose of your app via Live Updates, they are fully compliant with the Apple App Store policies since they only update the web layer of your app.

Google Play Store

Live Updates are also compliant with the Google Play Policies. The third paragraph of Device and Network Abuse states that an app distributed via Google Play may not modify, replace, or update itself using any method other than Google Play's update mechanism. However, the same paragraph also states that this restriction does not apply to JavaScript running in a webview or browser:

This restriction does not apply to code that runs in a virtual machine or an interpreter where either provides indirect access to Android APIs (such as JavaScript in a webview or browser).

As Live Updates can only update the web layer of your app, they are fully compliant with the Google Play Policies.

Conclusion

Live Updates are a powerful feature that allows you to deliver updates to your Capacitor app in real time. In this blog post, we have looked at how Live Updates work in detail and how they can be implemented in Android and iOS from a plugin developer's perspective. We have also discussed the security and compliance aspects of Live Updates and shown that they are fully compliant with the guidelines of the app stores.

Check out our Capacitor Live Update plugin to get started with Live Updates in your app today.