2014-12-28 2 views
0

У меня есть ViewController, который вызывает класс HKQueryWeight, который запускает запрос HealthKit (очень, очень медленный btw) и сохраняет данные в CoreData. Если пользователь покидает VC до того, как запрос завершен, приложение выйдет из строя.Индикатор активности останавливается перед завершением асинхронного запроса

fatal error: unexpectedly found nil while unwrapping an Optional value (lldb)

Первоначально я думал, что я мог бы исправить это путем добавления activityIndicator, который начинается анимация в viewDidAppear и останавливается в конце последней функции в ВК. Оно работает. Однако из-за, я считаю, асинхронного характера запросов HealthKit, анимация останавливается до завершения фактического запроса healthKit.

  • Вопрос: Как создать решение, в котором анимация прекращается только после завершения последнего запроса HealthKit?

Я не уверен, если это необходимо предоставить код, но я сделал это в случае полезно

ViewController:

class ViewController: UIViewController { 

    @IBOutlet var activityIndicator: UIActivityIndicatorView! 

    override func viewDidAppear(animated: Bool) { 
     super.viewDidAppear(animated) 

     activityIndicator.startAnimating() 

     setupArrays() 
    } 

func setupArrays(){ 
    println("setting up arrays") 

    if NSUserDefaults.standardUserDefaults().boolForKey("hrSwitch") == true { 
     var hkQueryHeartRate = HKQueryHeartRate() 
     hkQueryHeartRate.performHKQuery() 
    } 


    if NSUserDefaults.standardUserDefaults().boolForKey("weightSwitch") == true { 
     var hkQueryWeight = HKQueryWeight() 
     hkQueryWeight.performHKQuery() 
    } 

    self.activityIndicator.stopAnimating() 
} 

HKQuery

import Foundation 
import CoreData 
import HealthKit 


class HKQueryWeight: HKQueryProtocol { 


    func performHKQuery() { 
     var appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate 
     var context = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext! 

     let healthKitManager = HealthKitManager.sharedInstance 
     let calendar = NSCalendar.currentCalendar() 

     let interval = NSDateComponents() 
     interval.day = 1 

     // Set the anchor date to Monday at 3:00 a.m. 
     let anchorComponents = 
     calendar.components(.CalendarUnitDay | .CalendarUnitMonth | 
      .CalendarUnitYear | .CalendarUnitWeekday, fromDate: NSDate()) 

     let offset = (7 + anchorComponents.weekday - 2) % 7 
     anchorComponents.day -= offset 
     anchorComponents.hour = 3 
     //let now = NSDate() 

     let anchorDate = calendar.dateFromComponents(anchorComponents) 

     let quantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass) 

     // Create the query 
     let query = HKStatisticsCollectionQuery(quantityType: quantityType, 
      quantitySamplePredicate: nil, 
      options: .DiscreteAverage, 
      anchorDate: anchorDate, 
      intervalComponents: interval) 

     // Set the results handler 
     query.initialResultsHandler = { 
      query, results, error in 

      if error != nil { 
       // Perform proper error handling here 
       println("*** An error occurred while calculating the statistics: \(error.localizedDescription) ***") 
       abort() 
      } 

      let endDate = NSDate() 
      let startDate = 
      calendar.dateByAddingUnit(.MonthCalendarUnit, 
       value: -6, toDate: endDate, options: nil) 

      // Plot the weekly step counts over the past 6 months 
      results.enumerateStatisticsFromDate(startDate, toDate: endDate) { 
       statistics, stop in 

       if let quantity = statistics.averageQuantity() { 
        let date = statistics.startDate 
        let weight = quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo)) 

        println("weight date: \(date)") 
        println("weight value: \(weight)") 


        var weightData = NSEntityDescription.insertNewObjectForEntityForName("HKWeight", inManagedObjectContext: context) as HKWeight 

        //Saving to CoreData 
        weightData.setValue(weight, forKey: "weight_data") 
        weightData.setValue(date, forKey: "weight_date") 

        context.save(nil) 

       } 
      } 
     } 

     healthKitManager.healthStore.executeQuery(query) 
    } 
} 

ответ

3

Прямо сейчас, self.activityIndicator.stopAnimating() есть вызываемый сразу после вызова запросов. Поскольку запросы асинхронны, они все равно могут выполняться в фоновом режиме некоторое время после их вызова, поэтому, если вы выведете индикатор активности сразу после вызова запросов, запросы, вероятно, еще не будут завершены. Если вы хотите, чтобы индикатор активности прекратил анимацию после, ваши запросы завершены, вы должны вызвать его, чтобы остановить анимацию с в пределах вашего асинхронного блока запросов.

Поскольку ваши запросы находятся в другом классе, вы можете отправить уведомление, чтобы завершить анимацию индикатора активности в конце каждого запроса, а затем прекратить анимацию UIActivityIndicatorView после завершения второго запроса и получения второго уведомления, например:

var notificationCount:Int = 0 
var totalQueries = 0 

func setupArrays(){ 
    println("setting up arrays") 

    notificationCount = 0 
    totalQueries = 0 
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "removeActivityIndicator", name:"ActivityIndicatorNotification", object: nil) 

    if NSUserDefaults.standardUserDefaults().boolForKey("hrSwitch") == true { 
     totalQueries = totalQueries + 1 
     var hkQueryHeartRate = HKQueryHeartRate() 
     hkQueryHeartRate.performHKQuery() 
    } 
    if NSUserDefaults.standardUserDefaults().boolForKey("weightSwitch") == true { 
     totalQueries = totalQueries + 1 
     var hkQueryWeight = HKQueryWeight() 
     hkQueryWeight.performHKQuery() 
    } 

    if totalQueries == 0 { 
     self.activityIndicator.stopAnimating() 
    } 
} 

func removeActivityIndicator() { 

    notificationCount = notificationCount + 1 

    if notificationCount == totalQueries { 

     dispatch_async(dispatch_get_main_queue()) { 
      self.activityIndicator.stopAnimating() 
      NSNotificationCenter.defaultCenter().removeObserver(self, name:"ActivityIndicatorNotification", object:nil) 
     } 
    } 
} 

Тогда в HKQueryWeight:

func performHKQuery() { 

    // ...All the code before the query... 

    // Set the results handler 
    query.initialResultsHandler = { 
     query, results, error in 

     //...All the code currently within your query... 

     NSNotificationCenter.defaultCenter().postNotificationName("ActivityIndicatorNotification", object: nil) // <-- post notification to stop animating the activity indicator once the query's complete 
    } 
+0

Я получаю сообщение об ошибке: "Ожидаемое выражение после оператора для линии: если (notificationCount == 2) { – KML

+0

Будет ли это решение на самом деле держать счета, сколько NSUserDefaults. Станда rdUserDefaults() оценивается как true? – KML

+0

@karlml # 1, что ошибка должна исчезнуть после моего обновления. Просто получается, что swift не позволяет выполнять отдельную операцию notificationCount ++. –

Смежные вопросы