I need to fetch the very last record in cloudkit. Here is my code:
CKContainer *container = [CKContainer containerWithIdentifier:containerID];
CKDatabase *publicDatabase = [container publicCloudDatabase];
CKQuery *query = [[CKQuery alloc] initWithRecordType:recordType
predicate:[NSPredicate predicateWithFormat:#"TRUEPREDICATE"]];
CKQueryOperation *queryOp = [[CKQueryOperation alloc] initWithQuery:query];
queryOp.desiredKeys = #[#"record.recordID.recordName"];
queryOp.recordFetchedBlock = ^(CKRecord *record)
{
//do something
};
queryOp.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error)
{
NSLog(#"CKQueryCursor error %#", error);
};
queryOp.resultsLimit = CKQueryOperationMaximumResults;
[publicDatabase addOperation:queryOp];
My question is how can I modify my code to get the very last record in cloudkit?
I'll really appreciate your help
You can sort on the creation dat ascending and then just ask for 1 result like this (code is in Swift):
Adding the sort:
query.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
Limiting the result:
queryOp.resultsLimit = 1
Objective-C version
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:NO];
query.sortDescriptors = #[sortDescriptor];
for one record:
queryOp.resultsLimit = 1;
Setting resultsLimit does not seem to work for me, however set it anyway and sorting the results using a timestamp or the record creation date. Then store the results in an array and simply use the first or last item depending on the sort order
CKContainer *container = [CKContainer containerWithIdentifier:containerID];
CKDatabase *publicDatabase = [container publicCloudDatabase];
CKQuery *query = [[CKQuery alloc] initWithRecordType:recordType predicate:[NSPredicate predicateWithFormat:#"TRUEPREDICATE"]];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:NO];
query.sortDescriptors = #[sortDescriptor];
CKQueryOperation *queryOp = [[CKQueryOperation alloc] initWithQuery:query];
queryOp.desiredKeys = #[#"record.recordID.recordName"];
queryOp.recordFetchedBlock = ^(CKRecord *record)
{
//do something
recordArray.append(record)
};
queryOp.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error)
{
NSLog(#"CKQueryCursor error %#", error);
let myLastRecord = recordArray[recordArray.count - 1]
};
queryOp.resultsLimit = CKQueryOperationMaximumResults;
[publicDatabase addOperation:queryOp];
Related
I would like to download the step and distance data collected by the motion processor in the IPhone 5S (and later), and available in Apple's HealthKit, for analysis.
What's the easiest/best way to do this?
And clarifying (after new answers): is there any way to do it without writing a new iOS app? Are there any existing apps that provide the data, and/or any iCloud API that provides access.
I'm not sure it can help you but this is how I get steps
+ (void)readUsersStepFromHK:(NSDate*)startDate end:(NSDate*)endDate
{
stepBegin=startDate;
stepEnd=endDate;
if ([HKHealthStore isHealthDataAvailable])
{
HKUnit *unit = [HKUnit countUnit];
HKQuantityType *stepCountType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
[self fetchMostRecentDataOfQuantityType:stepCountType withCompletion:^(HKQuantity *mostRecentQuantity, NSError *error) {
if (!mostRecentQuantity)
{
//Either an error
}
else
{
double temCout=[mostRecentQuantity doubleValueForUnit:unit];
coutStep=temCout;
}
}];
}
}
+ (void)fetchMostRecentDataOfQuantityType:(HKQuantityType *)quantityType withCompletion:(void (^)(HKQuantity *mostRecentQuantity, NSError *error))completion {
NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
//=======
NSDate *startDate, *endDate; // Whatever you need in your case
startDate=stepBegin;
endDate=stepEnd;
// Your interval: sum by hour
NSDateComponents *intervalComponents = [[NSDateComponents alloc] init];
intervalComponents.hour = 1;
// Example predicate
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];
// Since we are interested in retrieving the user's latest sample, we sort the samples in descending order, and set the limit to 1. We are not filtering the data, and so the predicate is set to nil.
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:quantityType predicate:predicate limit:100 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);
}
}];
[healthStore executeQuery:query];
}
hop this help !
if (NSClassFromString(#"HKHealthStore") && [HKHealthStore isHealthDataAvailable])
{
// Add your HealthKit code here
HKHealthStore *healthStore = [[HKHealthStore alloc] init];
// Share body mass, height and body mass index etc....
NSSet *shareObjectTypes = [NSSet setWithObjects:
[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass],
[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight],
[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning],
[HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount],
nil];
// Read date of birth, biological sex and step count etc
NSSet *readObjectTypes = [NSSet setWithObjects:
[HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierDateOfBirth],
[HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBiologicalSex],
[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning],
[HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount],
nil];
HKQuantityType *type = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
// Request access
[healthStore requestAuthorizationToShareTypes:shareObjectTypes
readTypes:readObjectTypes
completion:^(BOOL success, NSError *error) {
if(success == YES)
{
//[healthStore ];
//NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];
// NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:YES];
NSSortDescriptor *timeSortDescription = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:type
predicate:nil
limit:HKObjectQueryNoLimit
sortDescriptors:#[timeSortDescription]
resultsHandler:^(HKSampleQuery *query, NSArray *result, NSError *error){
NSLog(#"RESULT : => %#",result);
if(!error && result)
{ long totalSteps=0;
for(HKQuantitySample *quantitySample in result)
{
// your code here
HKQuantity *quantity=quantitySample.quantity;
//HKQuantity *quantity = quantitySample.quantity;
NSString *string=[NSString stringWithFormat:#"%#",quantity];
NSString *newString1 = [string stringByReplacingOccurrencesOfString:#" count" withString:#""];
NSInteger count=[newString1 integerValue];
totalSteps=totalSteps+count;
}
//using total steps
}
}];
[healthStore executeQuery:query];
}
else
{
// Determine if it was an error or if the
// user just canceld the authorization request
//Fit_AAPLprofileviewcontroller_m.html
}
}];
}
You can perform a simple query for steps (and any other samples stored in HealthKit) using a HKSampleQuery. If you would like HealthKit to aggregate the samples for you, you could use a HKStatisticsQuery or HKStatisticsCollectionQuery instead. Before querying for the user's HealthKit data, you will need to ask for permission to access it with -[HKHealthStore
requestAuthorizationToShareTypes:readTypes:completion:
].
For a general introduction to writing applications that integrate with HealthKit, I recommend that you watch the WWDC talk.
This is how i am inserting the data,
NSEntityDescription * entityDescription = [NSEntityDescription entityForName:[DOSnow entityDescription] inManagedObjectContext: proxy.managedObjectContext];
DOCurrentCondition *doSnow = [[[DOSnow alloc] initWithEntity:entityDescription insertIntoManagedObjectContext: proxy.managedObjectContext] autorelease];
NSXMLElement *snowConditionsElement = [[roseElement elementsForName:SNOW] lastObject];
NSArray *snowElements = [snowConditionsElement children];
for (NSXMLElement *snowElement in snowElements)
{
NSEntityDescription * entityDescription = [NSEntityDescription entityForName:[DOPair entityDescription] inManagedObjectContext: proxy.managedObjectContext];
DOPair *pair = [[[DOPair alloc] initWithEntity:entityDescription insertIntoManagedObjectContext: proxy.managedObjectContext] autorelease];
pair.key = [snowElement name];
pair.value = [snowElement stringValue];
[doSnow addConditionsObject: pair];
}
[proxy save];
And this is how i am fetching the data,
- (NSArray *) fetchSnowConditions
{
ApplicationFacade *appFacade = [ApplicationFacade appFacade];
NSManagedObjectContext *context = appFacade.rProxy.managedObjectContext;
NSFetchRequest * request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:[DOSnow entityDescription] inManagedObjectContext:context]];
NSError *error;
NSArray *result = [context executeFetchRequest:request error:&error];
return result;
}
So i am not getting the data in same order as i inserted.
You have to use [NSFetchRequest setSortDescriptor:] to get a meaningful ordering of your results. The NSFetchRequest documentation doesn't say anything about the default order of the results, so it's not a good idea to assume there is any.
Of course, in order to correctly specify the sort descriptor, you probably need to add a field to your managed objects to sort on, and assign a value to it when creating the objects. It could be an incrementing index field, a creation date, or something like that.
I'm parsing data from JSON webservice and then using the following code to sort the data by price, date, discount etc.
here's the code I'm using to sort the data:
-(void)priceSort:(id)sender {
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey: #"price" ascending: YES];
NSMutableArray *sortedArray = (NSMutableArray *)[self.displayItems
sortedArrayUsingDescriptors: [NSArray arrayWithObject:sortDescriptor]];
[self setDisplayItems:sortedArray];
[self.tableView reloadData];
}
this works fine when I'm trying to sort by price, however when I want to sort by number of reviews I can't seem to get the code right:
for price I use:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey: #"price" ascending: YES];
but for reviews I want to get the "n" value. see how it's nested in the output (of the display Items mutableArray below:
"old_price" = 24;
price = "9.9";
reviews = {
**n = 11;**
val = 70;
};
"sold_count" = 101;
thanks for any help on this :)
To sort by the number of reviews n, your sort descriptor would be:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey: #"reviews.n" ascending: YES];
I have a entity Person with a property personId (personId is unique)
How can I fetch the Person with the max personId?
(I want to fetch the person itself not the value of the property)
You set the fetchLimit to 1 and sort by personId in descending order. E.g.:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Person"];
fetchRequest.fetchLimit = 1;
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"personId" ascending:NO]];
NSError *error = nil;
id person = [managedObjectContext executeFetchRequest:fetchRequest error:&error].firstObject;
You need to use a NSFetchRequest with a NSPredicate to specify your query...
Adapted from Apple's Predicate Progamming Guide :
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person"
inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
request.predicate = [NSPredicate predicateWithFormat:#"personId==max(personId)"];
request.sortDescriptors = [NSArray array];
NSError *error = nil;
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
The recommended way is to use Apple Recommended Method NSExpression. I would expect that this would be less expensive than using a sort.If you think about it, with a sort you would have to take all the records sort them and keep the maximum one. With an expression you would just have to read through the list and keep in memory the maximum.
Here is an example I use with NSDate
- (NSDate *)lastSync:(PHAssetMediaType)mediaType {
NSEntityDescription *entity = [NSEntityDescription entityForName:kMediaItemEntity inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = entity;
fetchRequest.resultType = NSDictionaryResultType;
NSMutableArray *predicates = [NSMutableArray array];
[predicates addObject:[NSPredicate predicateWithFormat:#"%K=%d", kMediaType,mediaType]];
[predicates addObject:[NSPredicate predicateWithFormat:#"%K=%d", kMediaProviderType,self.mediaProviderType]];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates: predicates];
fetchRequest.predicate = predicate;
// Create an expression for the key path.
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:kSyncTime];
// Create an expression to represent the function you want to apply
NSExpression *maxExpression = [NSExpression expressionForFunction:#"max:"
arguments:#[keyPathExpression]];
// Create an expression description using the maxExpression and returning a date.
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName:#"maxDate"];
[expressionDescription setExpression:maxExpression];
[expressionDescription setExpressionResultType:NSDateAttributeType];
// Set the request's properties to fetch just the property represented by the expressions.
fetchRequest.propertiesToFetch = #[expressionDescription] ; // #[kSyncTime];
NSError *fetchError = nil;
id requestedValue = nil;
// fetch stored media
NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&fetchError];
if (fetchError || results == nil || results.count == 0) {
return [NSDate dateWithTimeIntervalSince1970:0];
}
requestedValue = [[results objectAtIndex:0] valueForKey:#"maxDate"];
if (![requestedValue isKindOfClass:[NSDate class]]) {
return [NSDate dateWithTimeIntervalSince1970:0];
}
DDLogDebug(#"sync date %#",requestedValue);
return (NSDate *)requestedValue;
}
The answer given above using NSExpression is correct. Here is the Swift version.
private func getLastSyncTimestamp() -> Int64? {
let request: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest()
request.entity = NSEntityDescription.entity(forEntityName: "EntityName", in: self.moc)
request.resultType = NSFetchRequestResultType.dictionaryResultType
let keypathExpression = NSExpression(forKeyPath: "timestamp")
let maxExpression = NSExpression(forFunction: "max:", arguments: [keypathExpression])
let key = "maxTimestamp"
let expressionDescription = NSExpressionDescription()
expressionDescription.name = key
expressionDescription.expression = maxExpression
expressionDescription.expressionResultType = .integer64AttributeType
request.propertiesToFetch = [expressionDescription]
var maxTimestamp: Int64? = nil
do {
if let result = try self.moc.fetch(request) as? [[String: Int64]], let dict = result.first {
maxTimestamp = dict[key]
}
} catch {
assertionFailure("Failed to fetch max timestamp with error = \(error)")
return nil
}
return maxTimestamp
}
where moc is a NSManagedObjectContext.
Swift 3
let request:NSFetchRequest = Person.fetchRequest()
let sortDescriptor1 = NSSortDescriptor(key: "personId", ascending: false)
request.sortDescriptors = [sortDescriptor1]
request.fetchLimit = 1
do {
let persons = try context.fetch(request)
return persons.first?.personId
} catch {
print(error.localizedDescription)
}
SWIFT 4
let request: NSFetchRequest<Person> = Person.fetchRequest()
request.fetchLimit = 1
let predicate = NSPredicate(format: "personId ==max(personId)")
request.predicate = predicate
var maxValue: Int64? = nil
do {
let result = try self.context.fetch(request).first
maxValue = result?.personId
} catch {
print("Unresolved error in retrieving max personId value \(error)")
}
In addition to Ryan's answer, in Swift today, NSManagedObject's execute(_:) returns a NSPersistentStoreResult object, which need some extra code to retrieve the value:
// Cast `NSPersistentStoreResult` to `NSAsynchronousFetchResult<NSDictionary>`
let fetchResult = moc.execute(request) as! NSAsynchronousFetchResult<NSDictionary>
// Retrieve array of dictionary result
let dictsResult = fetchResult.finalResult
// Retrieve and cast the real result
let key = /* `expressionDescription.name` */
let result = dictsResult.first!.object(forKey: key) as! /* Your type, depending on `expressionDescription.expressionResultType` */
Note: Force unsafe type cast are used above to simplify code, in real case scenario, you should always avoid this.
I'm having NSDictinary objects array.
Each dictionary object has keys "display_name", "first_name" and "last_name".
Some dict objects have only display_name and some will not have.
Some dict objects have only first_name and some will not have.
Some dict objects have only last_name and some will not have.
I'm using this array to show the list in table view. What I am looking for is to sort the dict with following preference:
1. If display name is available, use that.
2. If display name is not available and first name is available, use that.
3. else last name.
How can I sort the array using above preference. I want to use NSPredicate the app has to work on older iOS as well....
I tried different combinations of NSPredicate as following, but I didn't succeeed:
NSSortDescriptor* firstNameDescriptor;
NSSortDescriptor* lastNameDescriptor;
NSSortDescriptor* displayNameDescriptor;
displayNameDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"display_name" ascending:YES];
lastNameDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"last_name" ascending:YES];
firstNameDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"first_name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:firstNameDescriptor, lastNameDescriptor,nil];
self.contactsArray = (NSMutableArray*)[tempArray sortedArrayUsingDescriptors:sortDescriptors];
Can some one guide me in right way to achieve it?
you can use :
sortedArrayUsingFunction:context:
and implement the rules you just listed in your own custom sorting function
What I did is that during sorting, I added another key to the dictionary "final_name" and the value is set according to my preference of names to display and just sorted the array with "final_name".
NSArray* tempArray = [jsonData objectForKey:#"contacts"];
for (NSDictionary* conDict in tempArray)
{
NSString* fName = [conDict objectForKey:#"first_name"];
NSString* lName = [conDict objectForKey:#"last_name"];
NSString* dName = [conDict objectForKey:#"display_name"];
NSString* finalName = #"<<No Name>>";
if (dName && ![dName isEqual:[NSNull null]]) {
finalName = dName;
}
else if (fName && ![fName isEqual:[NSNull null]] && lName && ![lName isEqual:[NSNull null]])
{
finalName = [NSString stringWithFormat:#"%# %#",fName,lName];
}
else if (fName && ![fName isEqual:[NSNull null]])
{
finalName = fName;
}
else if (lName && ![lName isEqual:[NSNull null]]) {
finalName = lName;
}
[conDict setValue:finalName forKey:#"final_name"];
}
if ([tempArray count])
{
NSSortDescriptor* finalSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"final_name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:finalSortDescriptor,nil];
self.contactsArray = [[NSArray alloc] initWithArray:[tempArray sortedArrayUsingDescriptors:sortDescriptors]];
}