71

After Apple's iOS 13 release, I realized window.navigator.userAgent in Safari on iPad iOS 13 is same as on MacOS. Something like this:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15

As you see, it's a wrong user-agent for iPad and there is no way to detect if the current device is an iDevice.


After an initial research, I found a workaround for it:

Go to Settings -> Safari -> Request Desktop Website -> All websites. You notice "All websites" is enabled by default. If you disable it and get window.navigator.userAgent the correct user agent is now displayed.

But I cannot ask each user to do this setting change for each device. So I tried to find another way and ended up by writing the following code which checks if it's Safari, macOS, and touch-screen then the device should be an apple mobile device, but I'm wondering is there any better suggestion/way to detect the correct device name in Safari iOS 13?

detectOs = function(){
   //returns OS name, like "mac"
};

//is Safari on an apple touch-screen device
isSafariInIdevice = function(){
   if (/Safari[\/\s](\d+\.\d+)/.test(windows.navigator.userAgent)) {
      return 'ontouchstart' in window && detectOs() === "mac";      
   }
   return false;
};
BuZZ-dEE
  • 6,075
  • 12
  • 66
  • 96
Saeid Amanzadeh
  • 713
  • 1
  • 5
  • 7
  • I am stuck with same issue but i need to check on Server side so I can show mobile side menu instead of full website menu on Safari iOS13. could you help me with this please? https://stackoverflow.com/questions/58344491/request-browser-ismobiledevice-not-working-with-ipadair2-and-ios-13-0-1 – newdeveloper Oct 13 '19 at 02:22
  • Why do you need to know this? Generally browser sniffing is a bad idea. Instead you should be writing your application so that it works on all browsers equally. If you need to disinguish between abilities of browsers then look into [feature detection](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection). – RoToRa Oct 15 '19 at 08:52
  • OP can you please mark @kikiwora's answer as accepted – Daniel Lizik Oct 31 '19 at 01:48
  • OP, could you mark @kikiwora's answer as accepted, if it actually soved your problem? If not, please let us know if you ran into other issues with this solution. – Hermann Schachner Nov 20 '19 at 13:55

3 Answers3

81

Indeed, while option change in Settings may be a good solution for the user, as a developer you can't rely on that. It is as weird as to ask the user to not to use dark mode cause your app doesn't support it instead of opt-out of it using plist.

As for me, the most simple way to detect iOS / iPad OS device now:

const isIOS = /iPad|iPhone|iPod/.test(navigator.platform) ||
  (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)

The first condition is old-fashioned and works with previous versions, while the second condition works for iPad OS 13 which now identifies itself as:

"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko)"

which by all platform detectors I know is not detected (for now) neither as mobile nor desktop.

So since iPad OS now calls itself Macintosh, but real macs have no multi-touch support, this solution is ideal to detect iPad OS devices which are the only multi-touch "Macintosh" devices in existence.

P.S. Also, you may want to augment this checkup for IE exclusion from being detected as an iOS device

