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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 08:24:19