Detect if device is iOS

473,986

Solution 1

Detecting iOS

With iOS 13 iPad both User agent and platform strings are changed and differentiating between iPad and MacOS seems possible, so all answers below needs to take that into account now.

This might be the shortest alternative that also covers iOS 13:

function iOS() {
  return [
    'iPad Simulator',
    'iPhone Simulator',
    'iPod Simulator',
    'iPad',
    'iPhone',
    'iPod'
  ].includes(navigator.platform)
  // iPad on iOS 13 detection
  || (navigator.userAgent.includes("Mac") && "ontouchend" in document)
}

iOS will be either true or false

Worse option: User agent sniffing

User Agent sniffing is more dangerous and problems appear often.

On iPad iOS 13, the user agent is identical with that of a MacOS 13 computer, but if you ignore iPads this might work still for a while:

var iOS = !window.MSStream && /iPad|iPhone|iPod/.test(navigator.userAgent); // fails on iPad iOS 13

The !window.MSStream is to not incorrectly detect IE11, see here and here.

Note: Both navigator.userAgent and navigator.platform can be faked by the user or a browser extension.

Browser extensions to change userAgent or platform exist because websites use too heavy-handed detection and often disable some features even if the user's browser would otherwise be able to use that feature.

To de-escalate this conflict with users it's recommended to detect specifically for each case the exact features that your website needs. Then when the user gets a browser with the needed feature it will already work without additional code changes.

Detecting iOS version

The most common way of detecting the iOS version is by parsing it from the User Agent string. But there is also feature detection inference*;

We know for a fact that history API was introduced in iOS4 - matchMedia API in iOS5 - webAudio API in iOS6 - WebSpeech API in iOS7 and so on.

Note: The following code is not reliable and will break if any of these HTML5 features is deprecated in a newer iOS version. You have been warned!

function iOSversion() {

  if (iOS) { // <-- Use the one here above
    if (window.indexedDB) { return 'iOS 8 and up'; }
    if (window.SpeechSynthesisUtterance) { return 'iOS 7'; }
    if (window.webkitAudioContext) { return 'iOS 6'; }
    if (window.matchMedia) { return 'iOS 5'; }
    if (window.history && 'pushState' in window.history) { return 'iOS 4'; }
    return 'iOS 3 or earlier';
  }

  return 'Not an iOS device';
}

Solution 2

After iOS 13 you should detect iOS devices like this, since iPad will not be detected as iOS devices by old ways (due to new "desktop" options, enabled by default):

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

The first condition for iOS < 13 or iPhone or iPad with disabled Desktop mode, the second condition for iPadOS 13 in the default configuration, since it position itself like Macintosh Intel, but actually is the only Macintosh with multi-touch.

Rather a hack than a real solution, but work reliably for me

P.S. As being said earlier, you probably should add IE checkup

let isIOS = (/iPad|iPhone|iPod/.test(navigator.platform) ||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) &&
!window.MSStream

Solution 3

This sets the variable _iOSDevice to true or false

_iOSDevice = !!navigator.platform.match(/iPhone|iPod|iPad/);

Solution 4

None of the previous answers here work for all major browsers on all versions of iOS, including iOS 13. Here is a solution that works for Safari, Chrome and Firefox for all iOS versions:

var isIOS = (function () {
    var iosQuirkPresent = function () {
        var audio = new Audio();

        audio.volume = 0.5;
        return audio.volume === 1;   // volume cannot be changed from "1" on iOS 12 and below
    };

    var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
    var isAppleDevice = navigator.userAgent.includes('Macintosh');
    var isTouchScreen = navigator.maxTouchPoints >= 1;   // true for iOS 13 (and hopefully beyond)

    return isIOS || (isAppleDevice && (isTouchScreen || iosQuirkPresent()));
})();

Note that this code snippet was written with priority on readability, not conciseness or performance.

