iOS ViewController 加载异步 (HealthKit) 数据的正确架构

Correct architecture of iOS ViewController loading async (HealthKit) data

下面的代码在 90% 的情况下都能正常工作,但有时我会 return 一个错误(即 self.hideAllStackViewsAndShowNoWorkoutsMessage() 将被调用),即使有要加载的训练。我认为这是一个 VC 生命周期计时问题,但我在我的代码中找不到缺陷?

MyViewController {
       override func viewWillAppear(_ animated: Bool) {
            loadAndSetWorkout() 
        }



   func loadAndSetWorkout() {
        WorkoutManager.loadMostRecentWorkout { (workout, error) in

                DispatchQueue.main.async {
                    self.refreshControl?.endRefreshing()

                    if let unwrappedWorkout = workout {
                        self.selectedWorkout = unwrappedWorkout
                    } else {
                        if let unwrappedError = error {
                            self.hideAllStackViewsAndShowNoWorkoutsMessage()
                            print("Error in LastWorkoutTVC loadMostRecentWorkout = \(unwrappedError)")
                        }
                    }
                }
            }

    }

}








                 class func loadMostRecentWorkout(handler: @escaping (HKWorkout?, WorkoutManagerError?) -> Void) {

        let workoutPredicate = HKQuery.predicateForWorkouts(with: .other)
        let sourcePredicate = HKQuery.predicateForObjects(from: HKSource.default()) //limit query to only this app
        let mostRecentPredicate = HKQuery.predicateForSamples(withStart: Date.distantPast, end: Date(), options: .strictStartDate)
        let compound = NSCompoundPredicate(andPredicateWithSubpredicates: [workoutPredicate, sourcePredicate, mostRecentPredicate])

        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)

        let query = HKSampleQuery(sampleType: HKObjectType.workoutType(), predicate: compound, limit: 1, sortDescriptors: [sortDescriptor]) { (query, samples, error) in

            if let unwrappedError = error {
                handler(nil, WorkoutManagerError.generalError(unwrappedError.localizedDescription))
                return //added this return
            }

            guard let samples = samples as? [HKWorkout] else {
                handler(nil, WorkoutManagerError.generalError("no samples in loadMostRecentWorkout"))
                return
            }

            guard let mostRecentWorkout = samples.first else {
                handler(nil, WorkoutManagerError.generalError("no first in samples in loadMostRecentWorkout"))
                return
            }
            handler(mostRecentWorkout, nil)

        }
        HealthStoreSingleton.sharedInstance.healthStore.execute(query)
    }

我把它放在这里只是为了展示另一种设计,因为我无法在评论中添加代码。

完成处理程序相关代码的语义本质上是这样的:

如果至少有一个样本,请准确无误地给出。 否则报错就报错,或者报错没有样本。

您有 "no samples" 错误,所以我们真的不需要 "no first sample" 错误。这是不必要的,所以我们可以摆脱它。

可能的重构可能看起来像这样。

// We either give a sample, or some error.

if let samples = samples as? [HKWorkout], let first = samples.first {
    handler(first, nil)
} else {
    handler(nil, .generalError(error?.localizedDescription ?? "no samples"))
}

同样的语义表达得更清楚

这不是答案,但由于永远不会为您的处理程序提供样本和错误,而当您知道自己的锻炼数据中有样本时您会收到错误,那么罪魁祸首可能是谓词/查询代码?