iOS修改WebView的UserAgent

4,508 阅读4分钟

UserAgent

由于 Android 和 iOS 与原生的交互不同,需要通过 UserAgent 来判断当前设备是苹果还是安卓,所以最好不要完全自定义 UserAgent,而是在默认的 UserAgent 后,拼接上所需要的自定义标识即可。

获取UserAgent

UIWebView 和 WKWebView 与 JS 交互的方法有点区别,UIWebView 是同步的,而 WKWebView 是异步的。

UIWebView方式

UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];NSString *userAgent = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
NSLog(@"userAgent :%@", userAgent);

WKWebView方式

WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero];[wkWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) {
    NSLog(@"userAgent :%@", result);
}];

默认UserAgent

以下是我的 iPhone 6s Plus,iOS 10.3.2 获取到的 UserAgent。

Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Mobile/14F89


无论使用 UIWebView 方式还是 WKWebView 方式,获取到的结果是一样的。也就是说,获取 UserAgent 不区分 webView 是哪个控件哪个内核。

修改UserAgent

修改全局UserAgent

如果想要统一自定义 UserAgent 让所有的 webView 访问网页时都生效,可以在 App 启动的时候,修改全局 UserAgent。

UIWebView方式
- (void)changeUIWebViewUserAgent{
    UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];
    NSString *userAgent = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
    NSString *newUserAgent = [userAgent stringByAppendingString:@" origin/sfddjapp"];
    if (DKToken) {
        newUserAgent = [newUserAgent stringByAppendingString:[NSString stringWithFormat:@" token/%@", DKToken]]; 
   }
   NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:newUserAgent, @"UserAgent", nil];
   [[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
}
  • userAgent 为默认 UserAgent。
  • newUserAgent 首先拼接了origin/sfddjapp ,标识当前是在「顺丰大当家 APP」环境。
  • DKToken 这个宏是缓存了用户登录后的 token,如果用户已登录,则再拼接上token/{token},标识当前访问页面的用户。
WKWebView方式
- (void)changeWKWebViewUserAgent{
    WKWebView *wkWebView = [[WKWebView alloc] initWithFrame:CGRectZero];
    self.wkWebView = wkWebView;
    [wkWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) {
        NSString *userAgent = result;
        NSString *newUserAgent = [userAgent stringByAppendingString:@" origin/sfddjapp"];
        if (DKToken) {
            newUserAgent = [newUserAgent stringByAppendingString:[NSString stringWithFormat:@" token/%@", DKToken]];
        }
        NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:newUserAgent, @"UserAgent", nil];
        [[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
    }];
}

虽然一样可以实现,但我不推荐使用这种方式,因为它是异步的。也就是必须要先声明个 property,调用 self.wkWebView = wkWebView; 把 wkWebView 保存起来。否则 block 回调时,这个 wkWebView 对象已经销毁了,回调的参数也都是 nil。

修改局部UserAgent

有时候只有部分页面访问的时候需要改 UserAgent,或者不同页面访问的时候需要修改不同的 UserAgent,这个时候就只能在加载页面前进行修改。

UIWebView方式
/** 修改UIWebView的UserAgent */
- (void)changeUIWebViewUserAgent{
    self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:self.webView];
    [self changeUIWebViewUserAgent];
    [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.whoishostingthis.com/tools/user-agent/"]]];
}

- changeUIWebViewUserAgent 方法就是上面修改全局 UserAgent 的方法,切记要在 - loadRequest: 之前调用。

注意,有时候把方法的调用写在 - loadRequest: 下面也没问题。这只是偶然,因为加载页面也是异步的,有时候会有延迟,实际上改 UserAgent 的代码执行完了才加载完页面。如果网速极端好的情况,就会出现 UserAgent 设置无效的问题。

还要注意,获取并修改 userAgent 的 webView 对象,跟加载网页的 webView 不能是同一个对象

我调用 - changeUIWebViewUserAgent 在方法内部重新初始化了一个 webView 对象去获取并修改 userAgent ,而 self.webView 则负责加载网页,两者不是同一个对象。否则,就会出现第一次设置 UserAgent 会无效的问题。

WKWebView方式

UIWebView修改+WKWebView加载

建议使用 UIWebView 的方式修改 UserAgent 后,再使用 WKWebView 加载网页,这样就很简单,使用起来跟 UIWebView 一样。

/** 修改WKWebView的UserAgent */
- (void)changeWKWebViewUserAgent{
    self.wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:self.wkWebView];
    [self changeUIWebViewUserAgent];
    [self.wkWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.whoishostingthis.com/tools/user-agent/"]]];
}

纯WKWebView修改+加载

当然也可以用纯 WKWebView 的方式,如果你真的如此执着 WebKit 的话。

/** 修改WKWebView的UserAgent */
- (void)changeWKWebViewUserAgent{
    WKWebView *wkWebView = [[WKWebView alloc] initWithFrame:CGRectZero];
    self.wkWebView = wkWebView;
        __weak typeof(self) weakSelf = self;
        [wkWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) {
        NSString *userAgent = result;
        NSString *newUserAgent = [userAgent stringByAppendingString:@" origin/sfddjapp"];
        NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:newUserAgent, @"UserAgent", nil];
        [[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
                __strong typeof(weakSelf) strongSelf = weakSelf;
        dispatch_async(dispatch_get_main_queue(), ^{
            // 重新初始化WKWebView
            strongSelf.wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds];
            [strongSelf.view addSubview:self.wkWebView]; 
           [strongSelf.wkWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.whoishostingthis.com/tools/user-agent/"]]];
        });
    }];
}

这里也一样有坑,调用获取并修改 UserAgent 的 wkWebView 对象和加载页面的 wkWebView 必须是不同的对象,也就是在回调里需要重新初始化 wKWebView。否则就会出现设置 UserAgent 无效的问题,大概也就是网上说的,“要第二次才会显示自定义的值”。

顺带一提,iOS9 出了新的 API

/*! @abstract The custom user agent string or nil if no custom user agent string has been set.*/@property (nullable, nonatomic, copy) NSString *customUserAgent API_AVAILABLE(macosx(10.11), ios(9.0));

可以直接修改 WKWebView 的 UserAgent。

总而言之,不管是 UIWebView 还是 WKWebView,获取到的 UserAgent 是一样的。如果要做到最简单最通用,就用 UIWebView 的方式获取并修改 UserAgent。还有注意,修改 UserAgent 之前获取 UserAgent 的 webView 对象,和修改之后调用加载网页的 webView 对象,不能是同一个对象,否则会出现第一次设置无效的问题。