iOS问题记录 - iOS 17通过NSUserDefaults设置UserAgent无效

news/2024/7/20 23:04:41 标签: ios

文章目录

  • 前言
  • 开发环境
  • 问题描述
  • 问题分析
  • 解决方案
  • 最后


前言

最近维护一个老项目时遇到的问题。说起这老项目我就有点头疼,一个快十年前的项目,这么说你可能不觉得有什么,但是你想想Swift也才发布不到十年(2014年6月发布,现2023年12月)。

开发环境

  • Xcode: 15.1
  • iOS: 17.2

问题描述

项目运行在iOS 17.2设备时,应用内网页无法成功获取设置后的UserAgent

项目中设置UserAgent的关键源码:

[self.webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) {
    NSString *userAgent = [NSString stringWithFormat:@"%@", result];
    NSString *newUserAgent = [userAgent stringByAppendingString:@" App/1.0.0"];
    [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"UserAgent": newUserAgent}];
}];

问题分析

从项目源码看,设置UserAgent是通过NSUserDefaults实现的。大致操作如下:

  1. 首先应用启动后创建一个WKWebView实例对象,无需加载任何网页
  2. 然后通过navigator.userAgent获取当前的UserAgent并修改
  3. 接着调用registerDefaults方法将修改后的UserAgent注册到NSUserDefaults,成为应用的默认设置
  4. 最后销毁该WKWebView实例对象

经过以上操作,后续创建WKWebView实例对象时都能从NSUserDefaults中获取到修改后的UserAgent用于初始化,从而实现全局设置。需要注意一点,registerDefaults方法设置默认值的操作不会进行持久化存储,所以应用每次启动都需要设置一遍。

虽然理了一遍设置逻辑感觉没什么问题,但是这个方法有点老了,现在一般通过customUserAgent属性设置,难道这个方法过时了?

为了排除其他可能存在的干扰,新建一个iOS项目用于测试,源码如下:

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {
    
    private let htmlString = """
    <html>
        <head>
            <meta name="viewport" content="width=device-width">
            <style>
                button {
                    font-size: 24px;
                }
            </style>
            <script>
                function getUserAgent() {
                    var userAgent = navigator.userAgent;
                    var paragraph = document.createElement('p');
                    paragraph.textContent = 'UserAgent: ' + userAgent;
                    document.body.appendChild(paragraph);
                }
            </script>
        </head>
        <body style="text-align: center;">
            <button οnclick="getUserAgent()">获取 UserAgent</button>
        </body>
    </html>
    """
    
    private var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 在WKWebView初始化前设置UserAgent
        UserDefaults.standard.register(defaults: ["UserAgent": "App/1.0.0"])
        
        // 创建WKWebView
        let webViewConfiguration = WKWebViewConfiguration()
        webView = WKWebView(frame: view.bounds, configuration: webViewConfiguration)
        webView.navigationDelegate = self
        view.addSubview(webView)
        
        // 加载HTML
        webView.loadHTMLString(htmlString, baseURL: nil)
    }
}

iOS 17.2测试结果:

screenshot1

iOS 16.4测试结果:

screenshot2

实测iOS 17.0和iOS 17.2测试结果一样,现在可以确定从iOS 17开始,通过NSUserDefaults设置UserAgent都无法生效。初步判断,这个设置方法在iOS 17及以上过时了,个人猜测可能是WKWebView初始化时不再从NSUserDefaults获取默认值导致的(今天天太冷了,实在扛不住,后面有机会再翻翻相关源码尝试验证猜测)。

既然这个方法失效了,那在iOS 17上通过customUserAgent属性设置能正常生效吗?

