4

I'm making Health App. I want to get walkingRunningDistance from HealthKit in Swift. But, I have a problem. Return value is 0.0mile.

Why return value is 0 mile?

My code is this.

func recentSteps3(completion: (Double, NSError?) -> () ){
    let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)

    let date = NSDate()

    let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!

    let newDate = cal.startOfDayForDate(date)

    let predicate = HKQuery.predicateForSamplesWithStartDate(newDate, endDate: NSDate(), options: HKQueryOptions.StrictStartDate)

    let query = HKSampleQuery(sampleType: type!, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { query, results, error in

        var distance: Double = 0

        if results?.count > 0
        {
            for result in results as! [HKQuantitySample]
            {
                distance += result.quantity.doubleValueForUnit(HKUnit.mileUnit())
            }
        }

        completion(distance, error)
    }

    healthAuth.healthStore.executeQuery(query)
}
Sam Spencer
  • 8,492
  • 12
  • 76
  • 133
Sung-jun Kim
  • 41
  • 1
  • 1
  • 6

4 Answers4

7

Your code will return a value if

  1. You have requested permission from the user to read distanceWalkingRunning from HealthKit
  2. The user has granted your app permission.

If not, your code will return 0.

To request authorization, you can call

func requestAuthorization(toShare typesToShare: Set<HKSampleType>?, read typesToRead: Set<HKObjectType>?, completion: @escaping (Bool, Error?) -> Swift.Void)

where typesToRead contains

let distanceType =  HKSampleType.quantityType(forIdentifier: HKQuantityTypeIdentifier.distanceWalkingRunning)!

I believe that using a HKStatisticsQuery or HKStatisticsCollectionQuery will be more efficient. Here is an example.

guard let type = HKSampleType.quantityType(forIdentifier: .distanceWalkingRunning) else {
    fatalError("Something went wrong retriebing quantity type distanceWalkingRunning")
}
let date =  Date()
let cal = Calendar(identifier: Calendar.Identifier.gregorian)
let newDate = cal.startOfDay(for: date)

let predicate = HKQuery.predicateForSamples(withStart: newDate, end: Date(), options: .strictStartDate)

let query = HKStatisticsQuery(quantityType: type, quantitySamplePredicate: predicate, options: [.cumulativeSum]) { (query, statistics, error) in        
    var value: Double = 0

    if error != nil {
        print("something went wrong")
    } else if let quantity = statistics?.sumQuantity() {
        value = quantity.doubleValue(for: HKUnit.mile())
    }
    DispatchQueue.main.async {
        completion(value)
    }
}
healthStore.execute(query)
Jack
  • 13,571
  • 6
  • 76
  • 98
Carien van Zyl
  • 2,853
  • 22
  • 30
  • Can I use this approach for continuous monitoring? Use case: Want to create one app that shows distance, speed, calories since app is running. Tried this but data was increasing (number of samples) at exponential rate. Timer was periodic with 1 sec duration. – Rajesh Budhiraja Oct 10 '21 at 20:00
3

For Swift 4.1

After enabled the HealthKit in your project's capabilities and added the necessary "Privacy - Health Share Usage Description" key to your info.plist file...

Ensure you are requesting for the approval of the user by adding this to the ViewController.swift, practically in the viewDidLoad() function.

    let store = HKHealthStore()

    let stepType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
    let woType = HKObjectType.workoutType()

    store.requestAuthorization(toShare: [], read: [stepType, woType], completion: { (isSuccess, error) in
        if isSuccess {
            print("Working")
            self.getSteps()
        } else {
            print("Not working")
        }
    })

Then create the getSteps() function.

func getSteps() {
    let startDate = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: Date())!

    let endDate = Date()

    print("Collecting workouts between \(startDate) and \(endDate)")

    let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: HKQueryOptions.strictEndDate)

    let query = HKSampleQuery(sampleType: HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning)!, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { (query, results, error) in
        for item in results! {
            print(item)
        }
    }

    store.execute(query)
}
KZD76
  • 122
  • 7
  • 1
    You're requesting authorisation for different `HKQuantityType` than your later try to query, this won't work, you must have authorisation for type you query for so you either need to change `stepType` like this `let stepType = HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning)!` or query should be changed to `stepCount` `let query = HKSampleQuery(sampleType: HKQuantityType.quantityType(forIdentifier: .stepCount)!, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { ... }` – dziobaczy Jan 26 '21 at 23:05
0
healthStore =[HKHealthStore new];
HKQuantityType *stepType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKSampleType *sleepType = [HKSampleType categoryTypeForIdentifier:HKCategoryTypeIdentifierSleepAnalysis];
HKQuantityType *walkType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
NSArray *arrayType = @[stepType,sleepType,walkType];

[healthStore requestAuthorizationToShareTypes:[NSSet setWithArray:arrayType]
                                    readTypes:[NSSet setWithArray:arrayType] completion:^(BOOL succeeded, NSError *error) {
                                        if (succeeded) {
                                            NSLog(@"Not working");
                                            NSLog(@"error %@",error);
                                        } else {
                                            NSLog(@"Working!");
                                            NSLog(@"error %@",error);
                                        }
                                        [self getStepCount];
                                    }];

The above method for requsting access and below method to get count

-(void)getStepCount{
     NSInteger limit = 0;
     NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd hh:mm:ss"];
    NSDate *startDate = [NSDate  dateWithTimeIntervalSince1970:1372418789];
    // Divided by 1000 (i.e. removed three trailing zeros) ^^^^^^^^
    NSString *formattedDateString = [dateFormatter stringFromDate:startDate];
   // Fri, 28 Jun 2013 11:26:29 GMT
   NSLog(@"start Date: %@", formattedDateString);
   NSDateFormatter *dateFormatter1=[[NSDateFormatter alloc] init];
   [dateFormatter1 setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
   NSLog(@"%@",[dateFormatter1 stringFromDate:[NSDate date]]);
   NSString *dateString =[dateFormatter1 stringFromDate:[NSDate date]];
  NSDate *endDate = [dateFormatter1 dateFromString:dateString];


 NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictEndDate];

HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning]
                        predicate: predicate
                        limit: limit
                        sortDescriptors: nil
                        resultsHandler:^(HKSampleQuery *query, NSArray* results, NSError *error){
                            dispatch_async(dispatch_get_main_queue(), ^{
                                // sends the data using HTTP
                                int dailyAVG = 0;
                                NSLog(@"result : %@",results);
                                for(HKQuantitySample *samples in results)
                                {
                                    NSLog(@"dailyAVG : %@",samples);
                                }
                                NSLog(@"dailyAVG : %d",dailyAVG);

                                NSLog(@"%@",@"Done");
                            });
                        }];
[healthStore executeQuery:query];

}

0

if it return zero then your code is correct. but since it is in simulator view, simulator you cannot read Distance. for that go to Health App in your simulator and select Distance Walking Running Distance and press add data

and then your app might read those data.

  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/33590579) – burnsi Jan 11 '23 at 00:06