聊一聊 WKWebView UserAgent 的相关姿势

一般情况下,web端的同学通过UA来区分当前浏览器是在PC还是Mobile或者App内,所以在App内设置UA就是必须要做的事情。

1. 通过 UserDefaults 设置

由于WKWebView有一个特性,在初始化时会获取UserDefaultsUserAgent这个key的值,这需要我们在真正使用的WKWebView之前要创建一个WKWebView获取他默认的UA;

webView = WKWebView();
webView?.evaluateJavaScript("navigator.userAgent", completionHandler: { (obj: Any?, error: Error?) in
   guard let ua = obj as? String else {
        return
    }
    let customUA = "\(ua) Custom User Agent"
    UserDefaults.standard.register(defaults: ["UserAgent": customUA])
    UserDefaults.standard.synchronize()
})

2. 通过 WKWebView.customUserAgent 设置

这种方式其实和第1种方式并没有什么差别,而且还比较复杂,因为只对这一个WebView有效,最关键的是这是一个异步执行js的代码,可能时间上会有些迟,虽然可以把异步转为同步。

还有一个坑点是之前我们没有通过navigator.userAgent先获取,再设置,而是直接设置了WKWebView.customUserAgent的值,导致并没有附带UA原有的值,这样web端的同学就拿不到浏览器的相关信息了。

let fakeWebView = WKWebView();
fakeWebView.evaluateJavaScript("navigator.userAgent", completionHandler: { (obj: Any?, error: Error?) in
    guard let ua = obj as? String else {
        return
    }
    let customUA = "\(ua) Custom User Agent"
    let realWebView = WKWebView()
    realWebView.customUserAgent = customUA
})

3. 通过 applicationNameForUserAgent 设置

  • config的此属性与上一个属性不同,不是将设置的字符串完全变成你所设置的值。

  • 它将字符串集添加到WebView的默认UserAgent并执行它。

  • 这正好就是我们想要的,我们所要做的就是设置要添加的字符串。

  • 修改后的UserAgent,如:Mozilla/5.0 (iPhone; CPU iPhone OS 13_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Custom UserAgent

let config = WKWebViewConfiguration()
config.applicationNameForUserAgent = "Custom User Agent"
let webview = WKWebView(frame: .zero, configuration: config)

4. 系统采用的优先级

customUserAgent > UserDefault > applicationNameForUserAgent

  • 左侧优先级高于右侧。
  • 如果设置了customUserAgentUserDefaults方法,则applicationNameForUserAgent将被忽略。
  • applicationNameForUserAgent仅添加到了webview具有的默认UserAgent中。

5. 关于 iPadOS 13 的特殊情况

5.1 问题

iPadOS 13以上,WKWebViewUserAgent变成了类似这样:

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

其主要原因是iPadOS设置-Safari浏览器-请求桌面网站-所有网站的设置默认是开启的,只要把它关掉就正常了,但是不能要求用户去做这个设置。

5.2 解决方案

WKWebView中新增的api中有个WKWebpagePreferences:

@property (null_resettable, nonatomic, copy) WKWebpagePreferences *defaultWebpagePreferences API_AVAILABLE(macos(10.15), ios(13.0));
typedef NS_ENUM(NSInteger, WKContentMode) {
 
    WKContentModeRecommended,
 
    WKContentModeMobile,
 
    WKContentModeDesktop
 
} API_AVAILABLE(ios(13.0));

WK_EXTERN API_AVAILABLE(macos(10.15), ios(13.0))
 
@interface WKWebpagePreferences : NSObject
 
@property (nonatomic) WKContentMode preferredContentMode API_AVAILABLE(ios(13.0));
 
@end

我们只需要在初始化的时候按下面的方式设置,就可以设置WKWebpagePreferencespreferredContentModeWKContentModeMobile,这样iPadOS上的UserAgent就正常了:

WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
if (@available(iOS 13.0, *)) {
  configuration.defaultWebpagePreferences.preferredContentMode = WKContentModeMobile;
}

UA就变为了:Mozilla/5.0 (iPad; CPU OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko)