Search code examples
xcodeipadios-simulatoruser-agent

How to test IOS in use from the javascript inside a WKWebView on iPadOS 13


When I use XCode 11.1 to test my hybrid app using a WKWebView in the "iPhone 8" simulator and inspect the userAgent using Safari 13, I get the following string :

"Mozilla/5.0 (iPhone; CPU iPhone OS 13_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148" 

However when I test the app in the "iPad Pro (11-inch)" simulator, I have found that the userAgent is set to the following string :

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

Note that this string does not contain the sub-string "iPad", nor the version of IOS in use, nor the fact that it is a Mobile app.

As my app examines userAgent to determine whether it is running under IOS, this works fine on the iPhone but presents me with a problem on how now to run this test on iPads that are running iPadOS 13. Please note that this is nothing to do with how the web view is displayed, it is to do with whether or not to establish communication between the javascript and Objective C.

My app worked fine on all iPads running IOS 12 as the userAgent is what I expected:

"Mozilla/5.0 (iPad; CPU OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16B92"

How should I now determine from inside Javascript whether my code is running on an iPad that is running iPadOS 13 or later?


Solution

  • There are a number of ways I could have solved this.

    1. Not testing at all - and simply using evaluateJavascript after loading to tell the webview that it was IOS.

    2. Detecting iPad using Objective C/swift and registering a custom user agent string in the user defaults containing the string "iPad: before creating the web view

       // get original UserAgent string by using temporal webView
       WKWebView *tmp = [[WKWebView alloc] init];
       NSString *originalUA = [tmp stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
      
       // create custom UserAgent string
       NSString *customUA = [NSString stringWithFormat:@"%@ %@", originalUA, @" iPad"];
      
       // set custom UserAgent as default
       NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:customUA , @"UserAgent", nil];
       [[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
      

    However and perhaps for the wrong reasons I decided to use a solution from Missing iPad tablet Web traffic

    as follows

    But it will incorrectly detect a new mac (with Apple m1 chip) as an iPad.

    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
    var IsChromeApp = window.chrome && chrome.app && chrome.app.runtime;
    
    // iPhone
    var IsIPhone = agent.match (/iPhone/i) != null;
    
    // iPad up to IOS12
    var 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            
    var IsIOSApp = IsIPad || IsIPhone ;
    
    // Android
    var IsAndroid = agent.match (/Android/i) != null; 
    var IsAndroidPhone = IsAndroid && deviceWidth <= 960 ;
    var IsAndroidTablet = IsAndroid && !IsAndroidPhone ;