64

How to detect if the user is browsing the page using webview for android or iOS?

There are various solutions posted all over stackoverflow, but we don't have a bulletproof solution yet for both OS.

The aim is an if statement, example:

if (android_webview) {
    jQuery().text('Welcome webview android user');
} else if (ios_webview) {
    jQuery().text('Welcome webview iOS user');
} else if (ios_without_webview) {
    // iOS user who's running safari, chrome, firefox etc
    jQuery().text('install our iOS app!');
} else if (android_without_webview) {
    // android user who's running safari, chrome, firefox etc
    jQuery().text('install our android app!');
}

What I've tried so far

Detect iOS webview (source):

if (navigator.platform.substr(0,2) === 'iP'){

  // iOS (iPhone, iPod, iPad)
  ios_without_webview = true;

  if (window.indexedDB) {
    // WKWebView
    ios_without_webview = false; 
    ios_webview = true; 
  }

}

Detect android webview, we have a number of solutions like this and this. I'm not sure what's the appropriate way to go because every solution seems to have a problem.

Community
  • 1
  • 1
Henrik Petterson
  • 6,862
  • 20
  • 71
  • 155

15 Answers15

47

Detecting browser for iOS devices is different from the Android one. For iOS devices you can do it by checking user agent using JavaScript:

var userAgent = window.navigator.userAgent.toLowerCase(),
    safari = /safari/.test( userAgent ),
    ios = /iphone|ipod|ipad/.test( userAgent );

if( ios ) {
    if ( safari ) {
        //browser
    } else if ( !safari ) {
        //webview
    };
} else {
    //not iOS
};

For Android devices, you need to do it through server side coding to check for a request header.

PHP:

if ($_SERVER['HTTP_X_REQUESTED_WITH'] == "your.app.id") {
    //webview
} else {
    //browser
}

JSP:

if ("your.app.id".equals(req.getHeader("X-Requested-With")) ){
    //webview
} else {
    //browser
}

Ref:detect ipad/iphone webview via javascript

Community
  • 1
  • 1
Avijit
  • 1,253
  • 13
  • 21
23

Note: This solution is PHP-based. HTTP headers can be faked so this is not the nicest solution but you can have a start with this.

//For iOS

