2

I am porting an extension from Chrome/Firefox to Safari on iOS. When the extension popup comes up there are two different behaviors on Safari on iOS. On iPhone it comes up as a full screenwidth menu from the bottom of the screen. On iPad it behaves like Safari on MacOS and the popup comes down from the extension icon in the toolbar and is dynamically sized.

I would like to detect these two different cases in my javascript code. In the former case I want to refrain from setting the popup window width (since that causes problems) in the latter case I do want to set the width (in the same way that I do in Chrome etc).

I don't know for sure if it is a case of detecting iPad vs iPhone (but I would be interested in knowing how to do that). There could be an iPhone with a large enough screen size that would cause it to use the latter behavior.

Namaste
  • 203
  • 1
  • 10
  • So `window.navigator.userAgent` doesn't draw the distinction? – matt Dec 29 '21 at 01:37
  • @matt in the past I found that to be unreliable for determining ipad vs iphone. things might have changed recently though – justinw Dec 29 '21 at 15:02
  • It may be possible to determine iPhone vs iPad using user agent. Here are a few quick tests I did: Chrome on Mac: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36 iPad Pro 12.9 inch 5th generation iOS 15 simulator: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15 iPhone8 iOS 15.0 simulator: Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1 – Namaste Dec 29 '21 at 18:19
  • So possibly one could search for "iPhone" in the string. However, as justinw pointed out it can behave like iPhone in split view on iPad. So I'm now looking for a test that will tell me if the popup is coming up from the bottom in full window width – Namaste Dec 29 '21 at 18:19
  • @Namaste also keep in mind that if a user requests the desktop version of a website, it might not show up at `iPad` or `iPhone` in the user agent – justinw Dec 29 '21 at 21:02

1 Answers1

2

While figuring out macOS vs iOS is fairly simple through using browser.runtime.getPlatformInfo(), determining iOS vs iPadOS is more difficult.

When the extension popup comes up there are two different behaviors on Safari on iOS. On iPhone it comes up as a full screenwidth menu from the bottom of the screen. On iPad it behaves like Safari on MacOS and the popup comes down from the extension icon in the toolbar and is dynamically sized.

Keep in mind that on iPadOS the popup can also show up as it does on iOS when the browser is in split view. Because of this it's important to track window resize events and to test the popup in and out of split view, across all of the split view sizes.

The only complete solution I've found is using a combination of Swift, Javascript and CSS.

Swift side

In your WebExtension, or wherever you like, you could create a simple function that returns which platform you're on. I use this over browser.runtime.getPlatformInfo() since the latter can't discern iOS vs iPadOS. Something like:

func getPlatform() -> String {
    var platform:String
    #if os(iOS)
        if UIDevice.current.userInterfaceIdiom == .pad {
            platform = "ipados"
        }
        else {
            platform = "ios"
        }
    #elseif os(macOS)
        platform = "macos"
    #endif
    return platform
}

In order to communicate with the swift side of you application, you'll need to use native messaging from the javascript side - and put the appropriate message handlers on your swift side.

background.js


browser.runtime.sendNativeMessage({name: "getPlatformFromSwiftSide"}, response => {
    if (response.platform === "ipados") {
        ...
    } else if (response.platform === "ios") {
        ...
    }
});

SafariWebExtensionHandler.swift

class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {
    func beginRequest(with context: NSExtensionContext) {
        let item = context.inputItems[0] as? NSExtensionItem
        let message = item?.userInfo?[SFExtensionMessageKey] as? [String: Any]
        guard let name = message?["name"] as? String else {return}
        let response = NSExtensionItem()
        if name == "getPlatformFromSwiftSide" {
            let platform = getPlatform()
            response.userInfo = [SFExtensionMessageKey: ["platform": platform]]
        }
        context.completeRequest(returningItems: [response], completionHandler: nil)
    }
}


At this point you can distinguish between iPadOS and iOS. However you still have the issue of whether or not the browser is in split view or not. As mentioned, for certain sizes in split view, the popup on iPadOS is laid out differently and you must account for this.

Currently I am unaware of any methods to easily determine this, outside of CSS media queries or Javascript matchMedia calls.

Further, calling window from the popup (or using media queries in the popup context) won't indicate the browser's size, but rather the popups. Since we wan't to know the browser size to determine how to size the popup, the only reliable way I know of accomplishing this is by using browser.tabs.sendMessage, send a message to the content script requesting the browser window size.


All of this seems more complicated than it need be, but I know of no other alternatives currently.

justinw
  • 3,856
  • 5
  • 26
  • 41
  • Thanks. There is a lot of useful information there. I was not aware of split view. I just tested my extension in the simulator for both iPad Pro 12.9 inch (5th generation) and for iPad Air 4th generation. I did split view in both but in both cases the extension icon still showed up in the URL bar and the popup came down from the top. So I have not yet been able to find a case where the popup behaves like iPhone on iPad. Do you know a way to make that happen? – Namaste Dec 29 '21 at 17:54
  • Nevermind - I found a way. On either iPad if it is in portrait mode and split view is used then the popup menu behaves like on iPhone. Also in landscape mode there is a "..." at the top of the window in split view and that has a menu that lets you make one of the split panes smaller than the other. This results in the iPhone like popup. – Namaste Dec 29 '21 at 18:04
  • @Namaste also keep in mind you can re-size the split view, so in landscape view you can make it a quarter width, rather than half width. Don't forget to accept the answer if you feel it provided you with an adequate solution – justinw Dec 29 '21 at 20:56
  • 1
    Accepted the answer as it answered the headline question and also provided me with a lot of useful info. I found a different solution for my actual problem which was to determine whether the extension popup was a fixed width window coming up from the bottom of the screen. My solution was to first test whether platformOS is ios. If so then set an onresize handler. In the first onresize call I test window.outerWidth and can use that to determine if this is a fixed width menu. In my extension if outerWidth is < 100 I know that it is not fixed width. – Namaste Dec 29 '21 at 21:48