Search code examples
iosswiftxcodemultithreadingbackground-process

Execute a function after completing all background process in swift


I am using GCD in swift like this :

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {

    //all background task

    dispatch_async(dispatch_get_main_queue()) {  
        self.second()
    }
}

In this code second function is getting called before completing all background task that's why I am not able to take some data which I am using in second function. I want to second method after completing all background task. Can anyone tell me how to achieve this task?

***************In background I am taking healthkit data like******

let healthKitTypesToRead = 
Set(
        arrayLiteral: HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth)!,
        HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBiologicalSex)!,
        HKObjectType.workoutType()
        )

let newCompletion: ((Bool, NSError?) -> Void) = {
        (success, error) -> Void in

        if !success {
            print("You didn't allow HealthKit to access these write data types.\nThe error was:\n \(error!.description).")

            return
        }
        else
      {
       let stepCount = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)

                    // Our search predicate which will fetch data from now until a day ago
                    // (Note, 1.day comes from an extension
                    // You'll want to change that to your own NSDate

                    //let date = NSDate()
                    //let predicate = HKQuery.predicateForSamplesWithStartDate(date, endDate: NSDate(), options: .None)

                    // The actual HealthKit Query which will fetch all of the steps and sub them up for us.
                    let stepCountQuery = HKSampleQuery(sampleType: stepCount!, predicate:.None, limit: 0, sortDescriptors: nil) { query, results, error in
                        var steps: Double = 0

                        if results?.count > 0
                        {
                            for result in results as! [HKQuantitySample]
                            {
                                steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
                            }
                            testClass.HK_stepCount = String(steps)
                        }

                        //completion(steps, error)
                    }

                    self.healthKitStore.executeQuery(stepCountQuery)
             //EDIT.....
          let tHeartRate = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)
            let tHeartRateQuery = HKSampleQuery(sampleType: tHeartRate!, predicate:.None, limit: 0, sortDescriptors: nil) { query, results, error in
                if results?.count > 0
                {
                    var string:String = ""
                    for result in results as! [HKQuantitySample]
                    {
                        let HeartRate = result.quantity
                        string = "\(HeartRate)"
                        print(string)
                    }
                    testClass.HK_HeartRate = string
                      finalCompletion(Success: true)

                }

            }
                 self.healthKitStore.executeQuery(tHeartRateQuery)
      }

    }


healthKitStore.requestAuthorizationToShareTypes(healthKitTypesToWrite, readTypes: healthKitTypesToRead, completion: newCompletion)

I am not able to take value of step count, It get executed after calling second(method) called, plz suggest me what to do?


Solution

  • You can create a separate function to execute your task on other thread

    func someFunction(finalCompletion: (Success: Bool)->()) {
    let healthKitTypesToRead = 
    Set(
            arrayLiteral: HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth)!,
            HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBiologicalSex)!,
            HKObjectType.workoutType()
            )
    
    let newCompletion: ((Bool, NSError?) -> Void) = {
            (success, error) -> Void in
    
            if !success {
                print("You didn't allow HealthKit to access these write data types.\nThe error was:\n \(error!.description).")
    
                return
            }
            else
          {
           let stepCount = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
    
                        // Our search predicate which will fetch data from now until a day ago
                        // (Note, 1.day comes from an extension
                        // You'll want to change that to your own NSDate
    
                        //let date = NSDate()
                        //let predicate = HKQuery.predicateForSamplesWithStartDate(date, endDate: NSDate(), options: .None)
    
                        // The actual HealthKit Query which will fetch all of the steps and sub them up for us.
                        let stepCountQuery = HKSampleQuery(sampleType: stepCount!, predicate:.None, limit: 0, sortDescriptors: nil) { query, results, error in
                            var steps: Double = 0
    
                            if results?.count > 0
                            {
    
                               // Edit--  
                                for result in results as! [HKQuantitySample]
                                {
                                    steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
                                 //   heartBeat += ....
                                }
                                testClass.HK_stepCount = String(steps)
                                finalCompletion(Success: true)
    
                            }
    
                            //completion(steps, error)
                        }
    
                        self.healthKitStore.executeQuery(stepCountQuery)
          }
    
        }
    
    
    healthKitStore.requestAuthorizationToShareTypes(healthKitTypesToWrite, readTypes: healthKitTypesToRead, completion: newCompletion)
    }
    

    Another function?

    I will edit this answer in some time to tell you about a better technique to deal with async request. In general you should have a separate singleton class for such background tasks. (RESTful API service class.. but for now you can use the below method)

    func getHeartBeatInfo(finalCompletionHeart: (Success: Bool)->()) {
    
     let tHeartRate = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)
            let tHeartRateQuery = HKSampleQuery(sampleType: tHeartRate!, predicate:.None, limit: 0, sortDescriptors: nil) { query, results, error in
                if results?.count > 0
                {
                    var string:String = ""
                    for result in results as! [HKQuantitySample]
                    {
                        let HeartRate = result.quantity
    
                        string = "\(HeartRate)"
                        print(string)
                    }
                    testClass.HK_HeartRate = string
                      finalCompletionHeart(Success: true)
    
                }
    
            }
                 self.healthKitStore.executeQuery(tHeartRateQuery)
       }
    

    And then you can call this method like so:

    override func viewDidLoad() {
    
          someFunction( { (finalCompletion) in
    
             if finalCompletion == true {
    
                getHeartBeatInfo( { finalCompletionHeart in
    
                    if finalCompletionHeart == true {
    
                          self.second()
                    }
    
                })
             }
          })
    }