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
  • 结合 ObservableObject 实现实时数据更新

结合 ObservableObject 实现实时数据更新

概述

在动态图表开发中,实时数据更新是核心需求之一。SwiftUI 通过 ObservableObject 协议和 @Published 属性包装器提供了响应式数据流机制,使图表能够自动响应数据变化并更新界面。

关键技术

1. ObservableObject 协议

class StockData: ObservableObject {
    @Published var prices: [Double] = []
    @Published var timestamps: [Date] = []
}
  • ObservableObject 协议标记的类可以通知视图其内部数据的变化
  • @Published 属性包装器自动在值改变时触发视图更新

2. 在视图中使用

struct StockChartView: View {
    @ObservedObject var data: StockData
    
    var body: some View {
        Chart {
            ForEach(Array(zip(data.timestamps, data.prices)), id: \.0) { timestamp, price in
                LineMark(
                    x: .value("Time", timestamp),
                    y: .value("Price", price)
                )
            }
        }
    }
}
  • @ObservedObject 属性包装器建立视图与数据模型的绑定关系

实现实时更新的三种模式

1. 定时器驱动更新

class StockData: ObservableObject {
    private var timer: Timer?
    
    func startUpdates() {
        timer = Timer.scheduledTimer(
            withTimeInterval: 1.0,
            repeats: true
        ) { [weak self] _ in
            self?.fetchNewData()
        }
    }
    
    private func fetchNewData() {
        // 模拟获取新数据
        let newPrice = Double.random(in: 100...200)
        prices.append(newPrice)
        timestamps.append(Date())
        
        // 保持数据量合理
        if prices.count > 50 {
            prices.removeFirst()
            timestamps.removeFirst()
        }
    }
}

2. WebSocket 实时数据

class WebSocketData: ObservableObject {
    private var webSocketTask: URLSessionWebSocketTask?
    
    func connect() {
        let url = URL(string: "wss://api.example.com/realtime")!
        webSocketTask = URLSession.shared.webSocketTask(with: url)
        webSocketTask?.resume()
        receiveMessage()
    }
    
    private func receiveMessage() {
        webSocketTask?.receive { [weak self] result in
            switch result {
            case .success(let message):
                self?.processMessage(message)
                self?.receiveMessage() // 继续监听下一条消息
            case .failure(let error):
                print("WebSocket error: \(error)")
            }
        }
    }
}

3. Combine 框架集成

import Combine

class WeatherData: ObservableObject {
    private var cancellables = Set<AnyCancellable>()
    
    func startUpdates() {
        WeatherAPI.fetchUpdates()
            .receive(on: DispatchQueue.main)
            .sink { [weak self] newData in
                self?.updateChart(with: newData)
            }
            .store(in: &cancellables)
    }
}

性能优化技巧

  1. 节流控制:使用 debounce 避免高频更新

    $data
        .debounce(for: .seconds(0.5), scheduler: RunLoop.main)
        .sink { _ in /* 更新视图 */ }
    
  2. 差异更新:仅更新变化的数据点

    func updateData(newPoints: [DataPoint]) {
        let diff = newPoints.difference(from: dataPoints)
        dataPoints.apply(diff)
    }
    
  3. 后台处理:将数据预处理放在后台线程

    DispatchQueue.global(qos: .userInitiated).async {
        let processed = processRawData(rawData)
        DispatchQueue.main.async {
            self.data = processed
        }
    }
    

完整案例:实时心率监测图表

class HeartRateMonitor: ObservableObject {
    @Published var readings: [(time: Date, rate: Int)] = []
    private var timer: Timer?
    
    func startMonitoring() {
        timer = Timer.scheduledTimer(
            withTimeInterval: 1.0,
            repeats: true
        ) { [weak self] _ in
            self?.simulateReading()
        }
    }
    
    private func simulateReading() {
        let newReading = (
            time: Date(),
            rate: Int.random(in: 60...120) // 模拟心率数据
        )
        readings.append(newReading)
        
        // 保持最近100个读数
        if readings.count > 100 {
            readings.removeFirst()
        }
    }
}

struct HeartRateChart: View {
    @ObservedObject var monitor: HeartRateMonitor
    
    var body: some View {
        Chart {
            ForEach(monitor.readings, id: \.time) { reading in
                LineMark(
                    x: .value("Time", reading.time),
                    y: .value("BPM", reading.rate)
                )
                .interpolationMethod(.catmullRom)
            }
        }
        .chartXAxis {
            AxisMarks(values: .stride(by: .minute, count: 5)) { value in
                AxisGridLine()
                AxisTick()
                AxisValueLabel(format: .dateTime.hour().minute())
            }
        }
        .onAppear { monitor.startMonitoring() }
    }
}

常见问题解决

  1. 视图不更新:

    • 确保数据模型类符合 ObservableObject
    • 检查 @Published 属性是否被正确标记
    • 确认在视图中使用 @ObservedObject 而非普通属性
  2. 性能问题:

    • 对于高频数据,考虑使用 Canvas 替代标准 Charts
    • 实现数据采样(downsampling)减少渲染点数
  3. 内存泄漏:

    • 在 ObservableObject 中正确使用 [weak self]
    • 及时取消 Combine 订阅和定时器
Last Updated:: 5/18/25, 10:44 AM