Правильная архитектура iOS ViewController загружает данные asyn c (HealthKit) - PullRequest
0 голосов
/ 26 февраля 2020

Приведенный ниже код работает в 90% случаев, но время от времени я возвращаю сообщение об ошибке (т. Е. Вызывается self.hideAllStackViewsAndShowNoWorkoutsMessage ()) даже при наличии загружаемых тренировок. Я думаю, что это проблема синхронизации жизненного цикла V C, но я не могу найти изъян в своем коде?

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)
    }

1 Ответ

1 голос
/ 27 февраля 2020

Я просто помещаю это здесь, чтобы показать другой дизайн, видя, что я не могу поместить код в комментарий.

Семантика вашего кода, связанного с обработчиком завершения, по сути такова:

Если есть хотя бы один образец, дайте его без ошибок. В противном случае выдайте ошибку, если мы получили ошибку, или укажите, что выборок не было.

У вас ошибка «без выборок», поэтому нам не нужна ошибка «нет первой выборки» , Это не нужно, поэтому мы можем избавиться от этого.

Возможный рефакторинг может выглядеть примерно так.

// 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"))
}

Это та же семантика, более четко выраженная.

Это это не ответ, но так как вашему обработчику никогда не будут даны пример и ошибка, и вы получаете ошибку, когда знаете, что у вас есть выборки в ваших собственных данных тренировки, то, возможно, виновником является код предиката / запроса?

...