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

如何用Swift实现单函数处理多城市天气API查询请求?

解决方案

方法一:使用OpenWeatherMap批量查询接口(推荐)

OpenWeatherMap提供了/group接口,支持一次性查询最多20个城市的天气数据,能大幅减少网络请求次数,提升效率。

1. 更新常量与APICaller逻辑

先修改常量定义,添加批量查询的基础URL:

struct Constants {
    static let API_KEY = "<api-key>"
    static let singleCityBaseURL = "https://api.openweathermap.org/data/2.5/weather?appid=\(API_KEY)&units=metric&q="
    // 批量查询接口,多个城市用逗号分隔
    static let multiCityBaseURL = "https://api.openweathermap.org/data/2.5/group?appid=\(API_KEY)&units=metric&q="
}

然后扩展APICaller,新增批量获取数据的方法:

class APICaller {
    static let shared = APICaller()
    
    // 保留单个城市查询方法,方便单独调用
    func getSingleCityWeather(cityName: String, completion: @escaping(Result<WeatherDataModel, Error>) -> Void) {
        // 处理城市名的URL编码,避免空格、特殊字符导致URL无效
        let encodedCity = cityName.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? cityName
        guard let url = URL(string: "\(Constants.singleCityBaseURL)\(encodedCity)") else {
            completion(.failure(NSError(domain: "InvalidURL", code: -1, userInfo: [NSLocalizedDescriptionKey: "城市名无法生成有效URL"])))
            return
        }
        
        let task = URLSession.shared.dataTask(with: url) { data, _, error in
            guard let data = data, error == nil else {
                completion(.failure(error ?? NSError(domain: "NetworkError", code: -2, userInfo: [NSLocalizedDescriptionKey: "无数据返回"])))
                return
            }
            do {
                let result = try JSONDecoder().decode(WeatherDataModel.self, from: data)
                completion(.success(result))
            } catch {
                completion(.failure(error))
            }
        }
        task.resume()
    }
    
    // 批量查询多个城市天气
    func getMultiCityWeather(cities: [String], completion: @escaping(Result<[WeatherDataModel], Error>) -> Void) {
        // 对每个城市名做URL编码,再拼接成逗号分隔的字符串
        let encodedCities = cities.map { $0.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? $0 }
                                  .joined(separator: ",")
        
        guard let url = URL(string: "\(Constants.multiCityBaseURL)\(encodedCities)") else {
            completion(.failure(NSError(domain: "InvalidURL", code: -1, userInfo: [NSLocalizedDescriptionKey: "城市列表无法生成有效URL"])))
            return
        }
        
        let task = URLSession.shared.dataTask(with: url) { data, _, error in
            guard let data = data, error == nil else {
                completion(.failure(error ?? NSError(domain: "NetworkError", code: -2, userInfo: [NSLocalizedDescriptionKey: "无数据返回"])))
                return
            }
            do {
                // 批量接口返回结构和单个接口不同,需新增对应模型
                let response = try JSONDecoder().decode(MultiCityResponse.self, from: data)
                completion(.success(response.list))
            } catch {
                completion(.failure(error))
            }
        }
        task.resume()
    }
}

// 批量接口对应的返回模型
struct MultiCityResponse: Codable {
    let list: [WeatherDataModel]
}

2. 调用方式

在需要加载数据的地方,传入20个城市的数组即可:

// 示例城市列表,替换成你需要的20个城市
let targetCities = ["London", "Paris", "Beijing", "NewYork", ...]
APICaller.shared.getMultiCityWeather(cities: targetCities) { result in
    switch result {
    case .success(let weatherList):
        // 回到主线程更新UI
        DispatchQueue.main.async {
            self.weatherData = weatherList
            self.tableView.reloadData()
        }
    case .failure(let error):
        print("数据加载失败:\(error.localizedDescription)")
    }
}

方法二:并发调用单个城市接口(备选)

如果无法使用批量接口,可以用DispatchGroup管理多个并发请求,收集所有结果后统一返回:

修改APICaller的批量方法

class APICaller {
    static let shared = APICaller()
    
    // 并发批量获取多个城市天气
    func getMultiCityWeatherConcurrent(cities: [String], completion: @escaping(Result<[WeatherDataModel], Error>) -> Void) {
        var weatherDataList = [WeatherDataModel]()
        let group = DispatchGroup()
        var errors = [Error]()
        
        for city in cities {
            group.enter()
            getSingleCityWeather(cityName: city) { result in
                defer { group.leave() }
                switch result {
                case .success(let data):
                    weatherDataList.append(data)
                case .failure(let error):
                    errors.append(error)
                }
            }
        }
        
        group.notify(queue: .main) {
            if !errors.isEmpty {
                // 可根据需求调整:返回第一个错误,或返回成功的部分数据+错误信息
                completion(.failure(errors.first!))
            } else {
                completion(.success(weatherDataList))
            }
        }
    }
    
    // 原有的getSingleCityWeather方法保持不变
    func getSingleCityWeather(cityName: String, completion: @escaping(Result<WeatherDataModel, Error>) -> Void) {
        // 代码同方法一的单个城市查询逻辑
    }
}

注意事项

  • 必须处理URL编码:城市名含空格、特殊字符时,直接拼接会导致URL无效,一定要用addingPercentEncoding处理。
  • 模型匹配:批量接口的返回结构和单个接口不同,需确保MultiCityResponse和接口返回的JSON字段完全对应。
  • 错误处理:并发请求时可能出现部分成功部分失败的情况,可根据业务需求调整逻辑(比如返回成功的部分数据)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 23:01:25