【发布时间】:2021-12-11 23:31:13
【问题描述】:
将我们的 API 客户端切换到组合后,我们开始收到用户关于错误“无法完成操作 (NSURLErrorDomain -1.)”的报告,这是从我们的 API 客户端转发到 UI 的 error.localizedDescription。
顶级 api 调用如下所示:
class SomeViewModel {
private let serviceCategories: ICategoriesService
private var cancellables = [AnyCancellable]()
init(service: ICategoriesService) {
self.serviceCategories = service
}
// ...
// Yes, the block is ugly. We are only on the half way of the migration to Combine
func syncData(force: Bool = false, _ block: @escaping VoidBlock) {
serviceCategories
.fetch(force: force)
.combineLatest(syncOrders(ignoreCache: force))
.receive(on: DispatchQueue.main)
.sink { [unowned self] completion in
// bla-bla-bla
// show alert on error
}
.store(in: &cancellables)
}
}
低级 API 客户端调用如下所示:
func fetch<R>(_ type: R.Type, at endpoint: Endpoint, page: Int, force: Bool) -> AnyPublisher<R, TheError> where R : Decodable {
guard let request = request(for: endpoint, page: page, force: force) else {
return Deferred { Future { $0(.failure(TheError.Network.cantEncodeParameters)) } }.eraseToAnyPublisher()
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return URLSession.shared
.dataTaskPublisher(for: request)
.subscribe(on: DispatchQueue.background)
.tryMap { element in
guard
let httpResponse = element.response as? HTTPURLResponse,
httpResponse.statusCode == 200 else
{ throw URLError(.badServerResponse) }
return element.data
}
.decode(type: type, decoder: decoder)
.mapError { error in
// We map error to present in UI
switch error {
case is Swift.DecodingError:
return TheError.Network.cantDecodeResponse
default:
return TheError(title: nil, description: error.localizedDescription, status: -2)
}
}
.eraseToAnyPublisher()
}
在我们的分析中,我们可以清楚地看到事件链:
- 应用程序已更新
- 应用程序已打开
- 显示主屏幕
- 显示警报 (NSURLErrorDomain -1)
- 应用程序后台 然后用户陷入循环“打开,警报,后台”尝试重新启动或重新安装应用程序但没有成功。
首先寻找的是它可能是从后端发送到客户端的一些垃圾,但我们的服务器日志有与分析日志相关的 API 调用记录,按日期和时间与 http 状态代码 499。
所以我们可以清楚地确定这不是服务器问题。
在此更新之前,我们也没有来自用户的报告或分析记录。
所有指向新 API 客户端的点都切换到了 Combine。
看起来会话由于某种原因被客户端丢弃,但同时它与内存释放周期无关,因为如果可取消,则释放sink 将永远不会执行闭包并且不会显示警报消息。
问题:
- 此 URLSession 设置有什么问题?
- 您是否遇到过类似的行为并设法解决?
- 您知道如何使用 URLSession 重现或至少模拟此类错误吗?
注意事项:
- 我们不使用 SwiftUI
- iOS 版本从 14.8 到 15.0 不等
- 5% 到 10% 的用户受到影响
- 我们在开发或测试期间从未遇到过此类错误
【问题讨论】:
-
您有分析功能,显然有问题的设备能够访问互联网 - 但如果它无法访问您的特定 URL,该怎么办?您的分析是否与您尝试访问的 URL 位于同一端点?我在公司环境中工作,我们的网络将允许我们访问某些 URL,但不能访问其他 URL。也许用户处于分析可以通过的环境中,但是这些请求试图访问的任何 URL 都被阻止。所以会话失败。
-
DispatchQueue.background是什么类型的DispatchQueue?
-
@LuLuGaGa DisoatchQueue.background 只是 DisoatchQueue 扩展中的
static let background = DispatchQueue.global(qos: .background) -
@ScottThompson 你没记错 - 分析是第三方的,它可以工作。但与此同时,我们在服务器上记录了准确的 api 调用,因此客户端显然可以到达我们的服务器,但由于某种原因,它在服务器生成响应时中途断开连接。大部分请求在 2 到 200 毫秒之间变化,没有一个超过 3 秒,因此没有理由超时,因为我们使用 URLSession.shared,默认超时为 60 秒。
标签: ios swift combine urlsession nsurlsessiondatatask