对前面的测试源码做简单修改,页面加载完成后自动获取一次未修改的UserAgent,然后再修改UserAgent并通过customUserAgent属性设置,接着手动点击按钮获取一次UserAgent,最后对比两次获取结果判断是否设置成功。

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {
    
    private let htmlString = """
    <html>
        <head>
            <meta name="viewport" content="width=device-width">
            <style>
                button {
                    font-size: 24px;
                }
            </style>
            <script>
                function getUserAgent() {
                    var userAgent = navigator.userAgent;
                    var paragraph = document.createElement('p');
                    paragraph.textContent = 'UserAgent: ' + userAgent;
                    document.body.appendChild(paragraph);
                }
                /* 页面加载完成后获取一次UserAgent */
                window.onload = function() {
                    getUserAgent();
                };
            </script>
        </head>
        <body style="text-align: center;">
            <button οnclick="getUserAgent()">获取 UserAgent</button>
        </body>
    </html>
    """
    
    private var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 创建WKWebView
        let webViewConfiguration = WKWebViewConfiguration()
        webView = WKWebView(frame: view.bounds, configuration: webViewConfiguration)
        webView.navigationDelegate = self
        view.addSubview(webView)
        
        // 加载HTML
        webView.loadHTMLString(htmlString, baseURL: nil)
    }
    
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        // 获取当前的UserAgent
        webView.evaluateJavaScript("navigator.userAgent") { (result, error) in
            guard let userAgent = result as? String else {
                return
            }
            let newUserAgent = userAgent + " App/1.0.0"
            // 设置UserAgent
            webView.customUserAgent = newUserAgent
        }
    }
}

测试结果:

screenshot3

从测试结果看,通过customUserAgent属性设置UserAgent一切正常。不过这种方法不好实现全局设置,每次创建新的WKWebView实例对象都需要再设置一遍。

解决方案

通过NSUserDefaults设置UserAgent改为通过customUserAgent属性设置UserAgent,更多详情请参考前面的问题分析。

最后

如果这篇文章对你有所帮助,点赞👍收藏🌟支持一下吧,谢谢~


本篇文章由@crasowas发布于CSDN。


http://www.niftyadmin.cn/n/5274016.html

相关文章

rabbitmq-常见七种消息队列-控制台界面管理-python-实现简单访问

文章目录 1.消息的基本概念1.1.生产者和消费者1.2.消息队列(Queue)1.3.交换机(Exchange)1.4.消息确认 2.七种队列模式2.1.简单模式(Hello World)2.2.工作队列模式(Work queues)2.3.发布订阅模式(Publish/Subscribe)2.4.路由模式(Routing)2.5.主题模式(Topics)2.6.远程过程调用(…

vue内容渲染

内容渲染指令用来辅助开发者渲染DOM元素的文本内容。常用的内容渲染指令有3个 1.v-text 缺点&#xff1a;会覆盖元素内部原有的内容 2.{{}}&#xff1a;插值表达式在实际开发中用的最多&#xff0c;只是内容的占位符&#xff0c;不会覆盖内容 3.v-html&#xff1a;可以把带有标…

22.VRRP网关冗余

VRRP网关冗余 STP 是二层冗余技术 VRRP是三层冗余技术 这里如果网关挂掉的话&#xff0c;用户就无法访问外网&#xff0c;还得配置新的网关&#xff0c;VRRP就是用来解决这种问题的 原理&#xff1a;可以把R1 的优先级设置的高一点&#xff0c;先由R1 来掌管192.168.1.254这个…

代码随想录第三十四天(一刷C语言)|不同路径不同路径II

创作目的&#xff1a;为了方便自己后续复习重点&#xff0c;以及养成写博客的习惯。 一、不同路径 思路&#xff1a;参考carl文档 机器人每次只能向下或者向右移动一步&#xff0c;机器人走过的路径可以抽象为一棵二叉树&#xff0c;叶子节点就是终点。 1、确定dp数组&#…

ansible模块 (7-13)

模块 7、hostname模块&#xff1a; 远程主机名管理模块 ansible 192.168.10.202 -m hostname -a nameliu 8、copy模块&#xff1a; 用于复制指定的主机文件到远程主机的模块 常用参数&#xff1a; dest: 指出要复制的文件在哪&#xff0c;必须使用绝对路径。如果源目标是…

【大数据面试】YARN常见问题与答案

目录 介绍下YARN YARN有几个模块 YARN工作机制 YARN有什么优势&#xff0c;能解决什么问题? YARN容错机制 YARN高可用 YARN调度器 YARN中Container是如何启动的? YARN的改进之处&#xff0c;Hadoop3.x相对于Hadoop 2.x? YARN监控 介绍下YARN YARN有几个模块 Yar…

【数据结构】二叉树的模拟实现

前言:前面我们学习了堆的模拟实现&#xff0c;今天我们来进一步学习二叉树&#xff0c;当然了内容肯定是越来越难的&#xff0c;各位我们一起努力&#xff01; &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:数据结构 &#x1f448; &…

JavaScript 循环语句之for、while循环 | break continue

for循环 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>首页</title> </head><body><script type"text/javascript">var num 100 for (let i 0; i < num; i){console.log(i)}&l…