const isIOS = (/iPad|iPhone|iPod/.test(navigator.platform) ||
  (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) &&
  !window.MSStream
tanguy_k
  • 11,307
  • 6
  • 54
  • 58
kikiwora
  • 1,724
  • 11
  • 7
  • 1
    thanks that is useful for clientside. could you please help how to detect in razor syntax on server side when page is rendering. badly stuck with this problem. https://stackoverflow.com/questions/58344491/request-browser-ismobiledevice-not-working-with-ipadair2-and-ios-13-0-1 – newdeveloper Oct 13 '19 at 02:03
  • I’m not proficient with a razor, sorry ‍♂️ Also, I don’t think that’s possible on the server side since iPad sends Macintosh header to server - iPad now meant to work like desktop in web and we should not intervene in this behavior – kikiwora Oct 14 '19 at 07:42
  • Seems a bit fragile for long-term use, but the only solution that works. Thanks! – Carson Oct 18 '19 at 15:11
  • Has anyone had success with this in Safari? Mozilla says Safari doesn't support navigator.maxTouchPoints. https://developer.mozilla.org/en-US/docs/Web/API/Navigator/maxTouchPoints – Anke Oct 19 '19 at 01:29
  • 2
    This solution designed for Safari in the first place. It's weird that Mozilla site lists this JS method as unsupported in Safari. It definitely works on all of my Apple devices (laptop, tablet and phone) But indeed, the downside of this method is very fragile from a long-term use perspective. Especially if touch-enabled macs will be available. The thing is, as I said, that new Safari is meant to be like that - it was designed to be detected as a desktop to provide more features for users, since modern sites ofter not so functional mobile versions instead, forcing users to use them. – kikiwora Oct 19 '19 at 09:35
  • Can this tell apart touch screen iBooks or IMacs or will this categorize them as isIOS as well? iBooks or iMacs are legitimately have Intel CPUs (not just a lie like the new iOS Safari), plus they are touch screen as well. – Csaba Toth Mar 05 '20 at 20:13
  • @CsabaToth, try it yourself if you have such a device. As far as I know, there are no macOS computers with touchscreen provided by Apple. But maybe you can somehow connect a touch-capable display to operate macOS, even so, I have no idea if macOS provides Safari information about touchpoints count. But if it doesn’t, then yes, I’m afraid that this method will not be able to distinguish iOS and macOS with touchscreen apart from each other. But anyway, you’re not supposed to detect iOS devices anyway when the user wants a desktop version of a site. So it is designed that way intentionally. – kikiwora Mar 07 '20 at 07:33
  • ios 12 does not support maxTouchPoints, but ios 13 does (safari) tested April 9, 2020 – Larry Flewwelling Apr 09 '20 at 19:36
  • @kikiwora - so does the P.S. should be used? in which condition IE will have similar userAgent? can you please elaborate on this? – Michal Tsadok Apr 30 '20 at 14:18
  • @MichalTsadok once upon a time Microsoft used sort of a hack, for no apparent reason to me, but we know it leads to some IEs (I don't know which versions exactly) being detected as iOS. And since some software uses IE as WebView substitute, it is also affected. There were no cases in my engineering life when that checkup was needed, so I did not study this issue fully. But the issue described is still quite possible in 2020. So my recommendation - just use that checkup, it can't break anything and removing it does not improve performance since the result of let is calculated only once – kikiwora May 01 '20 at 16:15
  • 1
    Thanks for this solution. This fixed what I needed it for. Although it's not the best long-term, it's a good bandaid fix until either Apple comes out with Touchscreen computers or they get their crap together and name their devices correctly! – Thomas Kellough May 22 '20 at 21:08
  • 1
    ⚠️ Just as a notice - this code may become obsolete soon, since now we have an Apple Silicone-based Macs & a hint regarding Touch-base Macs has been found in the App Store animations, and even publicly displayed for some time. – kikiwora Dec 04 '20 at 11:02
4
const isIOS = !!(/iPad|iPhone|iPod/.test(navigator.platform)
  || (navigator.platform === "MacIntel" && typeof navigator.standalone !== "undefined"))

As an alternative to the accepted answer, I found you can use the navigator.standalone param. It's non-standard and used only on iOS Safari at present:

Navigator.standalone

Returns a boolean indicating whether the browser is running in standalone mode. Available on Apple's iOS Safari only.

When combined with navigator.platform === "MacIntel" iPad's are the only devices that define this property, therefore typeof navigator.standalone !== "undefined" filters out Macs running Safari (touchscreen or not).

GuyC
  • 6,494
  • 1
  • 31
  • 44
  • shouldn't it be `isIpadSafari = ` ? – kofifus Sep 24 '20 at 04:14
  • 2
    @kofifus the first part of the OR condition tests for iPad//iPhone/iPod in the platform string. The second part is the iOS 13 iPad so it's not just looking for iPads running Safari – GuyC Sep 25 '20 at 11:46
0
     function mobileDetect() {


    var agent = window.navigator.userAgent;
    var d = document;
    var e = d.documentElement;
    var g = d.getElementsByTagName('body')[0];
    var deviceWidth = window.innerWidth || e.clientWidth || g.clientWidth;

    // Chrome
    IsChromeApp = window.chrome && chrome.app && chrome.app.runtime;

    // iPhone
    IsIPhone = agent.match(/iPhone/i) != null;

    // iPad up to IOS12
    IsIPad = (agent.match(/iPad/i) != null) || ((agent.match(/iPhone/i) != null) && (deviceWidth > 750)); // iPadPro when run with no launch screen can have error in userAgent reporting as an iPhone rather than an iPad. iPadPro width portrait 768, iPhone6 plus 414x736 but would probably always report 414 on app startup

    if (IsIPad) IsIPhone = false;

    // iPad from IOS13
    var macApp = agent.match(/Macintosh/i) != null;
    if (macApp) {
        // need to distinguish between Macbook and iPad
        var canvas = document.createElement("canvas");
        if (canvas != null) {
            var context = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
            if (context) {
                var info = context.getExtension("WEBGL_debug_renderer_info");
                if (info) {
                    var renderer = context.getParameter(info.UNMASKED_RENDERER_WEBGL);
                    if (renderer.indexOf("Apple") != -1) IsIPad = true;
                }
                ;
            }
            ;
        }
        ;
    }
    ;

    // IOS
    IsIOSApp = IsIPad || IsIPhone;

    // Android
    IsAndroid = agent.match(/Android/i) != null;
    IsAndroidPhone = IsAndroid && deviceWidth <= 960;
    IsAndroidTablet = IsAndroid && !IsAndroidPhone;



    message = ""


    if (IsIPhone) {

        message = "Device is IsIPhone"


    }
    else if (IsIPad) {

        message = "Device is ipad"

    } else if (IsAndroidTablet || IsAndroidPhone || IsAndroid) {

        message = "Device is Android"


    } else {

        message = "Device is Mac ||  Windows Desktop"

    }


    return {

        message: message,

        isTrue: IsIOSApp || IsAndroid || IsAndroidTablet || IsAndroidPhone

    }

}



const checkMobile = mobileDetect()

alert(checkMobile.message + "  =====>  " + checkMobile.isTrue)
shareeditdeleteflag
BuZZ-dEE
  • 6,075
  • 12
  • 66
  • 96
Mahmoud D. Alghraibeh
  • 1,507
  • 2
  • 10
  • 10
  • This will detect an iPhone also as iPad as the deviceWidth > 750. – Roger Far Jan 07 '20 at 14:55
  • @RogerFar I think that's less of a sacrifice than categorizing touch screen iBooks or iMacs as mobile devices. Seems like people are completely lost in the forest with this issue and since the Safari can lie now the only way to sort out the issue is to test for some feature like this snippet does. If you like you can add more code to test width and sophisticate it further as you like. – Csaba Toth Mar 05 '20 at 20:17