Hi any one know how to fetch workouts data from HealthKit. i have seen in this tutorial http://www.raywenderlich.com/89733/healthkit-tutorial-with-swift-workouts it is in swift. i have tried in objective c based on that tutorial but getting results zero. there are questions for saving workouts but i want to read workouts data and display.
HKWorkoutType *workouttype = [HKWorkoutType workoutType];
HKWorkout *workout;
NSDate *startDate, *endDate;
NSDate *date1 = [NSDate date];
int daysTominus = -2;
startDate = [date1 dateByAddingTimeInterval:60*60*24*daysTominus];
int daysToAdd = 1;
NSDate *newDate1 = [date1 dateByAddingTimeInterval:60*60*24*daysToAdd];
endDate = newDate1;
workout = [HKWorkout workoutWithActivityType:HKWorkoutActivityTypeSwimming startDate:startDate endDate:endDate];
NSPredicate *predicate = [HKQuery predicateForObjectsFromWorkout:workout];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:YES];
HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:workouttype
predicate:predicate
limit:HKObjectQueryNoLimit
sortDescriptors:#[sortDescriptor]
resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error)
{
if(!error && results){
for(HKQuantitySample *samples in results)
{
// your code here
NSLog(#"%#",samples);
}
}
}];
// Execute the query
[healthStore executeQuery:sampleQuery];
The problem seems to be your predicate.
This code works for me, I used running because I don't have swimming data but you can change it back to swimming:
-(void)retrieveWorkouts{
// 1. Predicate to read only running workouts
NSPredicate *predicate = [HKQuery predicateForWorkoutsWithWorkoutActivityType:HKWorkoutActivityTypeWalking];
// 2. Order the workouts by date
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:HKSampleSortIdentifierStartDate ascending:false];
// 3. Create the query
HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:[HKWorkoutType workoutType]
predicate:predicate
limit:HKObjectQueryNoLimit
sortDescriptors:#[sortDescriptor]
resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error)
{
if(!error && results){
NSLog(#"Retrieved the following workouts");
for(HKQuantitySample *samples in results)
{
// your code here
HKWorkout *workout = (HKWorkout *)samples;
NSLog(#"%f",workout);
}
}else{
NSLog(#"Error retrieving workouts %#",error);
}
}];
// Execute the query
[healthStore executeQuery:sampleQuery];
}
Related
Is there a way to query NSDate in CoreData. For example if I want an entity with the highest NSDate value? I see that NSExpression "max:" only takes an NSNumber.
You can actually ask SQL for just that value, not the object with that value:
NSExpression *date = [NSExpression expressionForKeyPath:#"date"];
NSExpression *maxDate = [NSExpression expressionForFunction:#"max:"
arguments:[NSArray arrayWithObject:maxDate]];
NSExpressionDescription *d = [[[NSExpressionDescription alloc] init] autorelease];
[d setName:#"maxDate"];
[d setExpression:maxSalaryExpression];
[d setExpressionResultType:NSDateAttributeType];
[request setPropertiesToFetch:[NSArray arrayWithObject:d]];
NSError *error = nil;
NSArray *objects = [managedObjectContext executeFetchRequest:request error:&error];
if (objects == nil) {
// Handle the error.
} else {
if (0 < [objects count]) {
NSLog(#"Maximum date: %#", [[objects objectAtIndex:0] valueForKey:#"maxDate"]);
}
}
more detail under Fetching Managed Objects -> Fetching Specific Values in the CoreData documentation.
or
Perfomed a query, ordered on Date field DESCENDING, and using setFetchLim it:1.
Its not perfect, but at least it worked.
You can do this directly in SQLite-- without fetching everything and then filtering the result, and without the complexity of NSExpression.
To get the one object that has the max date, do something like (assuming entity name Entity and date attribute timeStamp):
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Event"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"timeStamp = self.#max.timeStamp"];
fetchRequest.predicate = predicate;
Do the fetch. You'll get (at most) one result, which will be the instance with the max date.
If you want to get just the date and not the entire managed object, add this before doing the fetch:
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.propertiesToFetch = #[ #"timeStamp" ];
you can get it with
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"tablename"];
fetchRequest.fetchLimit = 1;
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"yourDate" ascending:NO]];
NSError *error = nil;
id person = [managedObjectContext executeFetchRequest:fetchRequest error:&error].firstObject;
My relation is shown on the picture below.
I'd like to sort Events first by those that are in active promotion and then by start date. Promotion is active if current date is between start and end date. Unfortunately, because of CoreData, I'm not able to use transient properties for sorting. In my controller I'm not using a fetch controller.
Is there any way to achieve that?
Update:
I've following sort descriptors:
// First is incorrect
[NSSortDescriptor(key: "promotion.start", ascending: false),
NSSortDescriptor(key: "start", ascending: true)]
Predicates (They're ok, though):
let promotionsPredicate =
NSPredicate(format: "(%# >= promotion.start && %# <= promotion.end) && " +
"(ANY promotion.cities.id == %#)", NSDate(), NSDate(), objectID)
let eventsPredicate =
NSPredicate(format: "start >= %# && venue.city.id == %#",
NSDate(), objectID)
let subpredicates = [eventsPredicate, promotionsPredicate]
let compoundPredicate NSCompoundPredicate(orPredicateWithSubpredicates: subpredicates)
And this is the Request (I'm using CoreStore, but the idea should be clear):
class func pagedEventsForPredicateSortedByInPromoAndStartDate(predicate: NSPredicate,
descriptors: [NSSortDescriptor],
fetchOffset: Int,
fetchLimit: Int) -> [Event] {
return CoreStore.fetchAll(From(Event),
Where(predicate),
OrderBy(descriptors),
Tweak { (fetchRequest) -> Void in
fetchRequest.fetchOffset = fetchOffset
fetchRequest.fetchLimit = fetchLimit
fetchRequest.returnsObjectsAsFaults = false
}) ?? []
}
As I understood you have to get all Event objects, but just in proper order. To do that with such complicated order, that includes relationship, as far as I know you have to fetch all Events and then sort them using NSArray's method
- (NSArray<ObjectType> *)sortedArrayUsingComparator:(NSComparator)cmptr
Here are the pieces of the code
1. Fetch from Core Data
// get the right context here
NSManagedObjectContext *yourContext;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Event"];
// extra line, predicate is nil by default, any other required predicate could be written here
request.predicate = nil;
__block NSArray *results = nil;
[yourContext performBlockAndWait:^{
NSError *error = nil;
results = [yourContext executeFetchRequest:request error:&error];
if (error) {
// handle error here
}
}];
Fetch is made manually with core methods and you may use Magical Record or any other framework that works with Core Data to make it in a row.
2. Sort the results
__weak typeof(self) weakSelf = self;
NSDate *now = [NSDate date];
NSArray *sortedResults = [results sortedArrayUsingComparator:^NSComparisonResult(Event *_Nonnull obj1, Event *_Nonnull obj2) {
BOOL isObj1InActivePromotion = [weakSelf date:now isBetweenDate:obj1.promotion.start andDate:obj1.promotion.end];
BOOL isObj2InActivePromotion = [weakSelf date:now isBetweenDate:obj2.promotion.start andDate:obj2.promotion.end];
// if they eather are in active promotion or no, just compare them by start date of the Event
if (isObj1InActivePromotion == isObj2InActivePromotion) {
return [obj1.start compare:obj2.start];
} else {
return isObj1InActivePromotion ? NSOrderedAscending : NSOrderedDescending;
}
}];
3. Additional method to work with NSDate
This method was used in sorting method
+ (BOOL)date:(NSDate *)date isBetweenDate:(NSDate *)beginDate andDate:(NSDate *)endDate
{
if ([date compare:beginDate] == NSOrderedAscending) {
return NO;
}
if ([date compare:endDate] == NSOrderedDescending) {
return NO;
}
return YES;
}
I could't check the code for obvious reasons, so sorry for any typos if they are.
I will suggest you to create "isActive" transection property in Promotion Entity to calculate active record.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Event"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey:#“start” ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[fetchRequest setIncludesPropertyValues:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
After that you can filter fetch result by sorting :
NSPredicate *predicate = [NSPredicate predicateWithFormat:#“isActive == %#", #1];
NSMutableArray *finalResult = [NSMutableArray arrayWithArray:[results filteredArrayUsingPredicate:predicate]];
HTH.
I'm working with HealthKit to read steps data from my iOS device.
here is my code:
if ([HKHealthStore isHealthDataAvailable]) {
__block double stepsCount = 0.0;
self.healthStore = [[HKHealthStore alloc] init];
NSSet *stepsType =[NSSet setWithObject:[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]];
[self.healthStore requestAuthorizationToShareTypes:nil readTypes:stepsType completion:^(BOOL success, NSError * _Nullable error) {
if (success) {
HKSampleType *sampleType = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:nil limit:HKObjectQueryNoLimit sortDescriptors:nil resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if (error != nil) {
NSLog(#"results: %lu", (unsigned long)[results count]);
for (HKQuantitySample *result in results) {
stepsCount += [result.quantity doubleValueForUnit:[HKUnit countUnit]];
}
NSLog(#"Steps Count: %f", stepsCount);
} else {
NSLog(#"error:%#", error);
}];
[self.healthStore executeQuery:sampleQuery];
[self.healthStore stopQuery:sampleQuery];
NSLog(#"steps:%f",stepsCount);
}
}];
}
I build and run the code on an iPhone6 which does have steps data and in the Settings -> Privacy -> Health, the app does have been allowed to read data, but the log area only shows:
steps:0.000000
I put a break point on the for-loop and on the NSLog(#"error:%#", error), but the app does not break.
Anybody can help?
Try this code u just change the start date and End date.
-(void) getQuantityResult
{
NSInteger limit = 0;
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:currentDate endDate:[[NSDate date]dateByAddingTimeInterval:60*60*24*3] options:HKQueryOptionStrictStartDate];
NSString *endKey = HKSampleSortIdentifierEndDate;
NSSortDescriptor *endDate = [NSSortDescriptor sortDescriptorWithKey: endKey ascending: NO];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]
predicate: predicate
limit: limit
sortDescriptors: #[endDate]
resultsHandler:^(HKSampleQuery *query, NSArray* results, NSError *error){
dispatch_async(dispatch_get_main_queue(), ^{
// sends the data using HTTP
int dailyAVG = 0;
for(HKQuantitySample *samples in results)
{
dailyAVG += [[samples quantity] doubleValueForUnit:[HKUnit countUnit]];
}
lblPrint.text = [NSString stringWithFormat:#"%d",dailyAVG];
NSLog(#"%#",lblPrint.text);
NSLog(#"%#",#"Done");
});
}];
[self.healthStore executeQuery:query];
}
Your code stops the query immediately, before it has a chance to run. For this query, there is no reason to call stopQuery: at all unless you want to cancel a query before it finishes. Since the query is not long lived (it doesn't have an updateHandler), it will stop immediately after the resultsHandler is called.
The second problem is that your code attempts to log step count too soon. The query runs asynchronously, and the resultsHandler will be called on a background thread once the query completes. I'd suggest logging stepsCount inside the block.
Finally, if you want to count the user's steps you should us an HKStatisticsQuery instead of summing the results of an HKSampleQuery. HKStatisticsQuery is more efficient and will yield correct results when there are multiple sources of overlapping data in HealthKit. Your current implementation will double count the user's steps if they have both an iPhone and an Apple Watch, for instance.
I just did a HKSourceQuery and got some results. When I do a println of the results, I got this: <HKSource:0x156c1520 "Health" (com.apple.Health)>//description of the object
How do I use this to make a predicate using the HKQuery.predicateForObjectsFromSource(/* source goes here */)
Here is the sample code in Obj-c,
NSSortDescriptor *timeSortDesriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
HKQuantityType *quantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned];
HKSourceQuery *sourceQuery = [[HKSourceQuery alloc] initWithSampleType:quantityType samplePredicate:nil completionHandler:^(HKSourceQuery *query, NSSet *sources, NSError *error) {
//Here, sources is a set of all the HKSource objects available for "quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned"
HKSource *targetedSource = [[sources allObjects] firstObject];//Assume this as your targeted source
if(targetedSource)
{
NSPredicate *sourcePredicate = [HKQuery predicateForObjectsFromSource:targetedSource];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:quantityType predicate:sourcePredicate limit:HKObjectQueryNoLimit sortDescriptors:[NSArray arrayWithObject:timeSortDesriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
//results array contains the HKSampleSample objects, whose source is "targetedSource".
}];
[self.healthStore executeQuery:query];
}
}];
[self.healthStore executeQuery:sourceQuery];
UPDATE 1:
It is not possible to construct HKSource object manually using [HKSource alloc] init]. In HealthKit framework, Apple restricted creation of objects using init for most of the HK classes.
I believe that you can find your HKSource object from the sources set using the HKSource properties like name and bundleIdentifier.
Here is the sample code,
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.source.bundleIdentifier = 'com.XXXX.XXXXX'"];
NSArray *tempResults = [[sources allObjects] filteredArrayUsingPredicate:predicate];
HKSource *targetedSource = [tempResults firstObject];
I am retrieving heart rate from Health Kit in my app. however i get the latest data from Health Kit, how can i get the data monthly wise? I mean last reading from each month.. i saw NSPredicate can be used to do so but i am not getting exactly how to do it..!! I am new to it, please help.!!
here is my code to access heart rate:
NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:quantityType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:#[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if (!results) {
if (completion)
{
completion(nil, error);
}
return;
}
if (completion) {
// If quantity isn't in the database, return nil in the completion block.
HKQuantitySample *quantitySample = results.firstObject;
HKQuantity *quantity = quantitySample.quantity;
completion(quantity, error);
}
}];
[self executeQuery:query];
Code for predicate:
NSDate *startDate = [NSDate dateWithYear:[NSNumber numberWithInt:2015] month:[NSNumber numberWithInt:1] day:[NSNumber numberWithInt:1] hour:[NSNumber numberWithInt:00] minute:[NSNumber numberWithInt:00] seconds:[NSNumber numberWithInt:00]];
NSDate *endDate = [NSDate dateWithYear:[NSNumber numberWithInt:2015] month:[NSNumber numberWithInt:30] day:[NSNumber numberWithInt:1] hour:[NSNumber numberWithInt:00] minute:[NSNumber numberWithInt:00] seconds:[NSNumber numberWithInt:59]];
NSPredicate *explicitTimeInterval =
[NSPredicate predicateWithFormat:#"%K >= %# AND %K < %#",
HKPredicateKeyPathEndDate, startDate,
HKPredicateKeyPathStartDate, endDate];
self.healthStore = [[HKHealthStore alloc]init];
Thanks in advance :)