Skip to content

Latest commit

 

History

History
67 lines (50 loc) · 4.1 KB

native-bridge-deep-dive.md

File metadata and controls

67 lines (50 loc) · 4.1 KB

Native Bridge deep dive

The ForemWebView instance makes use of WebKit's WKScriptMessageHandler and this is what we commonly refer to as the Native Bridge. With a few clever techniques this allows our web context interact (bi-directionally) with the native implementation.

All these interactions are handled by the WKScriptMessageHandler implementation.

Podcast Player

The Podcast Player within the Forem codebase relies on the Native Bridge to communicate with the ForemWebView implementation in order to use native APIs for audio playback.

Messages are JSON encoded and sent both ways (JavaScript -> Swift and Swift -> JavaScript). These JSON messages all include an "action" and sometimes other optional values. This is how the JavaScript context is able to present a web UI while the actual audio player is an AVPlayer.

You can see supported messages by the Swift context here and the supported messages by the JavaScript context here.

Video Player

The Video Player works in a very similar way, with JSON encoded messages exchanged between the native and the JavaScript contexts. This is a much more simplified process because the JavaScript context will send in a play message and the ForemWebView will create a fully native AVPlayerViewController.

It's the app's responsibility to implement the ForemWebViewDelegate and when a video player is initiated by the user the function willStartNativeVideo(playerController: AVPlayerViewController) will be called, giving you the opportunity to handle it. The most straightforward way of doing this is:

present(playerController, animated: true) {
    playerController.player?.play()
}

Native Image Upload

The Swift context also implements an interface for Native Image Uploads (via Camera or Library picker). This interface allows for a button in the DOM to trigger the native flow and expect the result (uploaded image URL).

The JavaScript event triggered by the user's tap on an "Upload Image" button requires an id and supports an optional ratio for image cropping. The id represents the hidden input field used for receiving the events back from the Swift context. The ratio allows for cropping the image on a specific ratio if necessary. This is the sample code from ArticleCoverImage.jsx (here and here):

initNativeImagePicker = (e) => {
  e.preventDefault();
  window.webkit.messageHandlers.imageUpload.postMessage({
    id: 'native-cover-image-upload-message',
    ratio: `${100.0 / 42.0}`,
  });
};
handleNativeMessage = (e) => {
  const message = JSON.parse(e.target.value);

  switch (message.action) {
    case 'uploading':
      this.setState({ uploadingImage: true });
      this.clearUploadError();
      break;
    case 'error':
      this.setState({
        uploadingImage: false,
        uploadError: true,
        uploadErrorMessage: message.error,
      });
      break;
    case 'success':
      this.props.onMainImageUrlChange({ links: [message.link] });
      this.setState({ uploadingImage: false });
      break;
  }
};

You can also read through how the Swift code handles the imageUpload message here.