如何用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
相关产品推荐
相关产品推荐

