Swift 5中Swizzle URLSession的data(for:)方法失败,求解决方案
解决URLSession.data(for:)方法Swizzle失败的问题
报错原因
URLSession.data(for:)是Swift 5.5+引入的原生异步方法,未通过@objc暴露给Objective-C runtime,因此无法用#selector(URLSession.data(for:))获取其选择器,常规基于Objective-C runtime的方法交换(method_exchangeImplementations)无法直接生效。
解决方案
方案一:调试专用(使用Swift Runtime私有API)
仅适合本地调试验证猜想,私有API可能导致App Store审核不通过:
import ObjectiveC.runtime import Foundation // 声明Swift Runtime私有函数(需手动引入) func swift_getMethod(_ klass: AnyClass, _ name: UnsafePointer<CChar>) -> UnsafeMutableRawPointer? func swift_setMethod(_ klass: AnyClass, _ name: UnsafePointer<CChar>, _ method: UnsafeMutableRawPointer?) extension URLSession { // 自定义替换方法 private func swizzled_data(for request: URLRequest) async throws -> (Data, URLResponse) { // 添加自定义逻辑,比如日志、拦截等 print("Swizzled data(for:) triggered for request: \(request.url?.absoluteString ?? "")") // 调用原方法(交换后,当前方法名指向原实现) return try await swizzled_data(for: request) } static func swizzleDataFor() { guard let originalMethod = swift_getMethod(Self.self, "data(for:)"), let swizzledMethod = swift_getMethod(Self.self, "swizzled_data(for:)") else { print("Failed to get method pointers") return } // 交换方法实现 let temp = UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout<UnsafeMutableRawPointer>.size, alignment: 1) memcpy(temp, originalMethod, MemoryLayout<UnsafeMutableRawPointer>.size) memcpy(originalMethod, swizzledMethod, MemoryLayout<UnsafeMutableRawPointer>.size) memcpy(swizzledMethod, temp, MemoryLayout<UnsafeMutableRawPointer>.size) temp.deallocate() } } // 在App启动时调用交换 // 例如在AppDelegate.didFinishLaunchingWithOptions或SceneDelegate.scene(_:willConnectTo:options:)中 URLSession.swizzleDataFor()
方案二:稳定替代(Swizzle dataTask(with:completionHandler:))
data(for:)的底层实现大概率依赖dataTask(with:completionHandler:),Swizzle该方法可覆盖包括data(for:)在内的所有请求路径,且基于Objective-C runtime,无私有API风险:
import ObjectiveC.runtime import Foundation extension URLSession { static func swizzleDataTaskCompletion() { let originalSelector = #selector(dataTask(with:completionHandler:) as (URLSession) -> (URLRequest, @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask) let swizzledSelector = #selector(swizzled_dataTask(with:completionHandler:)) guard let originalMethod = class_getInstanceMethod(Self.self, originalSelector), let swizzledMethod = class_getInstanceMethod(Self.self, swizzledSelector) else { return } method_exchangeImplementations(originalMethod, swizzledMethod) } @objc private func swizzled_dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { // 添加自定义逻辑 print("Swizzled dataTask(completion) triggered for request: \(request.url?.absoluteString ?? "")") // 调用原方法 return swizzled_dataTask(with: request, completionHandler: completionHandler) } } // 在App启动时调用 URLSession.swizzleDataTaskCompletion()
使用该方案后,调用try await URLSession.shared.data(for:)时会触发Swizzled方法的日志,可验证你的猜想。
内容的提问来源于stack exchange,提问作者cnbleu
相关产品推荐
相关产品推荐

