URL在Postman和浏览器可访问但Swift中URL(string:)返回nil
URL(string:)返回nil的核心原因是输入的字符串不符合RFC 2396规范,浏览器、Postman会自动对非法字符做自动编码处理,但Swift原生方法不会做自动处理,因此会直接返回nil。
排查及修复步骤
- 第一步:校验原始URL字符串排查
将传入的urlString参数打印出来,检查是否包含未转义的特殊字符:包括中文、空格、#、&、中文符号、emoji、换行符、零宽空格等隐形特殊符号,这类字符出现在URL路径、查询参数中时,是最常见的触发问题。 - 第二步:增加URL编码逻辑
在初始化URL前先对字符串做百分号编码,可根据URL的使用场景选择对应的合法字符集:- 完整URL包含路径和查询参数:使用
.urlQueryAllowed - 仅查询参数值:使用
.urlQueryAllowed或更严格的自定义字符集
代码示例:
- 完整URL包含路径和查询参数:使用
// 优先尝试直接解析,解析失败再做编码,避免重复编码问题 var finalUrl: URL? if let url = URL(string: urlString) { finalUrl = url } else if let encodedStr = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: encodedStr) { finalUrl = url } guard let validUrl = finalUrl else { print("URL解析失败") // 可返回自定义错误给上层业务层处理 completion(.failure(NSError(domain: "NetworkError", code: -1000, userInfo: [NSLocalizedDescriptionKey: "无效的URL地址"]))) return }
- 第三步:检查URL格式完整性
确认URL是否携带完整的协议前缀http://或https://,缺少协议前缀的字符串大概率会被判定为非法URL。
优化后的完整方法
func performRequest<T: Codable>(urlString: String, returnType: T.Type, completion: @escaping (Result<T, Error>) -> Void ) { print("\n\(#function)") // 新增URL编码校验逻辑 var targetUrl: URL? if let url = URL(string: urlString) { targetUrl = url } else if let encodedStr = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: encodedStr) { targetUrl = url } guard let url = targetUrl else { print("Something went wrong with the url") let error = NSError(domain: "NetworkError", code: -1000, userInfo: [NSLocalizedDescriptionKey: "无效的URL地址"]) completion(.failure(error)) return } let session = URLSession(configuration: .default) let task = session.dataTask(with: url) { (data, _, error) in if let error = error { completion(.failure(error)) return } guard let data = data else { let error = NSError(domain: "NetworkError", code: -1001, userInfo: [NSLocalizedDescriptionKey: "接口无返回数据"]) completion(.failure(error)) return } let decoder = JSONDecoder() do { let decodedData = try decoder.decode(T.self, from: data) completion(.success(decodedData)) } catch let decodingErr { completion(.failure(decodingErr)) } } task.resume() }
内容的提问来源于stack exchange,提问作者Ryan Kanno
相关产品推荐
相关产品推荐

