iOS13+下不使用已废弃init实现URLSessionDataTask Mock的方案问询
适配iOS 13+的URLSession Mock方案
方案一:基于协议抽象的Mock(优先推荐,无兼容性风险)
这种方案完全规避了继承系统URLSession相关类带来的API废弃问题,耦合度更低,同时兼容所有iOS版本。
首先定义两个协议抽象网络请求能力:
// 抽象URLSession的核心能力 protocol URLSessionProtocol { func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTaskProtocol } // 抽象DataTask的核心能力 protocol URLSessionDataTaskProtocol { func resume() func cancel() }
让系统原生类遵循上述协议,业务层无需修改原有调用逻辑:
extension URLSession: URLSessionProtocol { func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTaskProtocol { return dataTask(with: request, completionHandler: completionHandler) as URLSessionDataTask } } extension URLSessionDataTask: URLSessionDataTaskProtocol {}
实现Mock类,完全不需要继承系统的URLSession和URLSessionDataTask:
class URLSessionDataTaskMock: URLSessionDataTaskProtocol { private let closure: () -> Void init(closure: @escaping () -> Void) { self.closure = closure } func resume() { closure() } func cancel() { closure() } } class URLSessionMock: URLSessionProtocol { var data: Data? var response: URLResponse? var error: Error? func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTaskProtocol { let data = self.data let response = self.response let error = self.error return URLSessionDataTaskMock { completionHandler(data, response, error) } } }
使用方式:业务代码中依赖注入时使用URLSessionProtocol类型,正式环境传原生URLSession实例,测试环境传URLSessionMock实例即可。
方案二:兼容原有子类化实现的修复方案
如果你不想调整原有代码结构,仍坚持子类化URLSession和URLSessionDataTask,可以通过添加可用标注消除废弃警告。注意该方案仅建议在测试代码中使用,不推荐用于生产环境:
class URLSessionDataTaskMock: URLSessionDataTask { private let closure: () -> Void init(closure: @escaping () -> Void) { self.closure = closure super.init() } @available(*, deprecated, message: "This mock class is for testing only") override init() { fatalError("Use init(closure:) instead") } override func resume() { closure() } override func cancel() { closure() } } class URLSessionMock: URLSession { typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void var data: Data? var error: Error? override func dataTask( with _: URLRequest, completionHandler: @escaping CompletionHandler ) -> URLSessionDataTask { let data = self.data let error = self.error return URLSessionDataTaskMock { completionHandler(data, nil, error) } } }
由于苹果仅废弃了外部直接调用URLSessionDataTask()初始化的能力,子类内部调用super.init()在测试环境下运行是稳定的,不会出现崩溃问题。
内容的提问来源于stack exchange,提问作者gmoraleda
相关产品推荐
相关产品推荐

