用Swift实现基本网络通讯 – PART 1
BASIC NETWORKING IN SWIFT – PART 1
最近打算做一个Swift和SwiftUI的系列教学。用一些简单的例子和实践来完成App开发中常见的问题和解决。这个系列适合接触iOS开发的初学者。虽然Objective- C还没有完全“死硬”,但Swift早已成为iOS和macOS开发的主流选择,在加上SwiftUI这几年的突飞猛进,对于开发者而言,特别是刚刚入门的iOS开发者,这个组合应该不会错。本篇我们来看看Swift最基本的网络请求,例如请求某个远端API的实现。
作为测试对象,我选择Open Weather Map最为测试API提供商。注册过程很简单,选择免费的天气API,新建一个应用,并获取API Key即可。
OpenWeatherMap是一个广泛使用的天气数据提供商,它提供了一套全球范围内的天气API服务。下面是关于OpenWeatherMap的一些信息:
数据覆盖范围:OpenWeatherMap提供全球范围内的天气数据,包括当前天气情况、逐小时预报和未来几天的天气预报。他们的数据覆盖了几乎所有的地理位置,无论是城市、乡村还是偏远地区。
天气信息:OpenWeatherMap提供了丰富的天气信息,包括温度、湿度、气压、风速、降水量、天气状况描述等。此外,他们还提供了天文数据,如日出日落时间、月相等。
API功能:OpenWeatherMap的API具有广泛的功能,包括获取实时天气数据、逐小时和逐日预报、天气地图、历史天气数据等。你可以根据自己的需求选择合适的API来获取所需的天气信息。
文档和开发者支持:OpenWeatherMap提供了详细的API文档和示例代码,帮助开发者快速接入和使用他们的服务。他们还提供了开发者支持,可以通过论坛、电子邮件或社交媒体与他们的团队联系,以解决任何问题或疑问。
总体而言,OpenWeatherMap被广泛认可为可靠的天气数据提供商,并且其API功能丰富,适合各种天气相关的应用和项目。然而,正如使用任何第三方API一样,你应该仔细阅读他们的使用条款和价格政策,并确保你的使用符合他们的规定。
在开始Xcode中的开发工作之前,我们现在测试一下OpenWeatherMap的API调用。这里我强烈推荐一款API开发和测试工具:RapidAPI。
从OpenWeatherMap的文档中我们可以找到‘当前天气’的API请求规范如下:
https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={APIkey}
详细的请求参数和格式可以在文档中查询,这里我们只需要考虑API密钥和位置经纬度。exclude可以排出过滤掉一些返回数据内容,这个功能是OpenWeatherMap API 3.0中新增的,让我们的数据更加简洁,效率也更高。
下面我们用RapidAPI来测试API请求:
右侧返回的JSON结果已经格式化,一目了然。从返回JSON可以读出如天气、温度、湿度、风速风向以及城市名称和日出日落时间等等信息。其实提取以下一部分字段就足以构建一个具备基本功能的天气预报App了。
{
"coord": {
"lon": 136.9064,
"lat": 35.1815
},
"weather": [
{
"id": 804,
"main": "Clouds",
"description": "overcast clouds",
"icon": "04n"
}
],
"base": "stations",
"main": {
"temp": 293.48,
"feels_like": 293.81,
"temp_min": 292.32,
"temp_max": 294.04,
"pressure": 1007,
"humidity": 86
},
"visibility": 10000,
"wind": {
"speed": 0.45,
"deg": 87,
"gust": 1.34
},
"clouds": {
"all": 100
},
"dt": 1683387127,
"sys": {
"type": 2,
"id": 2001167,
"country": "JP",
"sunrise": 1683402960,
"sunset": 1683452514
},
"timezone": 32400,
"id": 1856057,
"name": "Nagoya",
"cod": 200
}
接下来我们新建一个Xcode项目,名为SwiftNetworking,注意使用Swift作为开发语言。因本章不太涉及SwiftUI内容,所以使用什么方式构建UI都可以。此例选择了SwiftUI。
首先我在SwiftNetworkingApp.swift中建立了一个字符串变量API_KEY,用以储存API密钥。只是简单测试的话也可以直接写到URL字符串中。
// SwiftNetworkingApp.swift
// SwiftNetworking
import SwiftUI
var API_KEY = "***************************"
@main
struct SwiftNetworkingApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
虽然我们直接可以在ContentView.swift也就是SwiftUI文件中编写调用函数,单位了代码的整洁和可读性,我还是建议把数据逻辑层和用户界面层分开。所以这里新建一个WeatherEngine.swift来存放API请求的函数。并且在App运行时调用请求函数。
// WeatherEngine.swift
// SwiftNetworking
import Foundation
func fetchWeatherData() {
print("API call runs here")
}
// ContentView.swift
// SwiftNetworking
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.onAppear(){
fetchWeatherData()
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
下面我们完成整个完整的fetchWeatherData()方法:
func fetchWeatherData() {
print("-----API call runs here-----")
//API请求地址
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=35.18147&lon=136.90641&appid=\(API_KEY)") else {
print("Invalid URL")
return
}
//建立URLRequest对象,请求方式为GET
var request = URLRequest(url: url)
request.httpMethod = "GET"
//建立URLSession dataTask来请求API并获取返回结果
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
//异常返回
if let error = error {
print("Error: \(error.localizedDescription)")
return
}
//Http响应异常
guard let httpResponse = response as? HTTPURLResponse else {
print("Invalid response")
return
}
//获得HTTP 200(OK)响应,处理返回数据
if httpResponse.statusCode == 200 {
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let weatherData = json as? [String: Any] {
//处理返回的data数据
print("Weather data: \(weatherData)")
}
} catch {
print("Error parsing JSON: \(error.localizedDescription)")
}
}
} else {
print("API request error with code: \(httpResponse.statusCode)")
}
}
//执行DataTask
task.resume()
}
在这个函数里,我们使用 JSONSerialization 对响应数据进行解析,将其转换为字典([String: Any])。然后,可以根据需要来处理天气数据。这里我们只是简单的把返回数据log出来。这里我们在每一步都进行了异常处理,虽然只是log出error.localizedDescription字符串,但在程序开发过程中这是非常重要的,当结构和逻辑越发复杂的时候,这个习惯可以让你更容易找出异常原因。
至此,当你运行程序的时候,Debug窗口便会打印出通过网络请求API并返回的结果。
最后我们在界面中添加一个按钮,点击按钮以后再调用fetchWeatherData()函数。以下是修改后的ContentView.seift
// ContentView.swift
// SwiftNetworking
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Button(action: {
fetchWeatherData()
}) {
Text("Fetch Weather Data")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
// .onAppear(){
// fetchWeatherData()
// }
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
至此我们已经完成了基本的网络API请求,并返回了数据。下一章我们会详细处理返回的数据,提取、转换及格式化需要的字段,并在UI中显示结果。
感谢阅读。Happy coding!