第 12 章:图表与数据源整合
从 API 获取数据(URLSession、async/await)
概述
在 SwiftUI 图表开发中,动态数据通常来自远程 API。本章将介绍如何使用 Swift 的 URLSession 和 async/await 语法从网络获取数据,并将其绑定到图表。
核心步骤
1. 定义数据模型
struct ChartData: Codable, Identifiable {
let id: UUID
let date: Date
let value: Double
}
2. 创建异步数据加载器
class DataLoader: ObservableObject {
@Published var dataPoints: [ChartData] = []
@Published var isLoading = false
@Published var error: Error?
func loadData(from url: URL) async {
isLoading = true
do {
let (data, _) = try await URLSession.shared.data(from: url)
let decodedData = try JSONDecoder().decode([ChartData].self, from: data)
DispatchQueue.main.async {
self.dataPoints = decodedData
self.isLoading = false
}
} catch {
DispatchQueue.main.async {
self.error = error
self.isLoading = false
}
}
}
}
3. 在 SwiftUI 视图中集成
struct ChartView: View {
@StateObject private var loader = DataLoader()
let apiURL = URL(string: "https://api.example.com/data")!
var body: some View {
VStack {
if loader.isLoading {
ProgressView()
} else if let error = loader.error {
ErrorView(error: error)
} else {
Chart(loader.dataPoints) { item in
LineMark(
x: .value("Date", item.date),
y: .value("Value", item.value)
)
}
}
}
.task {
await loader.loadData(from: apiURL)
}
}
}
关键技术与注意事项
async/await 最佳实践:
- 使用
Task包装异步调用 - 在主线程更新 UI(通过
DispatchQueue.main) - 正确处理取消操作
- 使用
错误处理:
- 网络请求错误
- JSON 解析错误
- 空数据状态
性能优化:
- 实现数据缓存(URLCache)
- 节流频繁请求
- 后台线程处理复杂数据
测试技巧:
// 测试用例示例 func testDataLoading() async throws { let testData = [ChartData(id: UUID(), date: Date(), value: 42)] let mockLoader = MockDataLoader(response: testData) let viewModel = ChartViewModel(loader: mockLoader) await viewModel.loadData() XCTAssertEqual(viewModel.dataPoints.count, 1) }
完整示例:天气数据 API 集成
// 扩展数据模型
struct WeatherData: Codable {
let temperature: Double
let humidity: Double
let timestamp: Date
}
// 扩展数据加载器
extension DataLoader {
func loadWeatherData() async {
let weatherURL = URL(string: "https://weatherapi.com/forecast")!
await loadData(from: weatherURL)
}
}
常见问题解决方案
- 证书问题:在 Info.plist 中添加
NSAppTransportSecurity配置 - 日期格式:自定义 JSONDecoder 的日期解码策略
let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 - 大数据集:实现分页加载或数据采样
提示:对于生产环境应用,建议添加请求重试机制和网络状态监测功能。
