Swift中URLSession泛型请求如何返回Data以加载图片?
问题解决方案
你的核心问题是:URLSessionManager的泛型fetchData方法要求T遵循Decodable协议,但获取图片时需要直接返回Data(无需JSON解码),当前代码在isImageFetch分支无法正确返回Data作为泛型结果。以下是两种可行的解决方式:
方案一:修改现有泛型方法,兼容直接返回Data
由于Data本身已经遵循Decodable协议,你可以在isImageFetch分支直接将获取到的原始数据转换为泛型T并返回,跳过JSON解码步骤:
修改后的URLSessionManager代码
class URLSessionManager { static let shared = URLSessionManager() static func fetchData<T: Decodable>(with url: URL, isImageFetch: Bool = false, completion: @escaping (Result<T, SessionDataTaskError>)->Void) { URLSession.shared.dataTask(with: url) { (data, response, error) in if let error = error { completion(.failure(SessionDataTaskError.failedIssue(error))) return } guard let data = data else { completion(.failure(SessionDataTaskError.dataIsNil)) return } if isImageFetch { // 将原始Data转换为泛型T(此时调用方需保证T为Data类型) guard let resultData = data as? T else { completion(.failure(SessionDataTaskError.invalidDataType)) return } completion(.success(resultData)) return } do { let genericType = try JSONDecoder().decode(T.self, from: data) completion(.success(genericType)) } catch { completion(.failure(SessionDataTaskError.catchIssue(error))) guard let err = error as? DecodingError else { debugPrint("decodingError-:", error) return } switch err { case .typeMismatch(let key, let value): print("decodingError-Mismatch: \(key), value \(value) and ERROR: \(error.localizedDescription)") case .valueNotFound(let key, let value): print("decodingError-ValueNotFound: \(key), value \(value) and ERROR: \(error.localizedDescription)") case .keyNotFound(let key, let value): print("decodingError-KeyNotFound(: \(key), value \(value) and ERROR: \(error.localizedDescription)") case .dataCorrupted(let key): print("decodingError-DataCorrupted: \(key), and ERROR: \(error.localizedDescription)") default: print("decodingError-UnknownError: \(error.localizedDescription)") } } }.resume() } }
补充:新增错误类型
需要在SessionDataTaskError中添加类型转换失败的错误枚举:
enum SessionDataTaskError: Error { case failedIssue(Error) case dataIsNil case catchIssue(Error) case invalidDataType // 新增错误类型,处理Data转T失败的情况 }
方案二:重载fetchData方法(更推荐)
为了让代码职责更清晰,避免用标记参数区分逻辑,直接重载一个专门用于获取原始Data的方法:
修改后的URLSessionManager代码
class URLSessionManager { static let shared = URLSessionManager() // 用于获取可解码的模型数据 static func fetchData<T: Decodable>(with url: URL, completion: @escaping (Result<T, SessionDataTaskError>)->Void) { URLSession.shared.dataTask(with: url) { (data, response, error) in if let error = error { completion(.failure(SessionDataTaskError.failedIssue(error))) return } guard let data = data else { completion(.failure(SessionDataTaskError.dataIsNil)) return } do { let genericType = try JSONDecoder().decode(T.self, from: data) completion(.success(genericType)) } catch { completion(.failure(SessionDataTaskError.catchIssue(error))) guard let err = error as? DecodingError else { debugPrint("decodingError-:", error) return } switch err { case .typeMismatch(let key, let value): print("decodingError-Mismatch: \(key), value \(value) and ERROR: \(error.localizedDescription)") case .valueNotFound(let key, let value): print("decodingError-ValueNotFound: \(key), value \(value) and ERROR: \(error.localizedDescription)") case .keyNotFound(let key, let value): print("decodingError-KeyNotFound(: \(key), value \(value) and ERROR: \(error.localizedDescription)") case .dataCorrupted(let key): print("decodingError-DataCorrupted: \(key), and ERROR: \(error.localizedDescription)") default: print("decodingError-UnknownError: \(error.localizedDescription)") } } }.resume() } // 专门用于获取原始Data(比如图片资源) static func fetchRawData(with url: URL, completion: @escaping (Result<Data, SessionDataTaskError>)->Void) { URLSession.shared.dataTask(with: url) { (data, response, error) in if let error = error { completion(.failure(SessionDataTaskError.failedIssue(error))) return } guard let data = data else { completion(.failure(SessionDataTaskError.dataIsNil)) return } completion(.success(data)) }.resume() } }
修改UIImageView扩展调用
extension UIImageView { func loadImage(with url: URL) { if let cachedImage = imageCache.object(forKey: url.absoluteString as AnyObject) as? UIImage { self.image = cachedImage return } URLSessionManager.fetchRawData(with: url) { (result: Result<Data, SessionDataTaskError>) in switch result { case .failure(let error): print(error) case .success(let data): guard let downloadedImage = UIImage(data: data) else { print("image data is nil") return } DispatchQueue.main.async{ [weak self] in imageCache.setObject(downloadedImage, forKey: url.absoluteString as AnyObject) self?.image = downloadedImage } } } } }
方案二的优势
- 职责清晰:两个方法分别处理不同场景,无需额外标记参数区分逻辑
- 避免风险:方案一中的类型转换存在潜在运行时错误,方案二完全规避了这类问题
- 扩展性好:后续新增特殊需求时,可继续重载对应方法,不影响原有逻辑
内容的提问来源于stack exchange,提问作者Lance Samaria
相关产品推荐
相关产品推荐

