Summary: I provide a practical solution for solving CORS issue in WKWebView in Ionic and Cordova. The solution works both for iOS and Android.
If you code with Ionic or Cordova and use a REST API, you probably have seen an error like this:
XMLHttpRequest cannot load http://api.ionic.com/endpoint. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:8100′ is therefore not allowed access.
What is CORS?
CORS stands for Cross Origin Resource Sharing. In short, CORS is a method to prevent a client to request a display a service from a host other than the one that is currently showing. This is done for security reasons.
In order for an external API server to work in the presence of CORS, it should include something like this in its header:
The problem however is that some API providers do not include this and since we don’t have any control over the server, we cannot add this to the response header.
This is a huge problem specially in iOS where Ionic and Cordova run in WKWebView, which enforces CORS.
Ionic’s blog has an old post on CORS issues. Unfortunately, the solution that they provide is only suitable when you are running the app using
ionic serve or
ionic run -l. That solution will not work on the production release!
The good news is that you can still solve the problem by doing a tiny little bit of work. I list a couple of solutions:
- Using a proxy (Not a good solution): the idea is simple: create your OWN server in the middle of your client and the main API server. Your client request goes to your own server, which support CORS. Your server sends the request to the API server, gets the results, and sends it back to you.
Your server can be as simple as reading requests from specific node like /requests from a database server like Google Firebase, and then write the results in another node called /replies.
While this is doable, I don’t like this solution cause you will need to create a new server and maintain it.
2. Use Native HTTP (My favorite): Fortunately Cordova’s native HTTP plugin comes to rescue. Since this plugin’s HTTP requests don’t go through WKWebView, it does not have CORS issues. This is awesome!
So the solution is to send the HTTP requests for the given API through native HTTP plugin rather than NodeJS’s requests. Therefore:
Solution: Instead of using npm’s
Fetch packages, use Cordova’s plugin.
Here is an example usage for Binance’s REST API using Cordova’s native HTTP:
What if we are using an npm wrapper for the API?
The above solution worked because we were using a simple REST HTTP call. However, sometimes an API call is more complex. For example, you have to provide APIKey and Secret and sign the request.
For such complex requests there are some npm wrappers that do the job and take care of the more complex issues, except that those packages use npm’s
Depending on the npm wrapper that you use, there are two possible conditions:
Fetch function is exposed in the constructor
If you are lucky, some npm packages like CCXT already allow you to overload their Fetch request function. In this case all you need is to pass your own HTTP Fetch wrapper to its constructor:
Fetch function is NOT exposed in the constructor
But if you are not lucky, the Fetch request function is not exposed in the constructor. For example here is a great npm wrapper for Binance’s API. But it uses npm’s
Request package which causes CORS issue, sigh…
In this case the solution is not too hard: you just need to modify that package and force it to use Cordova’s
Request rather than npm’s.
For example, I modified Binance’s npm wrapper such that instead of its default npm Request, it takes the
Request function from constructor, which I set it to
So in short, if you are using an npm wrapper for a REST API that doesn’t expose its HTTP request function in its constructor:
- Create a branch of that wrapper on Github in your own Github
- Modify that package and force it to use Cordova’s HTTP request
- Use the modified package in your Ionic program
The caveat is that if the npm wrapper is updated you will need to integrate the updates in your branch. But perhaps you can create a pull request and encourage the package owner to use your updates and expose the request function in the constructor.