35

The user agent of Safari on iPadOS beta is at this point exactly the same as Safari on macOS. Is there any other way to tell an iPad from a Mac?

iPad running iOS
Mozilla/5.0 (iPad; CPU OS 10_3_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.0 Mobile/14G60 Safari/602.1

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

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

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

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

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

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

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

iPadOS 13.1, developer beta 3
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Safari/605.1.15

iPadOS 13.1, developer beta 4
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Safari/605.1.15

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

macOS (older version)
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 Safari/605.1.15

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

macOS Catalina developer beta 8
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Safari/605.1.15

When playing HLS video, the iPadOS agent seems to be:

AppleCoreMedia/1.0.0.17A5821e (iPad; U; CPU OS 13_1 like Mac OS X; en_us)
Jonny
  • 15,955
  • 18
  • 111
  • 232
  • I'm wondering if this is actually another case of "spoofing" mentioned here. https://stackoverflow.com/a/7976057/129202 – Jonny Jun 18 '19 at 02:46
  • In Japanese, nonetheless https://qiita.com/m-otoguro/items/00602315724d4a8e580c – Jonny Jun 18 '19 at 02:47
  • 2
    I added the user agent of next macOS Catalina. Note that it is **EXACTLY** the same as iPadOS. – Jonny Jun 18 '19 at 03:05
  • I think you just can't ??? Currently my solution is, if user-agent shows 10.15, show a pop-up ask if is iPadOS or macOS. – Alen Liang Jun 24 '19 at 04:53
  • 1
    Looking for a solution to this issue as well, and the only thing I can see that may be of use is this: Navigator.standalone From https://developer.mozilla.org/en-US/docs/Web/API/Navigator: "Returns a boolean indicating whether the browser is running in standalone mode. Available on Apple's iOS Safari only." Trying to get a machine with macOS 10.15 to see if it returns a value on desktop safari, but on my 10.14 macOS safari it was undefined. Returned a value in 10.15 iOS/ipadOS in my simulator – user2879041 Jul 23 '19 at 17:46
  • Interesting thanks, will try on macOS 10.15 when I get time. But then again it sounds pretty fragile even if it would work now. If possible we should probably all treat iPads as desktops going forward. – Jonny Jul 24 '19 at 05:56
  • @Jonny please update with your findings on macOS 10.15.. but I agree it's fragile, not the best solution – user2879041 Jul 24 '19 at 14:30
  • @Jonny a catalina 10.15 beta (from July 16, 2019) macOS safari version did not have the navigator.standalone.. so this feature detection may be of use. Maybe combine it with screen size detection? either way, I am still curious if your macOS 10.15 has navigator.standalone – user2879041 Jul 24 '19 at 15:05
  • @user2879041 A pretty recent Catalina returned `undefined` for `Navigator.standalone`. – Jonny Jul 25 '19 at 01:26
  • @user2879041 iPadOS, developer beta 4 also returned `undefined` for `Navigator.standalone`. – Jonny Jul 25 '19 at 01:29

5 Answers5

44

I'm using this test client side:

   if (navigator.userAgent.match(/Mac/) && navigator.maxTouchPoints && navigator.maxTouchPoints > 2) {
     ...must be iPad OS...

Since there's no official touch screen for Mac, it seems pretty safe. The actual value of maxTouchPoints on the iPad is 5, BTW.

Joshua Smith
  • 3,689
  • 4
  • 32
  • 45
  • 13
    And to those who say it shouldn't matter (use fluid webdesign!) it does matter if the user needs to either install a Safari app/extension from the Mac Store or use the iOS Share extension. I need to provide the correct link and directions. – user984003 Mar 18 '20 at 20:48
  • 2
    iPad emulation on Chrome only supports 1 maxTouchPoints, so unless you don't plan on debugging using Chrome I would suggest dropping the > 2 check. – Nadav Aug 04 '20 at 06:21
13

I would not generally recommend this, and I haven't tested it much (using something similar in production since September 2019), but one way could be to detect the existence of TouchEvent on the client side, and match it with the state of the user agent to account for older iOS versions of iPad:s. YMMV. Likely not very future safe.

function isIpad() {
    const ua = window.navigator.userAgent;
    if (ua.indexOf('iPad') > -1) {
        return true;
    }

    if (ua.indexOf('Macintosh') > -1) {
        try {
            document.createEvent("TouchEvent");
            return true;
        } catch (e) {}
    }

    return false;
}
Jonny
  • 15,955
  • 18
  • 111
  • 232
3

I'm using this works fine:

const ua = window.navigator.userAgent.toLowerCase();
const isiPad = ua.indexOf('ipad') > -1 || ua.indexOf('macintosh') > -1 && 'ontouchend' in document;
Zohid
  • 438
  • 1
  • 3
  • 14
  • 1
    Checking only userAgent is NOT enough. Maybe `navigator.maxTouchPoints > 2` is a good enough stop-gap fix. – Simon B. Oct 05 '20 at 10:38
2

as @Jonny mentioned you can try and detect touch events, this is the solution i currently use

function isTouchDevice() {
  if (typeof window === 'undefined') return false
  const prefixes = ' -webkit- -moz- -o- -ms- '.split(' ')
  function mq(query) {
    return typeof window !== 'undefined' && window.matchMedia(query).matches
  }
  if ('ontouchstart' in window || (window?.DocumentTouch && document instanceof DocumentTouch)) return true
  const query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('') // include the 'heartz' - https://git.io/vznFH
  return mq(query)
}

interesting post on detecting touch events

and another note, Firefox on Ipad uses the same safari user-agent :/

Paul van Dyk
  • 868
  • 10
  • 8
  • `isTouchDevice()` (May 24th version) returns true on Chrome 85 on a MBP (Catalina) without touch. Maybe `navigator.maxTouchPoints > 2` is a good enough stop-gap fix. – Simon B. Oct 05 '20 at 10:39
0

If you use ua-parser-js to parse the User-Agent, you will see that iPad has "Mobile Safari" as the browser.name, while Macs have "Safari".

If the user is logged in on Chrome, parsedUA.device.model will be iPad for iPad.

The only problem seems to be on Firefox... which has "Safari" as browser.name and "Macintosh" as parsedUA.device.model for iPad...

( I am trying to rely solely on User-Agent because I'm pulling user's logged-in devices from back-end and I have no other information about the user's device )

Filip Savic
  • 2,737
  • 1
  • 29
  • 34