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
相关产品推荐
相关产品推荐