Explanation:

  • If the user agent contains any of "iPod|iPhone|iPad" then clearly the device is iOS. Otherwise, continue...

  • Any other user agent that does not contain "Macintosh" is not an Apple device and therefore cannot be iOS. Otherwise, it is an Apple device, so continue...

  • If maxTouchPoints has a value of 1 or greater then the Apple device has a touch screen and therefore must be iOS since there are no Macs with touch screens (kudos to kikiwora for mentioning maxTouchPoints). Note that maxTouchPoints is undefined for iOS 12 and below, so we need a different solution for that scenario...

  • iOS 12 and below has a quirk that does not exist in Mac OS. The quirk is that the volume property of an Audio element cannot be successfully set to any value other than 1. This is because Apple does not allow volume changes on the Audio element for iOS devices, but does for Mac OS. That quirk can be used as the final fallback method for distinguishing an iOS device from a Mac OS device.

Solution 5

If you are using Modernizr, you can add a custom test for it.

It doesn't matter which detection mode you decide to use (userAgent, navigator.vendor or navigator.platform), you can always wrap it up for a easier use later.

//Add Modernizr test
Modernizr.addTest('isios', function() {
    return navigator.userAgent.match(/(iPad|iPhone|iPod)/g);
});

//usage
if (Modernizr.isios) {
    //this adds ios class to body
    Modernizr.prefixed('ios');
} else {
    //this adds notios class to body
    Modernizr.prefixed('notios');
}
Share:
473,986
SparrwHawk
Author by

SparrwHawk

Front end designer, I've been designing websites for about a year now and I'm really keen to improve my jQuery coding skills.

Updated on July 08, 2022

