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

Swift中UITableView无法加载HealthKit运动数据的问题求助

问题描述

想要在UITableView中展示用户运动信息(活动名称、开始日期、时长),但表格始终无法填充数据。已确认dataValues数组有正确数据(点击按钮能打印10条数据),尝试通过按钮调用reloadData()、在异步请求后刷新表格均无效。用静态数组填充表格正常,说明表格配置无问题。

相关代码

import UIKit
import HealthKit

class ViewController: UIViewController, UITableViewDataSource{
    
    /* --------Global variables for queries ------------*/
    let healthStore = HealthData.healthStore
    let workoutType = HKObjectType.workoutType()
    
    
    //Creating the table that shows activities
    @IBOutlet weak var workoutTable: UITableView!
        
        /* ---------- UI element definitions--------------- */
        
        @IBAction func checkData(_ sender: UIButton){
            print(dataValues.count) //this prints 10, which is correct
            print(dataValues) //this prints the correct values.
            DispatchQueue.main.async{
                self.workoutTable.reloadData() //this doesn't work for some reason.
            }
        }
        
        
        /*--------- Variables for the table objects --------*/
        struct workoutStruct {
            var workoutActivity: String
            var workoutDate: Date
            var workoutDuration: Double
        }
        
    var dataValues = [workoutStruct]()
        
   
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        requestAuthorization()
        async{
            let dataValues = await workoutQueryii()
            self.dataValues = dataValues
            DispatchQueue.main.async {
                self.workoutTable.reloadData()
            }
        }
    
    }
        
    //function for setting the number of rows in the UI table
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return dataValues.count
        
    }
    
    //function for mapping the data from dataValues array to the table view.
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        //use the current row as the array value
        let workout = dataValues[indexPath.row]
        
        
        //identify the cell in the table view
        let cell = workoutTable.dequeueReusableCell(withIdentifier: "workoutCell", for: indexPath) as! CustomTableViewCell
        
        cell.activityLabel.text = workout.workoutActivity
        cell.dateLabel.text = funcDateFormatter(date: workout.workoutDate)
        cell.durationLabel.text = workout.workoutDuration.formatted()
        
        return cell
    }
        
        func workoutQueryii() async -> [workoutStruct] {
            let sort = [NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)]
            //this query finds the users workout samples based on the parameters set below.
            let query = HKSampleQuery(
                sampleType: workoutType,
                predicate: nil,
                limit: 10,
                sortDescriptors: sort) { (query, resultsOrNil, error) in
                    self.dataValues = []
                    //process results
                    //create an array for the HKWorkout objects to be passed to in the query
                    var workoutsQ: [HKWorkout] = []
                    
                    if let results = resultsOrNil {
                        for result in results {
                            if let workout = result as? HKWorkout {
                                //here's a workout object
                                workoutsQ.append(workout)
                                var dataValue = workoutStruct(
                                    workoutActivity: workout.workoutActivityType.name,
                                    workoutDate: workout.startDate,
                                    workoutDuration: workout.duration )
                                
                                self.dataValues.append(dataValue)
 
                            }
                            
                        }
                        
                    }
                    
                }
            
            self.healthStore.execute(query)
            print(dataValues.count) //this returns 0
            return dataValues
            
        }
      
//End of class
}
解决方案

核心问题

workoutQueryii函数中,HKSampleQuery是异步执行的,但你在调用healthStore.execute(query)后立刻返回dataValues,此时查询尚未完成,返回的是空数组。后续虽然在查询回调中给self.dataValues赋值,但viewDidLoad里已经把这个空数组重新赋值给了全局的dataValues,导致表格始终拿到空数据。

修复步骤

  1. 正确异步等待查询结果
    使用withCheckedContinuation等待查询完成后再返回数据,避免提前返回空数组:

    func workoutQueryii() async -> [workoutStruct] {
        return await withCheckedContinuation { continuation in
            let sort = [NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)]
            let query = HKSampleQuery(
                sampleType: workoutType,
                predicate: nil,
                limit: 10,
                sortDescriptors: sort) { (query, resultsOrNil, error) in
                    var tempData = [workoutStruct]()
                    guard let results = resultsOrNil else {
                        continuation.resume(returning: tempData)
                        return
                    }
                    for result in results {
                        if let workout = result as? HKWorkout {
                            let dataValue = workoutStruct(
                                workoutActivity: workout.workoutActivityType.name,
                                workoutDate: workout.startDate,
                                workoutDuration: workout.duration )
                            tempData.append(dataValue)
                        }
                    }
                    continuation.resume(returning: tempData)
                }
            self.healthStore.execute(query)
        }
    }
    
  2. 移除冗余的全局数组操作
    原代码在查询回调中修改self.dataValues属于多余操作,直接通过continuation返回处理好的临时数组即可,避免数组被多次修改导致的逻辑混乱。

  3. 确认表格数据源代理
    在viewDidLoad中添加数据源绑定,确保表格能正确获取数据:

    override func viewDidLoad() {
        super.viewDidLoad()
        workoutTable.dataSource = self // 新增这行
        requestAuthorization()
        async{
            let dataValues = await workoutQueryii()
            self.dataValues = dataValues
            DispatchQueue.main.async {
                self.workoutTable.reloadData()
            }
        }
    }
    

效果验证

修改后,viewDidLoad中的异步调用会拿到查询完成后的有效数据,赋值给dataValues后刷新表格就能正常显示内容;点击checkData按钮时,reloadData也能正常刷新表格。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 20:42:03