Detect if page is loaded inside WKWebView in JavaScript

How can I reliably detect using javascript that a page is loaded inside a WKWebView? I'd like to be able to detect these scenarios:

  • iOS & WKWebView
  • iOS & Safari
  • not iOS

There is a similar question about UIWebView here. But it's quite old and I'm not sure if same still applies to WKWebView.

Answers:

Answer

You can check for the existence of window.webkit.messageHandlers which WKWebKit uses to receive messages from JavaScript. If it exists, you're inside a WKWebView.

That combined with a simple user agent check should do the trick:

var iOS = (navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false);
var isWKWebView = false;
if (window.webkit && window.webkit.messageHandlers) {
    isWKWebView = true;
}
Answer

The accepted answer doesn't work as tested using the WKWebView vs UIWebView app

As the article mentions, the only HTML5 feature difference is IndexedDB support. So I'd go for a more reliable pattern with:

    if (navigator.platform.substr(0,2) === 'iP'){
      //iOS (iPhone, iPod or iPad)
      var lte9 = /constructor/i.test(window.HTMLElement);
      var nav = window.navigator, ua = nav.userAgent, idb = !!window.indexedDB;
      if (ua.indexOf('Safari') !== -1 && ua.indexOf('Version') !== -1 && !nav.standalone){      
        //Safari (WKWebView/Nitro since 6+)
      } else if ((!idb && lte9) || !window.statusbar.visible) {
        //UIWebView
      } else if ((window.webkit && window.webkit.messageHandlers) || !lte9 || idb){
        //WKWebView
      }
    }

You may ask: Why not using the UserAgent? That's because Android browsers use it as settings! So, we should never trust any UAs. Only browser features and property checks as such.

Also I noticed that the QuickTime plugin was always loaded as part of Older Safari and other Browsers in UIWebView. But the plugin is no longer present in WKWebView. So you can use the QuickTime plugin presence as an extra check.

9/23/16 Edit: I adjusted the code for Safari 10 which no longer allowed the sole idb check to be reliable, as mentioned by @xmnboy. To discard Safari 10, it checks for the old webkit engine bug, which only applied until Safari 9.2; and i use a window.statusbar.visible fallback which appears to be a reliable indicator signal after a few comparison tests between iOS 9 and 10. (please check though)

Answer

Given the change in behavior to the UIWebView that was introduced by Apple in iOS 10, here's a new answer that combines the original response by @Justin-Michael and the follow-up favorite by @hexalys.

var isWKWebView = false ;
if( navigator.platform.substr(0,2) === 'iP' ) {    // iOS detected
    if( window.webkit && window.webkit.messageHandlers ) {
        isWKWebView = true ;
    }
}

It turns out that Justin's answer was really the better feature detection mechanism, because it works for both iOS 9 and iOS 10.

No telling what happens when we get to iOS 11. :-)


Qualification: this test will work if you are using the official Cordova WKWebView plugin to build your webview app, because that plugin does initialize the addScriptMessageHandler method, as noted by @hexalys in the comments to this post. That mechanism is being used by Cordova to define a new JS to native bridge when the WKWebView plugin is present.

Search for addScriptMessageHandler in that plugin repo and see the very end of the ios-wkwebview-exec.js file in that repo for some implementation details (or search for the string window.webkit.messageHandlers in that file).

Answer

It seems that because of the latest iOS Chrome using WKWebView as rendering engine, Chrome is detected as WKWebView. ua.indexOf('CriOS') !== -1 will helps to distinguish Chrome from WKWebView in App.

Answer

In iOS, you could add this code to establish communication between javascript and objective-c:

WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKUserContentController *controller = [[WKUserContentController alloc] init];
[controller addScriptMessageHandler:self name:@"javascript_observer"];
configuration.userContentController = controller;

...

webview = [[WKWebView alloc] initWithFrame:... configuration: configuration];

In javascript, you could test the connection like this:

if ( window.webkit != undefined ){
//javascript is running in webview
}

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.