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];
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;
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];
}
I'm writing an app that amongst other things, reads weight samples from HealthKit.
I'm also writing samples.
I'm trying to read the latest sample that isn't mine:
NSPredicate* non_fdct = [NSCompoundPredicate notPredicateWithSubpredicate:[HKQuery predicateForObjectsFromSource:[HKSource defaultSource]]];
NSSortDescriptor *last = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
HKSampleQuery* query = [[HKSampleQuery alloc] initWithSampleType:[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass] predicate:non_fdct limit:1 sortDescriptors:#[last] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) { ... };
But I'm getting my own samples if they are the latest samples.
Any idea?
The way you have constructed the non_fdct predicate is not quite correct. Try this instead:
NSPredicate *non_fdct = [NSPredicate predicateFromString:#"%K != %#", HKPredicateKeyPathSource, [HKSource defaultSource]];
What I am trying to do
I am using the code below to download data (historic foreign exchange rates) from my backend server (parse.com) to my app's Core Data store.
The app checks for the latest available data stored locally and fetches only the newer data from the server. If there is no data stored locally yet, it fetches all data from the server.
The way the code is set up, it fetches the data in batches of 100 objects, saves the objects in Core Data, gets the new latest date for which data is now locally stored (by using NSExpression) and fetches the next batch until no more new objects are left on the server (objects.count = 0).
Because fetching is slow, I decided to run the fetching and Core Data saves on a back ground thread (using the new Core Data multi-treading model provided by iOS 5).
Fetching from the backend server works fine, but...
My Problem
it seems that only those objects are evaluated by the NSExpression which are stored on disk (physically in the database) and not the objects which are still in memory and will be saved soon. Hence, the fetch retrieves mostly the "old" values from disk (memory).
However, when using the following code of fetch (with no NSExpression and NSDictionary as result type), I get the current and correct values:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:localEntityName];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"date" ascending:YES];
request.sortDescriptors = #[sortDescriptor];
NSArray *results = [backgroundContext executeFetchRequest:request error:&error];
ForexHistory *forexHistoryItem = results.lastObject;
NSDate *lastLocalDate = forexHistoryItem.date;
NSLog(#"last local date results: %#",lastLocalDate);
What's wrong with my code below, which uses NSExpression and dictionary as the fetch resultType ?
My question
How can I make sure that the NSExpression which looks for the latest locally available date returns the latest date?
The code
- (void)seedForexHistoryInManagedObjectContext:(NSManagedObjectContext*)context {
NSString* const localEntityName = #"ForexHistory";
NSString* const parseEntityName = localEntityName;
NSString* const parseDateIdentifier = #"date";
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:#"date"];
NSExpression *maxPeriodExpression = [NSExpression expressionForFunction:#"max:"
arguments:#[keyPathExpression]];
NSString *expressionName = #"maxDate";
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
expressionDescription.name = expressionName;
expressionDescription.expression = maxPeriodExpression;
expressionDescription.expressionResultType = NSDateAttributeType;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:localEntityName];
request.propertiesToFetch = #[expressionDescription];
request.resultType = NSDictionaryResultType;
NSArray *currencies = #[#"AUD",#"EUR",#"NZD",#"GBP",#"BRL",#"CAD",#"CNY"];
dispatch_queue_t downloadQueue;
downloadQueue = dispatch_queue_create("download", NULL); // create serial dispatch queue
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
moc.parentContext = context;
dispatch_async(downloadQueue,^{
[moc performBlockAndWait:^{
NSArray *objects;
do {
NSError *error;
NSArray *dateResults = [moc executeFetchRequest:request error:&error];
NSAssert(dateResults.count == 1,#"Request error!");
NSDate *lastLocalDate = dateResults.lastObject[expressionName];
NSLog(#"last local date results: %#",lastLocalDate);
PFQuery *query = [PFQuery queryWithClassName:parseEntityName];
query.limit = 100;
[query orderByAscending:parseDateIdentifier];
if (lastLocalDate) [query whereKey:parseDateIdentifier greaterThan:lastLocalDate]; // else query all
objects = [query findObjects];
[objects enumerateObjectsUsingBlock:^(PFObject *obj, NSUInteger idx, BOOL *stop) {
ForexHistory *forexHistory = [NSEntityDescription insertNewObjectForEntityForName:localEntityName
inManagedObjectContext:moc];
forexHistory.date = NULL_TO_NIL(obj[#"date"]);
[currencies enumerateObjectsUsingBlock:^(NSString *currency, NSUInteger idx, BOOL *stop) {
[forexHistory setValue:NULL_TO_NIL(obj[currency]) forKey:currency.lowercaseString];
}];
}];
NSError *saveError = nil;
[moc save:&saveError];
if (!saveError) NSLog(#"%lu forex rates saved successfully.",(unsigned long)objects.count);
else NSLog(#"Error when downloading historic forex rates: %#",error.localizedDescription);
} while (objects.count > 0);
}];
}
Thank you for your help!
Unfortunately, this is not possible. See the documentation of setIncludesPendingChanges::
Special Considerations
A value of YES is not supported in conjunction with the result type
NSDictionaryResultType, including calculation of aggregate results
(such as max and min). For dictionaries, the array returned from the
fetch reflects the current state in the persistent store, and does not
take into account any pending changes, insertions, or deletions in the
context.
Include pending changes: Insert [request setIncludePendingChanges:YES]; before you do the fetch.
This ensures that unsaved changes are included in the fetch.
I have a core data entity called "TruckNumber" which has a string as it's only property. The string is usually a 1-3 digit integer (as a string) but sometimes can have letters such as TMP9. The name of the property is "itsNotANumma". I am doing a fetch request to populate a picker, but they are not being sorted and I don't know why. I've used this exact technique on other entities for other pickers and never had a problem. Please help... Here's the relevant code:
// Fetch truck numbers
NSFetchRequest *truckNumberFetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *truckEntity = [NSEntityDescription entityForName:#"TruckNumber" inManagedObjectContext:self.managedObjectContext];
[truckNumberFetchRequest setEntity:truckEntity];
// Sort Descriptor
NSSortDescriptor *truckDescriptor = [[NSSortDescriptor alloc] initWithKey:#"itsNotANumma" ascending:YES];
NSArray *truckSortDescriptors = [[NSArray alloc] initWithObjects:truckDescriptor, nil];
[inventoryFetchRequest setSortDescriptors:truckSortDescriptors];
error = nil;
NSArray *truckResults = [managedObjectContext executeFetchRequest:truckNumberFetchRequest error:&error];
if (error)
NSLog(#"Unresolved error while saving context: %#, %#", error, [error userInfo]);
truckNumbersArray = [[NSMutableArray alloc] init];
for (TruckNumber *truckNumber in truckResults)
{
[truckNumbersArray addObject:truckNumber.itsNotANumma];
}
Here is my data model for truck number:
These are the results:
Looks like you have a simple typo:
[inventoryFetchRequest setSortDescriptors:truckSortDescriptors];
Should be:
[truckNumberFetchRequest setSortDescriptors:truckSortDescriptors];
I would assume anyway, from reading that code.
[NSSortDescriptor sortDescriptorWithKey:#"self" ascending:YES comparator:^(id obj1, id obj2){
return [(NSString*)obj1 compare:(NSString*)obj2 options:NSNumericSearch];
}];
It works so perfectly if you have numbers in a range from 1-5000 or onwards.