You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中Bar Chart无法获取Firebase异步返回数据绘制柱状图

问题根因

核心问题是对Firebase异步请求的执行逻辑理解错误:

  • getDocuments是异步网络请求:调用后代码不会停在此处等待数据库返回结果,会立刻往下执行return thisDataPoint,这时候闭包里给randomValue、thisDataPoint的赋值逻辑还没执行,返回的是未赋值的无效值,柱状图自然拿不到正确数据。
  • 把固定值赋值写在闭包外时,赋值是同步执行的,map走到return的时候值已经准备完成,所以能正常显示。
  • 现有代码还存在几个严重逻辑缺陷:
    • 循环count次就会发起count次完全相同的数据库查询,属于多余的网络开销,还会因为共享counter/randomValue/thisDataPoint这几个外部变量产生竞态问题,值会被多次返回的请求反复覆盖。
    • 错误判断位置完全错误:把error != nil的判断写在了遍历snapshot!.documents的循环里,一旦真的请求出错,snapshot为nil,强制解包snapshot!.documents会直接触发崩溃,根本走不到错误提示分支。
    • 用counter == snapshot!.count判断最后一条数据的逻辑完全不可靠,异步请求的返回时机不受代码书写顺序控制,这个计数根本匹配不上要生成的数据点顺序。
修复方案

不要写同步返回[BarChart.DataPoint]的函数,网络请求不可能立刻拿到返回值,改成异步回调的写法:只发起1次查询,等数据全部返回后再组装成图表需要的结构体,回调给UI层刷新。
修复后的代码如下:

public func fetchSleepDataPoints(completion: @escaping ([BarChart.DataPoint]) -> Void) {
    // 仅发起1次数据库查询,无需循环调用
    fireStoreDatabase.collection("day")
        .whereField("email", isEqualTo: Auth.auth().currentUser!.email!)
        .order(by: "today")
        .limit(to: 7)
        .getDocuments { [weak self] snapshot, error in
            // 优先处理错误,禁止先强制解包snapshot
            if let error = error {
                self?.makeAlert(title: "Error", message: error.localizedDescription)
                print("查询睡眠数据失败: \(error)")
                completion([])
                return
            }
            guard let documents = snapshot?.documents else {
                completion([])
                return
            }
            // 遍历返回的文档,直接组装数据点
            let dataPoints: [BarChart.DataPoint] = documents.map { document in
                // 做类型兜底,避免强转失败崩溃
                let sleepVal = document.get("sleep") as? CGFloat ?? 0
                return BarChart.DataPoint(
                    value: sleepVal,
                    complexValues: [(sleepVal / 4, .red)]
                )
            }
            // 回到主线程回调,刷新UI
            DispatchQueue.main.async {
                completion(dataPoints)
            }
        }
}
调用方式

替换原来直接拿同步返回值的写法,用回调接收数据后刷新图表:

fetchSleepDataPoints { [weak self] points in
    // 把拿到的points赋值给柱状图数据源,刷新视图
    self?.barChart.dataPoints = points
    self?.barChart.setNeedsDisplay()
}

内容的提问来源于stack exchange,提问作者user1737983

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 12:24:59