Matthias
Matthias

Reputation: 1799

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?

Upvotes: 8

Views: 7977

Answers (3)

Eric Kim
Eric Kim

Reputation: 10952

Anyone having similar problem I was able to solve by updating android/app/main/java/.../MainAcitivity.java

https://github.com/ionic-team/capacitor/issues/5278#issuecomment-1313531876

public class MainActivity extends BridgeActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ServiceWorkerController swController = ServiceWorkerController.getInstance();

        swController.setServiceWorkerClient(new ServiceWorkerClient() {
            @Nullable
            @Override
            public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
                if (request.getUrl().toString().contains("index.html")) {
                    request.getRequestHeaders().put("Accept", "text/html");
                }
                return bridge.getLocalServer().shouldInterceptRequest(request);
            }
        });
    }
}

Also need to update minSdkVersion = 24 in android/variables.gradle

Upvotes: 0

Martin Braun
Martin Braun

Reputation: 12579

I'm dealing with a Capacitor v2 project that also requires to have in-app navigation to a remote website which uses custom Capacitor plugins that are hosted on the Capacitor wrapper. I was also testing with a local WebServer that can be reached at 10.0.2.2 (Android emulator to localhost). The accepted solution is not relevant anymore, the code was never reached, and it was not helpful, however I did some digging into the bridge itself.

My WebServer was hosted on port 8000 locally, so the Bridge failed to have an exact match of the url within the UrlMatcher and thus was returning no handler to progress the request to http://10.0.2.2:8000 in my wrapper.

I figured it would work when I put this into my capacitor.config.json:

    "server": {
        "allowNavigation": [
            "10.0.2.2",
            "10.0.2.2:8000",
        ]
    }

The first entry is to prevent capacitor from opening a new browser instead of launching it within the frame, the 2nd is to exact match the authority segment within the UrlMatcher. Maybe it was fixed in more recent versions, but for v2 this worked.

Upvotes: 0

Matthias
Matthias

Reputation: 1799

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.)

Upvotes: 11

Related Questions