if ((strpos($_SERVER['HTTP_USER_AGENT'], 'Mobile/') !== false) && (strpos($_SERVER['HTTP_USER_AGENT'], 'Safari/') == false) {
    echo 'WebView';
} else{
    echo 'Not WebView';
}

//For Android

if ($_SERVER['HTTP_X_REQUESTED_WITH'] == "com.company.app") {
    echo 'WebView';
} else{
    echo 'Not WebView';
}
doppelgreener
  • 4,809
  • 10
  • 46
  • 63
rhavendc
  • 985
  • 8
  • 21
  • 1
    How reliable is the X-Requested-With header? Will this be there in any case? – Dio F Aug 15 '18 at 09:25
  • 1
    It sounds like some devices (e.g. Galaxy S4 Mini(Android 4.2.2)) might not send this header - see https://stackoverflow.com/questions/24291315/android-webview-detection-in-php. Workarounds generally involve modifying existing headers in the application, or adding a custom one that can be checked by your web server. – Mike McG Oct 27 '18 at 01:03
  • Thanks bro ... This is working .... `if ($_SERVER['HTTP_X_REQUESTED_WITH'] == "com.company.app") { echo 'WebView'; } else{ echo 'Not WebView'; } ` – Shurvir Mori Dec 17 '19 at 04:49
  • Both Safari and Telegram contains "safari" in user agent, so that won't work – Chris Jan 30 '21 at 21:43
12

For me this code worked:

var standalone = window.navigator.standalone,
  userAgent = window.navigator.userAgent.toLowerCase(),
  safari = /safari/.test(userAgent),
  ios = /iphone|ipod|ipad/.test(userAgent);

if (ios) {
  if (!standalone && safari) {
    // Safari
  } else if (!standalone && !safari) {
    // iOS webview
  };
} else {
  if (userAgent.includes('wv')) {
    // Android webview
  } else {
    // Chrome
  }
};
Barnee
  • 3,212
  • 8
  • 41
  • 53
  • 1
    This is almost perfect BUT there is one exception. The WebView in Twitters iOS App is using the exact same UA string as Safari on iOS. Some say there is a workaround using a WebRTC check, but this seems to fail as well. So as for now, there is no way to distinguish between Twitter In-App Browser and the actual iOS Safari. https://leemartin.medium.com/twitter-please-identify-yourself-in-your-app-webview-user-agent-4f58dbadfa05 – kilian Jan 25 '21 at 20:04
  • Thanks for the answer. `userAgent.includes('wv')` seems to work perfectly in recent android versions (tested android 11) but it's not working on android 8. Wonder what needs to be tweaked. – Operator Mar 06 '22 at 23:37
11

This is the extended version of rhavendc's answer. It can be used for showing app install banner when a website is visited from browser, and hiding the banner when a website is opened in a webview.

$iPhoneBrowser  = stripos($_SERVER['HTTP_USER_AGENT'], "iPhone");
$iPadBrowser    = stripos($_SERVER['HTTP_USER_AGENT'], "iPad");
$AndroidBrowser = stripos($_SERVER['HTTP_USER_AGENT'], "Android");
$AndroidApp = $_SERVER['HTTP_X_REQUESTED_WITH'] == "com.company.app";
$iOSApp = (strpos($_SERVER['HTTP_USER_AGENT'], 'Mobile/') !== false) && (strpos($_SERVER['HTTP_USER_AGENT'], 'Safari/') == false);

if ($AndroidApp) {
    echo "This is Android application, DONT SHOW BANNER";
}
else if ($AndroidBrowser) {
    echo "This is Android browser, show Android app banner";
}
else if ($iOSApp) {
    echo "This is iOS application, DONT SHOW BANNER";
}
else if($iPhoneBrowser || $iPadBrowser) {
    echo "This is iOS browser, show iOS app banner";
}
Uğur Tılıkoğlu
  • 731
  • 1
  • 6
  • 6
8

Complementing the above answers, to find out if it's webview on newer versions of android, you can use the expression below:

const isWebView = navigator.userAgent.includes ('wv')

See details of this code at https://developer.chrome.com/multidevice/user-agent#webview_user_agent.

4

For anyone out there who is having issues in year 2022 with apple deciding to drop 'ipad' in the useragent, I had to tweak the script a little to make it work:

function isWebview() {
    if (typeof window === undefined) { return false };

    let navigator = window.navigator;

    const standalone = navigator.standalone;
    const userAgent = navigator.userAgent.toLowerCase();
    const safari = /safari/.test(userAgent);
    const ios = /iphone|ipod|ipad/macintosh.test(userAgent);
    const ios_ipad_webview = ios && !safari;

    return ios ? ( (!standalone && !safari) || ios_ipad_webview ) : userAgent.includes('wv');
}
dperez
  • 592
  • 5
  • 11
M-B
  • 319
  • 2
  • 9
1

Updated the solution using TypeScript: https://codepen.io/davidrl1000/pen/YzeaMem

const isWebview = () => {
    if (typeof window === undefined) { return false };

    let navigator: any = window.navigator;

    const standalone = navigator.standalone;
    const userAgent = navigator.userAgent.toLowerCase();
    const safari = /safari/.test(userAgent);
    const ios = /iphone|ipod|ipad/.test(userAgent);

    return ios ? !standalone && !safari : userAgent.includes('wv');
}
davidrl1000
  • 311
  • 1
  • 4
  • 16
0

I found this simple-to-use package is-ua-webview

intall: $ npm install is-ua-webview

Javascript:

var isWebview = require('is-ua-webview');    
var some_variable = isWebview(navigator.userAgent);

Angular2+:

import * as isWebview from 'is-ua-webview';
const some_variable = isWebview(navigator.userAgent);
Manoj De Mel
  • 927
  • 9
  • 16
0

If you're using Flask (and not PHP), you can check to see if "wv" is in the string for the "User-Agent" header. Feel free to edit / add a more precise way of checking it, but basically "wv" is put there if it's a web view; otherwise, it is not there.

user_agent_header = request.headers.get('User-Agent', None)
is_web_view = "wv" in str(header_request) if user_agent_header is not None else False

Chrome Developer Docs:

WebView UA in Lollipop and Above

In the newer versions of WebView, you can differentiate the WebView by looking for the wv field as highlighted below.

Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.65 Mobile Safari/537.36

Joshua Wolff
  • 2,687
  • 1
  • 25
  • 42
0

A simple solution that is found for this issue is passing a parameter in url when hitting webview and check if the parameter exists and is it from android or IOS. It works only when you have access to App source code. otherwise you can check for user agent of request.

  • This isn't a good solution because any navigation to other pages will lose the querystring parameter. – Justin Jun 13 '20 at 17:02
0

I came to decision, that instead of detection, more efficient solution simply specify platform in AppConfig.

const appConfig = {
   appID: 'com.example.AppName.Web'
   //or
   //appID: 'com.example.AppName.MobileIOS'
   //or
   //appID: 'com.example.AppName.MobileAndroid'
}
Yuriy N.
  • 4,936
  • 2
  • 38
  • 31
0

The following works for me as well: (in case you get puzzled and undefined index for $_SERVER['HTTP_X_REQUESTED_WITH'])

if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == "com.systechdigital.bgmea") { 
    // web view Android
} else if ( (strpos($_SERVER['HTTP_USER_AGENT'], 'Mobile/') !== false) && (strpos($_SERVER['HTTP_USER_AGENT'], 'Safari/') == false) ) {
    // web view iOS
}
else {
    // responsive mobile view or others (e.g. desktop)
}
Siddiqui Noor
  • 5,575
  • 5
  • 25
  • 41