Comments

  • SparrwHawk
    SparrwHawk almost 2 years

    I'm wondering if it's possible to detect whether a browser is running on iOS, similar to how you can feature detect with Modernizr (although this is obviously device detection rather than feature detection).

    Normally I would favour feature detection instead, but I need to find out whether a device is iOS because of the way they handle videos as per this question YouTube API not working with iPad / iPhone / non-Flash device

  • Douglas Greenshields
    Douglas Greenshields over 12 years
    There's also iPod Touches to consider.
  • Bryan Naegele
    Bryan Naegele over 12 years
    @DouglasGreenshields Correct. Forgot about that one but I believe it transmits its identity in the user-agent, as well.
  • SparrwHawk
    SparrwHawk over 12 years
    Thanks Pierre - this code seems simpler though, I just wonder whether I can just specify 'iOS' rather than having to type out all the separate iDevices.... if((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) || (navigator.userAgent.match(/iPad/i))) { // Do something }
  • Pierre
    Pierre over 12 years
    As far as i know there aren't any properties that include the word iOS. I however simplified the regex (but still, device names are needed).
  • viebel
    viebel over 12 years
    navigator.appVersion is the browser version not the iOS version! w3schools.com/jsref/prop_nav_appversion.asp
  • viebel
    viebel over 12 years
    document.write("Browser Version: " + navigator.appVersion) It's the browser version and not the iOS version.
  • Tim Down
    Tim Down about 12 years
    What you're doing in the second snippet is feature inference, not feature detection. Feature detection is testing features that you're actually going to use, whereas what you're doing is testing features that you happen to know were introduced in a particular version of the OS and inferring the OS version from them. This is fragile because future versions of iOS could remove these features.
  • Vitim.us
    Vitim.us almost 12 years
    @Pierre also parsing iOS ins't all safe, the older versions of iOS >4 was refered as iPhoneOS including on the iPad
  • zack
    zack over 11 years
    ..interestingly, just across a userAgent string like Tiphone T67/1.0 Browser/wap2.0 Sync/SyncClient1.1 Profile/MIDP-2.0 Configuration/CLDC-1.1 ..
  • LandonSchropp
    LandonSchropp about 11 years
    This is a better way to write your check: var iOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
  • Kevin Newman
    Kevin Newman over 10 years
    Just a note - the navigator.platform array doesn't work on the iPad Simulator because it has the entire phrase "iPad Simulator" in the platform string.
  • Bas Slagter
    Bas Slagter almost 10 years
    Testing for iPad, iPhone or iPod in the user agent string will give a false positive in case the user has a Window Phone. Internet Explorer on this device contains a message '...like iPhone OS...' in the user agent string and thus will return true on this test.
  • VenomVendor
    VenomVendor almost 10 years
    This is much better var iOS = /(iP*)/g.test(navigator.userAgent);
  • Jscti
    Jscti over 9 years
    Be carefull, Modernizr automatically lowercase the name of the added test. (in your example, Modernizr.isiOS will never return true). Bad behavior of the lib in my view ...
  • Marty Cortez
    Marty Cortez over 9 years
    Why aren't you a fan of User Agent sniffing?
  • Sarsaparilla
    Sarsaparilla over 9 years
    Simplest answer: var iOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
  • Koraktor
    Koraktor over 9 years
    If you also want this to work on iOS Simulator you can use: navigator.platform.replace(' Simulator', '').
  • Peter O'Callaghan
    Peter O'Callaghan almost 9 years
    Just another spanner to throw into the works... Crazy as it seems, (at least some) IE11 Mobile phones include iPhone in their UA.
  • Pierre
    Pierre almost 9 years
    Thanks for the info Peter! I've done some research and updated the answer accordingly!
  • dzh
    dzh almost 9 years
    Is there a way to infer iOS 9 yet?
  • Pierre
    Pierre almost 9 years
    @Dzh well in theory you can. It looks like iOS 9 will support forceTouch events.. If so, then you can try and test 'webkitmouseforcechanged' in document;.. I don't have access to it yet, so if you are able to test this code let me know to update my answer.
  • tibalt
    tibalt almost 9 years
    But it doesn't work, cause ['str'].indexOf('string') == -1
  • dzh
    dzh almost 9 years
    @Pierre good idea, but I wonder whether that will only detect iPhone 6S (as I would expect force touch events will only be available on the devices that support them)?
  • Pierre
    Pierre almost 9 years
    Aah then we need to find another feature, OS related rather than Device related..
  • Kory Nunn
    Kory Nunn over 8 years
    navigator.platform will be exactly 'iPad', 'iPhone' or 'iPod' unless the simulator is running.
  • Dan
    Dan over 8 years
    MDN says that navigator.platform is deprecated and not to rely on it.
  • Pierre
    Pierre over 8 years
    Well spotted @Dan! I've updated the answer accordingly. Cheers
  • tibalt
    tibalt over 8 years
    Just tiny notice: you can simplify return x ? true : false to return Boolean(x) or just return !!x
  • gdibble
    gdibble about 8 years
    @Pierre your code is superb (truely; respect) and I rolled it into some other detection code i've been using in prod, resulting in a great/lightweight gist for modern UI/UX control - isMobile, isTablet, isIE, isIOS -gist.github.com/gdibble/df3fd4016e632344336f3227a6f159e6
  • aruno
    aruno over 7 years
    iOS10 now allows for playsinline so you can use 'playsInline' in document.createElement('video'); as a test now github.com/Modernizr/Modernizr/issues/2077
  • Ferie
    Ferie about 7 years
    On a Mac mini I have this string (maybe is helpful for someone that needs to upgrade the regex): navigator.userAgent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
  • Yulian
    Yulian about 7 years
    var iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; - this might not work when minifying your js. Use this instead - var iOS = new RegExp("/iPad|iPhone|iPod/").test(navigator.userAgent) && !window.MSStream;
  • cspanring
    cspanring over 6 years
    @Pierre @Dan navigator.platform is not deprecated, it's just not recommended to rely on it.
  • jessepinho
    jessepinho over 5 years
    why is Microsoft literally always the worst
  • deleb
    deleb about 5 years
    Please add "iPod touch" to the device list!! Other than that, perfect solution for my use case!
  • patrick
    patrick about 5 years
    what does !! do?
  • Vitim.us
    Vitim.us about 5 years
    @astronought double negation is used to cast to a boolean
  • Qback
    Qback about 5 years
    @astronought bang bang, you're boolean :D
  • undefined
    undefined about 5 years
    You are not checking whether it's an iOS device, but whether the device is either an iPhone, iPod or an iPad. If a new device with iOS were to be created, or if an Apple device is running another OS, then this will obviously not work. It's like detecting Windows by checking if the device is Microsoft Surface.
  • zvi
    zvi almost 5 years
    From iOS 13 the iPad's user agent has changed to "Mac OS", for example: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15 so this answer need to be updated
  • Jonny
    Jonny almost 5 years
    User agent of iPad safari will no longer include "iPad" from iPadOS 13.
  • 0xAnon
    0xAnon almost 5 years
    iOS = /^(iPhone|iPad|iPod)/.test(navigator.platform); rather than this i would do iOS = /^(iPhone|iPad|iPod)/.test(navigator.userAgent || navigator.vendor || navigator.platform); as a fallback measure cuz in my case navigator.platform didn't work, but doing it like later worked fine
  • lionello
    lionello over 4 years
    Using /iPhone|iPod|iPad/.test(navigator.platform) you can avoid the !!
  • j.j.
    j.j. over 4 years
    navigator.platform didn't work? Are you really on iOS then?. Check with jeka.info/test/navigator.html . userAgent gives false positives because some vendors fake it to mimic Apple devices for whatever reasons. vendor just returns either Google Inc., Apple Computer, Inc., or nothing (in Firefox).
  • Harry Theo
    Harry Theo over 4 years
    Why not use the navigator.userAgent for this check /iPad|iPhone|iPod/.test(navigator.platform)? It seems that navigator.platform always returns 'MacIntel' for iPhone iOS <= 12
  • Sandip Jadhav
    Sandip Jadhav over 4 years
    Not working for iOS 13. navigator.userAgent returns value of indexOf("ipad") as -1;
  • Kzrbill
    Kzrbill over 4 years
    @CharisTheo Because iPad is not in the userAgent in iOS >= 13
  • Harry Theo
    Harry Theo over 4 years
    but you are already checking for iPad iOS >= 13 in the second check or am I missing something?
  • sdfsdf
    sdfsdf about 4 years
    Doesn't work in Brave/Chrome dev tools mobile simulator. I get MacIntel
  • PaulC
    PaulC about 4 years
    navigator.maxTouchPoints isn't supported in iOS, so that check isn't going to do anything for you.
  • Bob Arlof
    Bob Arlof about 4 years
    @PaulC, You are correct in that maxTouchPoints is undefined for iOS 12 and below, but kikiwora is on the right track since maxTouchPoints is supported in iOS 13. See my answer.
  • Tspoon
    Tspoon almost 4 years
    It seems like this will have the side effect of actually changing the audio volume on non-iOS devices (in case that's important to anyone)
  • Bob Arlof
    Bob Arlof almost 4 years
    @Tspoon, The provided code snippet creates a throw-away Audio element (if necessary). The element is not actually used to play sound in this case, and it does not affect the volume of other Audio elements you might employ in your system.
  • Simon B.
    Simon B. almost 4 years
    platform not deprecated (no red border) and less messy than userAgent. You can use navigator.platform.indexOf('iP')>=0 or navigator.userAgent.indexOf("(iP")>=0 which seemed to cover 99% of cases (googled useragent and platform strings). And nobody develops to be compatible with 100% of all browsers anyway.
  • Simon B.
    Simon B. almost 4 years
    This misses out on the the "... Simulator" variety. So many answers, so few well-researched :(
  • Simon B.
    Simon B. almost 4 years
    @paul-rumkin Your last edit makes it NOT work for iOS 13 iPads. Please see the links right above what you edited.
  • Simon B.
    Simon B. almost 4 years
    @PaulRumkin thanks, but there were more necessary fixes in my rejected edit. Please at least fix this inconsistency: function iOS() (from someone elses edit) but then follows iOS will be either true or false (from original author). Also, the edit that changed from var to function forgot to change the text above the edit from the shortest alternative to something like the most easy to read and edit
  • Simon B.
    Simon B. almost 4 years
    Finally, this bit if (iOS) { // <-- Use the one here above feels a bit off in general. It is supposed to reuse whatever is your final decision on the topmost recommended approach. I think var is better than function for the topmost. Or reformat to allow it to run, emit a boolean, and garbage collect itself. And same applies to the lowermost function - no need to rerun it, so no need to have it as a function.
  • Simon B.
    Simon B. over 3 years
    @gcharita The edit you rejected yesterday was to fix two glaring bugs in the answer. Please can you fix the bug instead? I've tried and gotten rejected. Otherwise I can try hunting down the original author on social media perhaps. I just want this to be not wrong.
  • Simon B.
    Simon B. over 3 years
    This bit (navigator.userAgent.includes("Mac") && "ontouchend" in document) as recommended both here and elsewhere on StackOverflow page is no longer enough, since it returns true on Chrome 85 on a non touch device. Tacking on && navigator.maxTouchPoints > 2 seems like a good enough stop-gap fix for now.
  • drooh
    drooh over 3 years
    Curious if there is a similar PHP version of this? Can PHP handle the maxTouchPoints?
  • kikiwora
    kikiwora over 3 years
    @drooh I know not enough about PHP to be sure, but isn't it used for server-side logic? As far as I know, PHP was never executed in browsers. Thus, it would have no way to detect a page's runtime properties, only those that are passed during requests. Furthermore, the server should not ever know about the view layer, that's a responsibility of view, in our case - the page itself, not of a server that forms that page. Again, I may be mistaken about PHP, since I have had very limited experience with it.
  • toastal
    toastal about 3 years
    This ternary operator is useless; test returns a boolean and can replace match
  • IBG
    IBG over 2 years
    What if the browser is chrome?
  • JamesWilson
    JamesWilson over 2 years
    navigator.platform is now deprecated, so I'm searching for the replacement technique to no avail. developer.mozilla.org/en-US/docs/Web/API/Navigator/platform
  • strix25
    strix25 over 2 years
    yes navigator.platform is deprecated, yet still supported by all relevant browsers and its replacement "navigator.userAgentData.platform" is useless because in most browsers it returns just empty string or it is undefined as of 1st November 2021
  • strix25
    strix25 over 2 years
    @IBG just tried and it returns "undefined" on iOs chrome. So this answer is not correct
  • Jeffery To
    Jeffery To over 2 years
    @IBG I just tried this on Firefox 38.1 on iPhone (iOS 14.3) and Chrome 95.0.4638.50 on iPad (iPadOS 15.0.2) and it worked on both
  • Jeffery To
    Jeffery To over 2 years
    @strix25 are you sure you typed the code correctly?
  • Alfrex92
    Alfrex92 over 2 years
    @Pierre any replacement for navigator.platform ?
  • Alfrex92
    Alfrex92 over 2 years
    @kikiwora navigator.platform is deprecated, any other alternative? 😢
  • Alan Omar
    Alan Omar over 2 years
    navigator.vendor is deprecated as well
  • Ronni DC
    Ronni DC about 2 years
    Using the const in @JefferyTo's example above worked well for me with this conditional check: if (isIOS === true ) { //stuff for iOS } else {// stuff for all other }
  • Kevin Oswaldo
    Kevin Oswaldo about 2 years
    'platform' is deprecated
  • Kevin Oswaldo
    Kevin Oswaldo about 2 years
    'platform' is deprecated now
  • A-Tech
    A-Tech almost 2 years
    navigator.platform is being discouraged: "[it] should almost always be avoided in favor of feature detection."