You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.10 08:18:02