0

WebView UA in Lollipop and Above

In the newer versions of WebView, you can differentiate the WebView by looking for the wv field as highlighted below.

enter image description here

More Details - https://developer.chrome.com/docs/multidevice/user-agent/

Yesu Raj
  • 314
  • 3
  • 5
0

Previous answers doesn't take into consideration the Android Version being used

I tried to make a script that is valid for all android versions

   <script>
      var isWebView = false;
      var userAgent = navigator.userAgent;
    
      if (/Android/.test(userAgent)) {
        // Check the Android version to determine how to differentiate WebView from Chrome
        var androidVersion = parseFloat(userAgent.slice(userAgent.indexOf("Android")+8));
        if (androidVersion >= 10) {
          // For Android 10 and above, check for the "wv" field in the user-agent string
          isWebView = /(wv)/.test(userAgent);
        } else {
          // For versions of Android below 10, check for the "Version/_X.X_" string in the user-agent string
          isWebView = userAgent.includes("Version/");
        }
      }
    
      if (isWebView) {

         // user is viewing page from WebView
      } else {

        //user is not using WebView
      }
    </script>
-1

For iOS I've found that you can reliably identify if you're in a webview (WKWebView or UIWebView) with the following:

var isiOSWebview = (navigator.doNotTrack === undefined && navigator.msDoNotTrack === undefined && window.doNotTrack === undefined);

Why it works: All modern browsers (including webviews on Android) seem to have some sort of implementation of doNotTrack except webviews on iOS. In all browsers that support doNotTrack, if the user has not provided a value, the value returns as null, rather than undefined - so by checking for undefined on all the various implementations, you ensure you're in a webview on iOS.

Note: This will identify Chrome, Firefox, & Opera on iOS as being a webview - that is not a mistake. As noted in various places online, Apple restricts 3rd party browser developers on iOS to UIWebView or WKWebView for rendering content - so all browsers on iOS are just wrapping standard webviews except Safari.

If you need to know you're in a webview, but not in a 3rd party browser, you can identify the 3rd party browsers by their respective user agents:

(isiOSWebview && !(/CriOS/).test(navigator.userAgent) && !(/FxiOS/).test(navigator.userAgent) && !(/OPiOS/).test(navigator.userAgent)
zaffudo
  • 47
  • 3
  • 1
    Tested with Telegram WebView on an iPhone SE and navigator.doNotTrack returned '0' so seems they recently added support for doNotTrack – Selo Sep 18 '18 at 11:35
  • @Selo - Telegram the messaging app? I was under the impression they used Safari View Controller for links and such. As best I can tell, in iOS 12, webviews still do not have doNotTrack implemented. – zaffudo Dec 05 '18 at 19:04