Search code examples
androidcapacitor

How to package a hosted web app with Ionic Capacitor


I have a hosted, CMS-based web app that I want to package as an Android / iOS app with Ionic Capacitor. So I added

  "server": {
    "url": "https://my.domain/"
  },

to capacitor.config.json and did

import { Capacitor, Plugins } from '@capacitor/core';

console.log('Plugins', Capacitor.Plugins);

in the main Javascript file of my app (I use webpack for bundling). This basically works, i.e. the app gets loaded and displayed properly. But on the console I see that Capacitor loads the web versions of the plugins, and Device.getInfo() says that the platform is "web", not "android".

How can I make Capacitor act like it would when my app was loaded from the device's file system, and in particular how can I make it use the native versions of the plugins in this setup?


Solution

  • As it turned out, the reason for my troubles where that my pages had an active service worker. Capacity uses WebViewClient::shouldInterceptRequest for injecting the Javascript code that initializes the bridge to the native world, and Android does not call this callback for requests that a service worker handles. Instead, it has a separate callback for these requests that is available via a ServiceWorkerController.

    So what I did was creating my own tiny plugin:

    @NativePlugin
    public class ServiceWorker extends Plugin {
    
      @RequiresApi(api = Build.VERSION_CODES.N)
      @Override
      public void load() {
        ServiceWorkerController swController = ServiceWorkerController.getInstance();
    
        swController.setServiceWorkerClient(new ServiceWorkerClient() {
          @Override
          public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
            return bridge.getLocalServer().shouldInterceptRequest(request);
          }
        });
      }
    
    }
    

    and then it worked as expected. (I also had to add the getter for localServer to the Bridge class.)