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

Swift 4中修改现有代码以从返回两个图片URL的API接口获取并解析两张图片

解决APOD API获取两张图片的问题

看起来你需要从NASA的APOD接口获取标准分辨率和高分辨率两张图片对吧?原来的代码只处理了单个url字段,我们需要调整几个关键部分来支持双图片的解析和下载:


关键修改步骤

1. 更新API响应模型

APOD API返回的JSON里包含url(标准图)和hdurl(高分辨率图)两个图片链接,首先要扩展你的响应结构体,同时考虑高分辨率图可能不存在的情况:

struct ApodApiResponse: Decodable {
    var url: String
    var hdurl: String? // 高分辨率图为可选字段
    var title: String
}

2. 修改响应结果枚举

原来的ApodImageResponse只能返回单张图,我们需要调整它来支持多图返回,同时兼容部分下载失败的场景:

enum ApodImageResponse {
    case Success(images: [UIImage], titles: [String]) // 存储多张图及对应标题
    case PartialSuccess(mainImage: UIImage, title: String, errorMessage: String) // 仅单张成功
    case Failure(errorMessage: String) // 全部失败
}

3. 重构并行下载逻辑

原来的代码是串行下载单张图,现在需要用DispatchGroup来并行处理两个图片下载任务,确保所有任务完成后再返回结果:

修改parseResponseAndGetImage方法:

static func parseResponseAndGetImage(data: Data?, urlResponse: URLResponse?, error: Error?, completion: ((ApodImageResponse) -> Void)?) {
    guard error == nil, let content = data else {
        completion?(.Failure(errorMessage: "获取API数据失败"))
        return
    }
    
    var apodApiResponse: ApodApiResponse
    do {
        apodApiResponse = try JSONDecoder().decode(ApodApiResponse.self, from: content)
    } catch {
        completion?(.Failure(errorMessage: "解析API响应失败:\(error.localizedDescription)"))
        return
    }
    
    // 整理需要下载的图片链接列表
    var imageUrls: [String] = [apodApiResponse.url]
    if let hdUrl = apodApiResponse.hdurl {
        imageUrls.append(hdUrl)
    }
    
    let dispatchGroup = DispatchGroup()
    var downloadedImages: [UIImage] = []
    var downloadErrors: [String] = []
    
    for imageUrlString in imageUrls {
        guard let url = URL(string: imageUrlString) else {
            downloadErrors.append("无效图片链接:\(imageUrlString)")
            continue
        }
        
        dispatchGroup.enter()
        let urlRequest = URLRequest(url: url)
        
        URLSession.shared.dataTask(with: urlRequest) { data, _, error in
            defer { dispatchGroup.leave() }
            
            if let error = error {
                downloadErrors.append("下载图片失败:\(error.localizedDescription)")
                return
            }
            
            guard let imageData = data, let image = UIImage(data: imageData) else {
                downloadErrors.append("图片数据解析失败")
                return
            }
            
            downloadedImages.append(image)
        }.resume()
    }
    
    // 所有任务完成后统一返回结果
    dispatchGroup.notify(queue: .main) {
        switch (downloadedImages.count, downloadErrors.count) {
        case (2, 0):
            completion?(.Success(images: downloadedImages, titles: [apodApiResponse.title, "\(apodApiResponse.title) (高清版)"]))
        case (1, _):
            completion?(.PartialSuccess(mainImage: downloadedImages[0], title: apodApiResponse.title, errorMessage: downloadErrors.joined(separator: " | ")))
        default:
            completion?(.Failure(errorMessage: downloadErrors.joined(separator: " | ")))
        }
    }
}

4. 移除冗余方法

原来的parseImageFromResponse方法已经不需要了,图片解析逻辑已经整合到下载任务的闭包中。


完整修改后的代码

import Foundation
import SwiftUI

