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

Swift中Firebase数据传至TableViewController时数组为空问题排查

Firebase异步数据获取空数组问题解决

问题根源

Firebase Realtime Database的数据获取是异步操作,你当前的fetchDataFromFirebase函数是同步返回数组,函数会在Firebase请求完成前就返回初始化的空数组,所以拿到的结果始终是空的。另外,闭包的返回类型是Void,不能在闭包内返回[NewWorkout]类型的值,这就是你遇到类型转换错误的原因。

修改后的FirebaseHelper代码

将同步返回改为异步回调方式,用completion handler传递数据:

import FirebaseAuth
import FirebaseDatabase

class FirebaseHelper {
    static let shared = FirebaseHelper()
    private let database = Database.database(url: "").reference()
    
    private init() {}
    
    func getUserID() -> String {
        return Auth.auth().currentUser!.uid
    }
    
    func saveDataInFirebase(userName: String, setValue: [String:Any]) {
        database.child("\(getUserID())").child("\(userName)").setValue(setValue)
        print("Saved Information")
    }
    
    // 改为带completion的异步方法
    func fetchDataFromFirebase(completion: @escaping ([NewWorkout]) -> Void) {
        var workoutList = [NewWorkout]()
        
        database.child("\(getUserID())").observeSingleEvent(of: .value, with: { snapshot in
            guard let value = snapshot.value as? [String:Any] else {
                completion(workoutList)
                return
            }
            
            for (_, infoValue) in value {
                guard let data = infoValue as? NSDictionary else { continue }
                
                guard let userName = data["userName"] as? String else { continue }
                guard let trainingStartDate = data["trainingStartDate"] as? String else { continue }
                guard let trainingTime = data["trainingTime"] as? String else { continue }
                guard let workoutDuration = data["workoutDuration"] as? String else { continue }
                guard let selectedColor = data["selectedColor"] as? [String:Double] else { continue }
                guard let workoutDates = data["workoutDates"] as? [String] else { continue }
                guard let weekDays = data["weekDays"] as? [String:Bool] else { continue }
                
                let userColor = UserColor(red: selectedColor["red"] ?? 0.0,
                                         green: selectedColor["green"] ?? 0.0,
                                         blue: selectedColor["blue"] ?? 0.0)
                let workout = NewWorkout(userName: userName,
                                        weekDays: weekDays,
                                        trainingStartDate: trainingStartDate,
                                        workoutDuration: workoutDuration,
                                        wortoutTime: trainingTime,
                                        userColor: [userColor],
                                        workoutDates: workoutDates)
                workoutList.append(workout)
            }
            // 数据解析完成后通过completion返回
            completion(workoutList)
        })
    }
}

修改后的ScheduleTableViewController代码

在回调中更新数据并刷新表格:

import UIKit

class ScheduleTableViewController: UITableViewController {
    
    private var workoutList = [NewWorkout]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 调用异步方法,在completion中更新数据
        FirebaseHelper.shared.fetchDataFromFirebase { [weak self] workouts in
            guard let self = self else { return }
            self.workoutList = workouts
            print(workouts) // 现在能打印出正确的数据
            // 主线程刷新表格
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    }
    
    // MARK: - Table view data source
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return workoutList.count
    }
    
    override func tableView(_ tableView: UITableView,
                            cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ScheduleTableViewCell
        let workout = workoutList[indexPath.row]
        
        cell.timeLabel.text = "\(workout.wortoutTime) (\(workout.workoutDuration))"
        cell.userNameLable.text = workout.userName
        // 可以用解析后的userColor设置背景色
        let color = workout.userColor.first
        cell.bgView.backgroundColor = UIColor(red: color?.red ?? 0.91,
                                              green: color?.green ?? 0.6,
                                              blue: color?.blue ?? 0.4,
                                              alpha: 0.28)
        cell.bgLineView.backgroundColor = UIColor(red: color?.red ?? 0.91,
                                                 green: color?.green ?? 0.6,
                                                 blue: color?.blue ?? 0.4,
                                                 alpha: 1.0)
        
        return cell
    }
}

额外优化点

  1. 原循环中value["\(info.key)"]替换为info.value,更高效简洁。
  2. 解析selectedColor时直接指定[String:Double]类型,避免后续强制转换Double。
  3. 更新UI操作必须放在主线程,用DispatchQueue.main.async包裹表格刷新。
  4. 使用[weak self]避免循环引用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 20:54:54