Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
  • 错误处理与加载状态

错误处理与加载状态

在图表应用中,数据通常来自外部数据源(如API、数据库或本地文件)。网络请求可能失败,数据可能不完整或格式错误,因此良好的错误处理和加载状态管理至关重要。本章将介绍如何在SwiftUI图表中优雅地处理这些情况。

1. 加载状态管理

基本加载状态枚举

enum LoadingState<Value> {
    case idle       // 初始状态
    case loading    // 加载中
    case success(Value) // 加载成功
    case failure(Error) // 加载失败
}

在视图中使用加载状态

struct WeatherChartView: View {
    @StateObject var viewModel = WeatherViewModel()
    
    var body: some View {
        Group {
            switch viewModel.loadingState {
            case .idle:
                Color.clear.onAppear { viewModel.fetchData() }
            case .loading:
                ProgressView("加载天气数据...")
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
            case .success(let data):
                Chart { /* 渲染图表 */ }
            case .failure(let error):
                ErrorView(error: error, retryAction: viewModel.fetchData)
            }
        }
    }
}

2. 错误处理策略

常见错误类型

enum DataError: Error, LocalizedError {
    case invalidURL
    case networkError(Error)
    case decodingError(Error)
    case emptyData
    
    var errorDescription: String? {
        switch self {
        case .invalidURL: return "无效的URL"
        case .networkError(let error): return "网络错误: \(error.localizedDescription)"
        case .decodingError(let error): return "数据解析错误: \(error.localizedDescription)"
        case .emptyData: return "没有可用数据"
        }
    }
}

错误视图组件

struct ErrorView: View {
    let error: Error
    let retryAction: () -> Void
    
    var body: some View {
        VStack {
            Image(systemName: "exclamationmark.triangle")
                .font(.largeTitle)
                .foregroundColor(.orange)
            Text("加载失败")
                .font(.title)
            Text(error.localizedDescription)
                .multilineTextAlignment(.center)
                .padding()
            
            Button("重试", action: retryAction)
                .buttonStyle(.borderedProminent)
        }
        .padding()
    }
}

3. 数据预处理与回退方案

处理不完整数据

extension WeatherData {
    func sanitized() -> WeatherData {
        // 过滤掉无效数据点
        let validPoints = dataPoints.filter { $0.temperature != -999 }
        return WeatherData(points: validPoints.isEmpty ? [.default] : validPoints)
    }
}

图表中的占位数据

Chart {
    if data.points.isEmpty {
        RuleMark(y: .value("平均值", 0))
            .annotation {
                Text("无可用数据")
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
    } else {
        ForEach(data.points) { point in
            LineMark(x: ..., y: ...)
        }
    }
}

4. 最佳实践

  1. 渐进式加载:对于大数据集,考虑分页加载或增量渲染
  2. 本地缓存:实现本地缓存机制,在网络不可用时显示上次成功加载的数据
  3. 超时处理:为网络请求设置合理的超时时间(建议15-30秒)
  4. 错误日志:记录错误信息以便后续分析
  5. 用户反馈:重要的错误(如权限问题)应该明确提示用户解决

案例实现

class StockViewModel: ObservableObject {
    @Published var loadingState: LoadingState<[StockData]> = .idle
    
    @MainActor
    func fetchData() async {
        loadingState = .loading
        do {
            let data = try await StockAPI.fetchLatest()
            loadingState = .success(data.sanitized())
        } catch {
            loadingState = .failure(DataError.networkError(error))
        }
    }
}

struct StockChartView: View {
    @StateObject var viewModel = StockViewModel()
    @State var showErrorDetails = false
    
    var body: some View {
        VStack {
            switch viewModel.loadingState {
            case .success(let data):
                StockChart(data: data)
                    .frame(height: 300)
            case .failure(let error):
                VStack {
                    ErrorView(error: error) {
                        Task { await viewModel.fetchData() }
                    }
                    if showErrorDetails {
                        Text(error.localizedDescription)
                            .font(.caption)
                    }
                }
            default:
                ProgressView()
            }
        }
        .task { await viewModel.fetchData() }
        .toolbar {
            if case .failure = viewModel.loadingState {
                Button {
                    showErrorDetails.toggle()
                } label: {
                    Image(systemName: "info.circle")
                }
            }
        }
    }
}

通过以上方法,您可以创建健壮的图表应用,在各种数据状态下都能提供良好的用户体验。

Last Updated:: 5/18/25, 10:44 AM