WKWebView CORS Solution

Send/Proxy Network Requests With Native Code In An Asynchronous Manner Using Promise

Thor Chen
2 min readAug 17, 2019

Background

I was facing the CORS problem while building a web-based iOS app using WKWebView. The app needs to send API requests using JavaScript inside the WebView, but there is no workaround to disable the CORS protection of WKWebView but listen to its complaint — Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at...

The easiest way to solve the problem is to modify the backend code to include “Access-Control-Allow-Origin” header in the response. However, I have no control of the backend, so that the only possible solution is to proxy the API request using native code instead of doing the request directly in the WebView (I don’t want to fallback to UIWebView).

The challenge for this solution is to bridge the JavaScript code and the native code (ObjC/Swift) in an asynchronous manner, and there are two steps to conquer it.

First Step: Allow JavaScript to call native code

In the native side, I provide an interface for JavaScript to invoke by writing the code below:

After that, I am able to use JavaScript to invoke native code to send network requests like this (I will explain the use of uuid later):

const uuidv4 = require('uuid/v4');
const uuid = uuidv4();
window.webkit.messageHandlers.native.postMessage({
type: 'SEND_HTTP_REQUEST',
url,
body,
method: 'POST',
uuid,
accept: 'application/json',
content_type: 'application/x-www-form-urlencoded; charset=utf-8'
});

Second Step: Get the result back from the native code

After the first step, I am already able to proxy the API request with native code. However, I have to find a way to get the result back as the postMessage method of the messageHandler is a one-way communication, which means the result will not be returned after that method call.

The workaround is to expose a global callback function in JavaScript side for native code to invoke after it gets the result. Also, to allow multiple requests happen in parallel and match a result to a request call, I use uuid.

Additionally, I would like to use Promise to handle the API requests, so that the code to expose the callback function looks like this:

By doing so, I have a pool of promises (actually, they are resolve-reject pairs) indexed by uuid stored in window.jsCallbackBridge.promises, and the native code can invoke window.jsCallbackBridge.resolvePromise after it gets the result.

The code for dealing with results in the native side looks like this:

The lines above fill the blanks in the first step (marked as //TODO:).

Note: I use base64-encoded data when passing the result from native side to JavaScript. The reason is that I expect the result data to be json and I need to parse it later, but the data in the json may contain special characters in plain text which may cause problems while parsing.

In the end, I can create the asynchronous function returning Promise to send the API request:

I might be able to provide a working example later when I am free, thanks for reading.

--

--

Thor Chen
Thor Chen

Written by Thor Chen

Passionate JavaScript/TypeScript Developer with a Full-stack Background

Responses (1)