动手练一练,做一个简单的天气预报应用(weather app two)【中】

Published in D-案例练习
August 10, 2022
1 min read
动手练一练,做一个简单的天气预报应用(weather app two)【中】

在本教程系列的第一部分中,我们学习了如何使用 CoreLocation 获取用户的当前位置及其坐标。接下来通过我们获取坐标的信息去请求天气相关的接口,本篇文章将使用免费的 OpenWeather API 来获取天气。

获取天气

现在我们获得了坐标,我们可以使用 OpenWeather API 轻松获取该位置的天气信息。您首先需要去上述网站创建一个免费帐户,然后在我的 API 密钥页面下,获取您的 API 密钥。

OpenWeather_home_page.png
OpenWeather_home_page.png

现在,让我们回到 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 所有,谢绝一切形式的转载

更多精彩内容,请关注「前端达人」

欢迎关注「前端达人」
欢迎关注「前端达人」

SwiftUI学习笔记
© 2025, All Rights Reserved.

公众号:前端达人

京ICP备16033841号-8