在本教程系列的第一部分中,我们学习了如何使用 CoreLocation 获取用户的当前位置及其坐标。接下来通过我们获取坐标的信息去请求天气相关的接口,本篇文章将使用免费的 OpenWeather API 来获取天气。
现在我们获得了坐标,我们可以使用 OpenWeather API 轻松获取该位置的天气信息。您首先需要去上述网站创建一个免费帐户,然后在我的 API 密钥页面下,获取您的 API 密钥。
现在,让我们回到 Xcode。在 Managers 文件夹下,我们创建一个新的 Swift 文件并将其命名为 WeatherManager.swift 。在新文件中引入以下内容。
// ./Managers/WeatherManager.swift import Foundation import CoreLocation
接下来我们创建 WeatherManager 类
// ./Managers/WeatherManager.swift class WeatherManager { }
在这个类中只定义一个函数 getCurrentWeather 。它使用 WWDC21 大会中介绍的新异步接口请求方法,示例代码如下。
func getCurrentWeather(latitude: CLLocationDegrees, longitude: CLLocationDegrees) async throws -> ResponseBody { guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(latitude)&lon=\(longitude)&appid=YOUR_API_KEY&units=metric") else { fatalError("Missing URL") } let urlRequest = URLRequest(url: url) let (data, response) = try await URLSession.shared.data(for: urlRequest) guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching data") } let decodedData = try JSONDecoder().decode(ResponseBody.self, from: data) return decodedData }
在函数的第一行,您会看到一个 API 的请求URL。这是我们将要调用的API。我们正在向 API 传参纬度和经度,以及 API 密钥。使用 API 密钥记得替换成自己申请的 API KEY。最后,我们返回decodedData。在同一个 WeatherManager 文件中,在类的正下方添加一个 ResponseBody 模型。您可以从头开始,也可以简单地复制下面的内容。最后借助 JSONDecoder 将尝试将数据解码成对应的内容。
// ./Managers/WeatherManager.swift struct ResponseBody: Decodable { var coord: CoordinatesResponse var weather: [WeatherResponse] var main: MainResponse var name: String var wind: WindResponse struct CoordinatesResponse: Decodable { var lon: Double var lat: Double } struct WeatherResponse: Decodable { var id: Double var main: String var description: String var icon: String } struct MainResponse: Decodable { var temp: Double var feels_like: Double var temp_min: Double var temp_max: Double var pressure: Double var humidity: Double } struct WindResponse: Decodable { var speed: Double var deg: Double } } extension ResponseBody.MainResponse { var feelsLike: Double { return feels_like } var tempMin: Double { return temp_min } var tempMax: Double { return temp_max } }
在 ContentView 文件中,我们将在顶部创建一个 WeatherManager 的新实例。
// ./Views/ContentView.swift var weatherManager = WeatherManager()
添加一个 weather 的状态变量
// ./Views/ContentView.swift @State var weather: ResponseBody?
在 if let location = locationManager.location 语句中,我们将用 if else 语句替换文本。在 if 中,我们现在将显示一个 Text(接口请求成功后,调用显示)。在 else 中,由于是异步调用,接口请求过程中,我们添加一个 ProgressView 加载状态 ,当它出现时,我们将调用任务修饰符(因为我们使用的是 async await )。在任务修饰符内部,我们从 weatherManager 调用 getCurrentWeather 方法,并将结果保存在天气状态中。
// ./Views/ContentView.swift if let weather = weather { Text("Weather data fetched!") } else { ProgressView() .task { do { weather = try await weatherManager.getCurrentWeather(latitude: location.latitude, longitude: location.longitude) } catch { print("Error getting weather: \(error)") } } }
现在,将手机连接到 Mac,在手机上构建应用程序,然后尝试测试应用程序。一旦你分享了你的位置,你应该会在屏幕上看到文本天气数据,这意味着我们确实得到了天气数据。很好!如果不是这种情况,请向上滚动以查看您是否错过了一个步骤或忘记添加一些代码。
接下来为了开发方便,我们需要准备一些预览数据,避免在真机上进行测试。我们来创建一个新的视图文件,天气相关的数据将会被展现出来。我们在 Views 目录下创建 WeatherView.swift 文件,定义个继承 ResponseBody
变量的 weather 变量。
**var weather: ResponseBody**
接下来我们在Preview Content
文件夹下创建一个 weatherData.json
的文件,此文件的内容可以通过 https://api.openweathermap.org/data/2.5/weather?q=New York&appid=YOURAPIKEY&units=metric 这个地址请求获取JSON文件的内容,记得替换你的API参数和城市名称。将返回的内容复制到 JSON 文件中,示例JSON内容如下
{ "coord": { "lon": -74.006, "lat": 40.7143 }, "weather": [ { "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04d" } ], "base": "stations", "main": { "temp": 3.42, "feels_like": 0.51, "temp_min": 1.36, "temp_max": 5.28, "pressure": 1015, "humidity": 73 }, "visibility": 10000, "wind": { "speed": 3.13, "deg": 274, "gust": 4.92 }, "clouds": { "all": 90 }, "dt": 1638298034, "sys": { "type": 2, "id": 2039034, "country": "US", "sunrise": 1638273608, "sunset": 1638307792 }, "timezone": -18000, "id": 5128581, "name": "New York", "cod": 200 }
然后在Preview Content
目录下新建个 ModelData.swift
的文件,解析 weatherData.json
中的数据,示例代码如下:
// // ModelData.swift // WeatherApp // // Created by 林森 on 2022/7/3. // import Foundation var previewWeather: ResponseBody = load("weatherData.json") func load<T: Decodable>(_ filename: String) -> T { let data: Data guard let file = Bundle.main.url(forResource: filename, withExtension: nil) else { fatalError("Couldn't find \(filename) in main bundle.") } do { data = try Data(contentsOf: file) } catch { fatalError("Couldn't load \(filename) from main bundle:\n\(error)") } do { let decoder = JSONDecoder() return try decoder.decode(T.self, from: data) } catch { fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)") } }
回到 WeatherView ,将 previewWeather 变量传递给预览。
// ./Views/WeatherView.swift struct WeatherView_Previews: PreviewProvider { static var previews: some View { WeatherView(weather: previewWeather) } }
由于我们从 OpenWeather API 获取的数据将返回双精度数,因此我们需要将双精度数四舍五入舍去小数部分,保留整数部分。接下来让我们创建一个新的 Extensions.swift 文件,我们将在其中存储一些工具函数的扩展。如下代码所示我们添加 Double extension
扩展,将所有双精度数四舍五入保留整数部分。
// ./Extensions.swift extension Double { func roundDouble() -> String { return String(format: "%.0f", self) } }
非常棒,我们现在获得了天气数据,并为 WeatherView 试图准备了天气预览的数据和字符串处理扩展。在下篇文章,我们继续完善 WeatherView 的UI部分。
注:本文属于原创文章,版权属于「前端达人」公众号及 SwiftUI.cc 所有,谢绝一切形式的转载
更多精彩内容,请关注「前端达人」