enum ApodImageResponse {
    case Success(images: [UIImage], titles: [String])
    case PartialSuccess(mainImage: UIImage, title: String, errorMessage: String)
    case Failure(errorMessage: String)
}

struct ApodApiResponse: Decodable {
    var url: String
    var hdurl: String?
    var title: String
}

class ApodImageProvider {
    static func getImageFromApi(completion: ((ApodImageResponse) -> Void)?) {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        formatter.timeZone = TimeZone(abbreviation: "UTC")
        let date = Date()
        let urlString = "https://api.nasa.gov/planetary/apod?api_key=eaRYg7fgTemadUv1bQawGRqCWBgktMjolYwiRrHK&date=\(formatter.string(from: date))"
        guard let url = URL(string: urlString) else {
            completion?(.Failure(errorMessage: "无效API链接"))
            return
        }
        let urlRequest = URLRequest(url: url)
        
        let task = URLSession.shared.dataTask(with: urlRequest) { data, urlResponse, error in
            parseResponseAndGetImage(data: data, urlResponse: urlResponse, error: error, completion: completion)
        }
        task.resume()
    }
    
    static func parseResponseAndGetImage(data: Data?, urlResponse: URLResponse?, error: Error?, completion: ((ApodImageResponse) -> Void)?) {
        guard error == nil, let content = data else {
            completion?(.Failure(errorMessage: "获取API数据失败"))
            return
        }
        
        var apodApiResponse: ApodApiResponse
        do {
            apodApiResponse = try JSONDecoder().decode(ApodApiResponse.self, from: content)
        } catch {
            completion?(.Failure(errorMessage: "解析API响应失败:\(error.localizedDescription)"))
            return
        }
        
        var imageUrls: [String] = [apodApiResponse.url]
        if let hdUrl = apodApiResponse.hdurl {
            imageUrls.append(hdUrl)
        }
        
        let dispatchGroup = DispatchGroup()
        var downloadedImages: [UIImage] = []
        var downloadErrors: [String] = []
        
        for imageUrlString in imageUrls {
            guard let url = URL(string: imageUrlString) else {
                downloadErrors.append("无效图片链接:\(imageUrlString)")
                continue
            }
            
            dispatchGroup.enter()
            let urlRequest = URLRequest(url: url)
            
            URLSession.shared.dataTask(with: urlRequest) { data, _, error in
                defer { dispatchGroup.leave() }
                
                if let error = error {
                    downloadErrors.append("下载\(imageUrlString)失败:\(error.localizedDescription)")
                    return
                }
                
                guard let imageData = data, let image = UIImage(data: imageData) else {
                    downloadErrors.append("\(imageUrlString)数据解析失败")
                    return
                }
                
                downloadedImages.append(image)
            }.resume()
        }
        
        dispatchGroup.notify(queue: .main) {
            switch (downloadedImages.count, downloadErrors.count) {
            case (2, 0):
                completion?(.Success(images: downloadedImages, titles: [apodApiResponse.title, "\(apodApiResponse.title) (高清版)"]))
            case (1, _):
                completion?(.PartialSuccess(mainImage: downloadedImages[0], title: apodApiResponse.title, errorMessage: downloadErrors.joined(separator: " | ")))
            default:
                completion?(.Failure(errorMessage: downloadErrors.joined(separator: " | ")))
            }
        }
    }
}

使用示例

调用时可以这样处理返回结果:

ApodImageProvider.getImageFromApi { response in
    switch response {
    case .Success(let images, let titles):
        print("成功获取\(images.count)张图片:\(titles)")
        // 可将图片展示在SwiftUI视图中,比如HStack里
    case .PartialSuccess(let mainImage, let title, let error):
        print("获取主图成功:\(title),错误信息:\(error)")
    case .Failure(let error):
        print("获取图片失败:\(error)")
    }
}

这样修改后,你的代码就能同时获取APOD的标准分辨率和高分辨率两张图片了~

内容的提问来源于stack exchange,提问作者astrakid932

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 13:52:31