Let's say we have some steps data in healthkit which was auto detected by iphone and written to healthkit. And we have some steps data was enetred by user manually. Now I can differentiate between these steps using this code
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount) // The type of data we are requesting
let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let newDate = cal.startOfDayForDate(date)
print(newDate)
let predicate = HKQuery.predicateForSamplesWithStartDate(newDate, endDate: NSDate(), options: .None) // Our search predicate which will fetch all steps taken today
let query = HKSampleQuery(sampleType: type!, predicate: predicate, limit: 0, sortDescriptors: nil) { query, results, error in
var steps: Double = 0
//if result.metadata!["HKWasUserEntered"]! == 1{
if results?.count > 0
{
for result in results as! [HKQuantitySample]
{
print("Steps \(result.quantity.doubleValueForUnit(HKUnit.countUnit()))")
print()
// checking and truncating manually added steps
if result.metadata != nil {
}
else{
steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
}
}
print(steps)
}
completion(steps, error)
//}
}
executeQuery(query)
But Lets say if some other app writes some some step's data to healthkit like some fitness app.
Now I am able to read steps with device icon(auto recorded), but
How can I read steps with UP icon as well ?
(UP) is and fitness application by JawBone.
Related
There isn't a whole of sample code on the internet for querying for sleep data. The below code will return all sleep samples from a given day which if you look at the Apple Health App include "Asleep" samples from the Apple Watch which are sleep intervals, but there is also an "In Bed" sample from the iPhone which contains the total range from getting in bed to getting out of bed. How can I query HealthKit for only this In Bed sample?
func sleepTime() {
let healthStore = HKHealthStore()
// startDate and endDate are NSDate objects
// first, we define the object type we want
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
// You may want to use a predicate to filter the data... startDate and endDate are NSDate objects corresponding to the time range that you want to retrieve
//let predicate = HKQuery.predicateForSamplesWithStartDate(startDate,endDate: endDate ,options: .None)
// Get the recent data first
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
// the block completion to execute
let query = HKSampleQuery(sampleType: sleepType, predicate: nil, limit: 100000, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
if error != nil {
// Handle the error in your app gracefully
return
}
if let result = tmpResult {
for item in result {
if let sample = item as? HKCategorySample {
let startDate = sample.startDate
let endDate = sample.endDate
print()
let sleepTimeForOneDay = sample.endDate.timeIntervalSince(sample.startDate)
}
}
}
}
}
HKCategorySample contains the value of type Int which is the enumeration value for the sample.
It has three values:
0 -> inBed
1 -> asleep
2 -> awake
So, the proposed change if you only want inBed data is:
if let result = tmpResult {
for item in result {
if let sample = item as? HKCategorySample {
if sample.value == 0 {
let startDate = sample.startDate
let endDate = sample.endDate
print()
let sleepTimeForOneDay = sample.endDate.timeIntervalSince(sample.startDate)
}
}
}
}
Better way is to go with switch case.
if let result = tmpResult {
for item in result {
if let sample = item as? HKCategorySample {
switch sample.value {
case 0:
// inBed, write logic here
print("inBed")
case 1:
// asleep, write logic here
print("asleep")
default:
// awake, write logic here
print("awake")
}
}
}
}
I am creating an app that provides workout information. On apple watch i have three labels that actively shows HEARTRATE ,DISTANCE TRAVELLED and CALORIES BURNED. Things work fine with apple watch, however the area where things go bad is when i try to show this data on iphone in realtime.
APPLE WATCH PART :-
I started workout on apple watch and saved it using folling code
func startWorkoutSession() {
// Start a workout session with the configuration
if let workoutConfiguration = configuration {
do {
workoutSession = try HKWorkoutSession(configuration: workoutConfiguration)
workoutSession?.delegate = self
workoutStartDate = Date()
healthStore.start(workoutSession!)
} catch {
// ...
}
}
}
Then i save that workout on apple watch using following code :-
func saveWorkout() {
// Create and save a workout sample
let configuration = workoutSession!.workoutConfiguration
let isIndoor = (configuration.locationType == .indoor) as NSNumber
print("locationType: \(configuration)")
let workout = HKWorkout(activityType: configuration.activityType,
start: workoutStartDate ?? Date(),
end: workoutEndDate ?? Date(),
workoutEvents: workoutEvents,
totalEnergyBurned: totalEnergyBurned,
totalDistance: totalDistance,
metadata: [HKMetadataKeyIndoorWorkout:isIndoor]);
healthStore.save(workout) { success, _ in
if success {
self.addSamples(toWorkout: workout)
}
}
// Pass the workout to Summary Interface Controller
// WKInterfaceController.reloadRootControllers(withNames: ["StopPauseInterfaceController"], contexts: [workout])
if #available(watchOSApplicationExtension 4.0, *){
// Create the route, save it, and associate it with the provided workout.
routeBuilder?.finishRoute(with: workout, metadata: metadata) { (newRoute, error) in
guard newRoute != nil else {
// Handle the error here...
return
}
}
}
else {
// fallback to earlier versions
}
}
Then i added samples to those workouts using following code :-
func addSamples(toWorkout workout: HKWorkout) {
// Create energy and distance samples
let totalEnergyBurnedSample = HKQuantitySample(type: HKQuantityType.activeEnergyBurned(),
quantity: totalEnergyBurned,
start: workoutStartDate!,
end: workoutEndDate ?? Date())
let totalDistanceSample = HKQuantitySample(type: HKQuantityType.distanceWalkingRunning(),
quantity: totalDistance,
start: workoutStartDate!,
end: workoutEndDate ?? Date())
// Add samples to workout
healthStore.add([totalEnergyBurnedSample, totalDistanceSample], to: workout) { (success: Bool, error: Error?) in
if success {
// Samples have been added
}
}
}
IPHONE PART :-
This is where things get worse. I try to access the healthkit store using HKSampleObjectQuery and It just returns static values. P.S - I am fetching healthkit store every one second for latest data using NSTimer.
The code which is called every second is :-
func extractDataFromHealthStore(identifier : HKQuantityTypeIdentifier,completion: #escaping (String) -> ()) {
let heightType = HKSampleType.quantityType(forIdentifier:identifier)!
let distantPastDate = Date.distantPast
let endDate = Date()
let predicate = HKQuery.predicateForSamples(withStart: distantPastDate, end: endDate, options: .strictStartDate)
// Get the single most recent Value
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)
let query = HKSampleQuery(sampleType: heightType, predicate: predicate, limit: 1, sortDescriptors: [sortDescriptor]) { (query, results, error) in
if let result = results?.first as? HKQuantitySample{
print("Height => \(result.quantity)")
completion("\(result.quantity)")
}else{
print("OOPS didnt get height \nResults => \(String(describing: results)), error => \(error)")
completion("")
}
}
self.healthStore.execute(query)
}
Please guide Me if my whole approach is wrong, or if it is right then what is the mistake at my end.
Active calories, stand hours and workouts are saved in healthkit, but it seems that exercise data is stored only in Activity app but not in healthkit. Are there any way to access this information?
As of iOS 9.3, you can read each of the activity rings via the new HKActivitySummaryQuery which will return an HKActivitySummary object containing details of each ring. The sample code from Apple is as follows:
// Create the date components for the predicate
guard let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian) else {
fatalError("*** This should never fail. ***")
}
let endDate = NSDate()
guard let startDate = calendar.dateByAddingUnit(.Day, value: -7, toDate: endDate, options: []) else {
fatalError("*** unable to calculate the start date ***")
}
let startDateComponents = calendar.components(units, fromDate: startDate)
startDateComponents.calendar = calendar
let endDateComponents = calendar.components(units, fromDate:endDate)
endDateComponents.calendar = calendar
let startDateComponents = calendar.components(units, fromDate: startDate)
// Create the predicate for the query
let summariesWithinRange = HKQuery.predicateForActivitySummariesBetweenStartDateComponents(startDateComponents, endDateComponents: endDateComponents)
// Build the query
let query = HKActivitySummaryQuery(predicate: summariesWithinRange) { (query, summaries, error) -> Void in
guard let activitySummaries = summaries else {
guard let queryError = error else {
fatalError("*** Did not return a valid error object. ***")
}
// Handle the error here...
return
}
// Do something with the summaries here...
if let summary = summaries?.first {
NSLog("Exercise: \(summary.appleExerciseTime)")
}
}
// Run the query
store.executeQuery(query)
The piece you'll be interested in is the appleExerciseTime property of the HKActivitySummary.
Please note that this code doesn't include the authorisation request you'll need to make to be able to read the activity summary which is of HKObjectType.activitySummaryType(). It is not possible to write HKActivitySummary to HealthKit so the app will crash if you request write permissions.
The green Exercise ring data is unfortunately not accessible to apps. You should file a radar with Apple to ask for it to be exposed by HealthKit.
So I have this function, which gets and prints all the HealthKit step data from the past 24 hours, and saves it to an array:
func stepsInPastDay(completion: (Double, NSError?) -> () )
{
var dayStepData = [Double]()
for x in 1...24 {
// The type of data we are requesting
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
var hoursAgo = -1 * x
var hoursSince = (-1 * x) + 1
// Our search predicate which will fetch data from now until a day ago
let predicate = HKQuery.predicateForSamplesWithStartDate(NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitHour, value: hoursAgo, toDate: NSDate(), options: nil), endDate: NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitHour, value: hoursSince, toDate: NSDate(), options: nil), options: .None)
// The actual HealthKit Query which will fetch all of the steps and sub them up for us.
let query = HKSampleQuery(sampleType: type, predicate: predicate, 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())
}
}
completion(steps, error)
dayStepData.append(steps)
if dayStepData.count > 23 {
for item in dayStepData {
println(item)
}
}
}
self.healthKitStore.executeQuery(query)
println(dayStepData.count)
}
println(dayStepData.count)
}
However, when I tried to access the array (dayStepData) in my AppDelegate file, with "HKManager.stepsInPastDay.dayStepData" (HKManager is the class), Xcode returns an error. Is there a way to get the array from my function?
This is OOP (Object-oriented-programmming) 101 stuff. You are saving your value to a local variable. Of course it isn't visible in your app delegate.
Put the function in a singleton class of some sort make the function return the array as the function result.
If you're putting app logic in your app delegate you're doing it wrong. Keep your app delegate small and lightweight. It should ONLY handle startup and other app delegate tasks. Put your app-specific logic in other modules.
I have a function that takes the total number of steps recorded by the device, saves it to a variable, and then gets the step data from each day, adding them to another variable until the two have the same value. I need this in order to for the app to know when to stop when it is saving the all time step data to an array.
However, the second half of this function does not execute, and I have no idea why. Here is the function:
// allTimeStepTotal and allTimeStepSum are doubles that are defined with a value of 0.0
func stepsAllTime(completion: (Double, NSError?) -> () ) {
// The type of data we are requesting
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
// Our search predicate which will fetch data from now until a day ago
let predicate = HKQuery.predicateForSamplesWithStartDate(NSDate.distantPast() as! NSDate, endDate: NSDate(), options: .None)
// The actual HealthKit Query which will fetch all of the steps and sub them up for us.
let query = HKSampleQuery(sampleType: type, predicate: predicate, 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())
}
}
completion(steps, error)
self.allTimeStepsTotal += steps
println("Total:")
println(self.allTimeStepsTotal)
println("Sum:")
println(self.allTimeStepsSum)
}
self.healthKitStore.executeQuery(query)
println("Moving On")
var x = 1
while self.allTimeStepsTotal > self.allTimeStepsSum {
x += -1
// The type of data we are requesting
let sampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
var daysAgo = -1 * x
var daysSince = (-1 * x) + 1
// Our search predicate which will fetch data from now until a day ago
let samplePredicate = HKQuery.predicateForSamplesWithStartDate(NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitDay, value: daysAgo, toDate: NSDate(), options: nil), endDate: NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitDay, value: daysSince, toDate: NSDate(), options: nil), options: .None)
// The actual HealthKit Query which will fetch all of the steps and sub them up for us.
let stepQuery = HKSampleQuery(sampleType: sampleType, predicate: samplePredicate, 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())
}
}
completion(steps, error)
self.allTimeStepsSum += steps
println("New Sum:")
println(self.allTimeStepsSum)
}
self.healthKitStore.executeQuery(stepQuery)
}
And here is the call:
healthManager.stepsAllTime({Double, NSError in
println("All Done")
})
println("Finished executing stepsAllTime")
Can anyone tell me what I need to fix, or what went wrong?
Assuming that allTimeStepsTotal and allTimeStepsSum are initialized to 0.0, the second half of that function won't execute because the HKSampleQuery you've created executes asynchronously—that is, it calls the resultHandler at some time in the future after the the while loop in the second half of your function is evaluated. The condition self.allTimeStepsTotal > self.allTimeStepsSum will evaluate to false since both values are still 0.0, and the loop will not execute.