iOS WKWebview + Ionic + Laravel
Ionic is an awesome hybrid mobile framework. With devices and browsers getting better and better, the experience for some apps can compete with native implementations. On the apps I work with, I couldn’t help but notice that some things felt inherently smoother on Android than iOS. Though I’m an Android user myself, I don’t kid myself that this is due to superior hardware. Something else was going on.
Turns out, by default Android devices will use the WKWebview and (in Ionic 3 and below) iOS will use UIWebview. The performance on WKWebview is vastly superior, and so my apps felt better on Android than iOS.
The newest version of Ionic (v4) will ship using WKWebview on iOS by default, but until then, you have to enable it manually to make it work. This can be a bit tedious, as there are some caveats to using it. Most notably, your app will now require CORS to be enabled on your server if you’re hitting an API. Let’s break down the steps:
- Install the plugin.
No worries there:
ionic cordova plugin add cordova-plugin-ionic-webview — save
After you do this, it’s a good idea to refresh all your plugins on iOS by removing and re-adding the platform:
ionic cordova platform rm ios
ionic cordova platform add ios
2. Update the config.xml file.
This hung me up for a while. You’ll need two new additions:
<feature name="CDVWKWebViewEngine">
<param name="ios-package" value="CDVWKWebViewEngine" />
</feature>
<preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine" />
This sets WKWebview as the default view engine.
Next, you’ll need to find this line in your config.xml file:
<allow-navigation href="http://ionic.local/*" />
And replace it with:
<allow-navigation href="http://localhost/*" />
If it’s not already in your config file, just add it, no need to replace anything, but the point here is that you don’t want both settings. Also, the point here is, if you find the initial setting don’t add to it, replace it. Now you’re ready for the last step:
3. Enable CORS on your API.
Wait… what’s CORS? No, not the beer. And no, not the band. CORS stands for Cross Origin Resource Sharing. Here’s the simple line: if you want to make an http request from a something that is not a server, then it is a CORS request. JavaScript running in a browser makes CORS requests to external servers. WKWebview changes the source of HTTP requests on your device, and they will now be made as a CORS request (formerly they were made from a file://
URL).
In a Laravel instance, this can be (fairly) easily done with middleware. To enable CORS is basically just putting a bunch of headers on your server responses to let callers know they are allowed to make HTTP requests. They’ll know this because, by default, browsers make an options
request to servers before making your actual request (like a get
request). This options request will return all the headers the server allows.
There are two things you’ll need:
- You’ll need some global middleware to allow
options
requests. With Laravel routing, unless you explicitly define routes that specifyoptions
as the HTTP verb to catch. You don’t want to have to do that with every route, so just make it a global middleware. Something like this would work:
<?php
namespace App\Base\Http;
use Closure;
class PreflightMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next )
{
if ($request->getMethod() === "OPTIONS") {
return response('')
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->header('Access-Control-Allow-Headers', 'Origin, Content-Type, X-Auth-Token, authorization, X-Requested-With');
}
return $next($request);
}
}
All this is doing is checking to see if the method is option
and if it is, tell the requesting entity what you allow.
Make these headers specific to your needs, the above example is very broad and generic.
2. Allow CORS requests on your API endpoints.
This is very similar to the preflight
middleware we have above, but this will live on only the resources we want exposed via API. Personally, I put this on our API
middleware group:
<?php
namespace App\Auth\Middleware;
use Closure;
class CorsMiddleware
{
public function handle($request, Closure $next)
{
return $next($request)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->header('Access-Control-Allow-Headers', 'Origin, Content-Type, X-Auth-Token, authorization, X-Requested-With');
}
}
This is doing basically the same thing as our preflight
middleware, but for all verbs. We want two different middlewares because we only want to expose CORS request at a global level for options
requests.
Viola! Now WKWebview will work for your project. Enjoy the new experience.