Deleting all HKQuantitySamples - ios

I want to delete all HKQuantitySamples that my app has saved in the Health App for a certain HKQuantityType, how would I do that?
I can see the function
deleteObject:withCompletion:
in the Apple Documentation, but I do not really understand how to use it. Can somebody show an example maybe?
EDIT: I now use the following code for deleting:
I have saved my food information as a HKCorrelation and set my Local Food ID in the Correlations metadata HKMetadataKeyExternalUUID key.
For deletion I am fetching all the HKCorrelation objects between startDate and endDate and then if one of these fetched objects matches the Local Food ID I am looking for:
- I delete every object in that Correlation,
- Followed by deleting the Correlation itself
HKCorrelationType *foodType = [HKObjectType correlationTypeForIdentifier:HKCorrelationTypeIdentifierFood];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:foodType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:nil resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if (!results) {
NSLog(#"An error occured fetching the user's tracked food. In your app, try to handle this gracefully. The error was: %#.", error);
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
for (HKCorrelation *foodCorrelation in results) {
if([[foodCorrelation.metadata valueForKey:HKMetadataKeyExternalUUID] isEqualToString:food_id_I_want_to_delete]) {
NSSet *objs = foodCorrelation.objects;
for (HKQuantitySample *sample in objs) {
[self.healthStore deleteObject:sample withCompletion:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"Success. delete sample");
}
else {
NSLog(#"delete: An error occured deleting the sample. In your app, try to handle this gracefully. The error was: %#.", error);
}
}];
}
[self.healthStore deleteObject:foodCorrelation withCompletion:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"Success. delete %#", [foodCorrelation.metadata valueForKey:HKMetadataKeyExternalUUID]);
}
else {
NSLog(#"delete: An error occured deleting the Correlation. In your app, try to handle this gracefully. The error was: %#.", error);
}
}];
return;
}
}
});
}];
[self.healthStore executeQuery:query];

Related

HKObserverQuery in Health Kit

In which format does HKObserverQuery gets data?
There is no NSArray or NSDictionary to store data then how to get.
I am putting the query put not able to get all data, anyone please help?
HKObserverQuery *query =
[[HKObserverQuery alloc]
initWithSampleType:object
predicate:Predicate
updateHandler:^(HKObserverQuery *query,
HKObserverQueryCompletionHandler completionHandler,
NSError *error) {
[HKUnit unitFromString:#"count/min"]];
if (error) {
// Perform Proper Error Handling Here...
NSLog(#"*** An error occured while setting up the stepCount observer. %# ***",
error.localizedDescription);
abort();
}
else{
NSLog(#"#%",query);
}
}];
HKObserverQuery is not intended to deliver data to your app. The updateHandler is simply called whenever a HKSample matching your predicate is added or removed from HealthKit. It is then up to your app to perform additional queries in response, such as HKSampleQuery or HKStatisticsQuery.

Can I keep reading steps with HealthKit?

The purpose is trigger a method when the user walks the required steps.
here is my code:
if ([HKHealthStore isHealthDataAvailable]) {
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) {
__block double stepsCount = 0.0;
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 && results>0) {
for (HKQuantitySample *result in results) {
stepsCount += [result.quantity doubleValueForUnit:[HKUnit countUnit]];
}
}
}];
[self.healthStore executeQuery:sampleQuery];
double currentSteps = stepsCount;
while (1) {
[self.healthStore stopQuery:sampleQuery];
[self.healthStore executeQuery:sampleQuery];
if (currentSteps + requiredSteps >= stepsCount) {
[self triggerOneMethod];
break;
}
}
}
}];
}
But when I run the app, Xcode shows:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: 'You cannot start a query that is already active'
***
I have read the HealthKit Document, it says that
HealthKit executes queries asynchronously on a background queue. Most
queries automatically stop after they have finished executing.
and stopQuery: is to stop a long-running query.
I think these two points are what really matter.
Is it possible to achieve the purpose? If so, how can I fix it?
In the loop, you must create a new HKSampleQuery before calling executeQuery:. You cannot reuse an HKQuery instance.

Parse- [query findObjectsInBackgroundWithBlock:^.)]; Block Doesn't execute w/ Action Extension

I'm trying to develop and Action Extension for iOS9.1 that is supposed to query Parse for some data.
I've added, enabled and tested Parse in the extension and I'm successful at creating test objects and checking for current user.
I can not get the code inside Parse's query method
[query findObjectsInBackgroundWithBlock:^ to execute. LLDB just keeps skipping it so I'm really at a loss.
This code executes perfectly within the container app so I'm a bit confused.
- (void)viewDidLoad
{
[super viewDidLoad];
[Parse enableLocalDatastore];
[Parse enableDataSharingWithApplicationGroupIdentifier:#"group.com.app.slinky"
containingApplication:#"com.app.Slinky"];
[Parse setApplicationId:#"xxxxx"
clientKey:#"xxxxx"];
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *itemProvider in item.attachments) {
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypePropertyList]) {
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypePropertyList options:nil completionHandler:^(NSDictionary *jsDict, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *jsPreprocessingResults = jsDict[NSExtensionJavaScriptPreprocessingResultsKey];
NSString *pageTitle = jsPreprocessingResults[#"title"];
NSString *pageURL = jsPreprocessingResults[#"URL"];
if ([pageURL length] > 0) {
self.siteURL.text = pageURL;
self.URLstring = pageURL;
}
if ([pageTitle length] > 0) {
self.siteTitle.text = pageTitle;
}
});
}];
break;
}
}
}
[self queryParse];
}
-(void)queryParse{
PFQuery *query = [self.friendsRelation query];
[query orderByAscending:#"username"];
**[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"%# %#", error, [error userInfo]);
} else {
self.friends = objects;
[self.tableView reloadData];
}
}];**
}
There are some limit to fetch Objects from Parse. Read Here
One has a limit of obtaining 100 objects, but anything from 1 to 1000 has a valid limit.
You have to set the limit again for the query for next objects and skip the properties of PFQuery.
You will have to query again and again until you reach the total count.
For Example - If you have 3000 objects to be fetched, then you have to query 3 times with a count of say 1000.
OR
Call [self queryParse]; inside dispatch_async(dispatch_get_main_queue(), ^{ }); block.
Hope this might workout.
This was just a result of a 12 hour refractoring marathon :-(
When I moved the queryParse call into its own method. I lost the definition of
self.friendsRelation = [[PFUser currentUser] objectForKey:#"friendsRelation"];
essentially sending a bad query... I don't know why it isn't return an error but regardless I can check that off now.
Thank you all for you help!

creatorUserRecordID.recordName contains "__defaultOwner__" instead of UUID shown in Dashboard

Downloading a CKRecord from CloudKit and when plotting creator recordName, I can see this:
(lldb) po record.creatorUserRecordID.recordName
__defaultOwner__
but, Dashboard show a real value.
Why the difference?!
I hope I do not have to download only because of this the logged in user first?!
__defaultOwner__ mean's it's owned by the currently logged in iCloud account. So you could could check for that and display "Me" or the person's name if you have it. If you need to find out the logged-in user's recordID you can use the async method: fetchUserRecordIDWithCompletionHandler.
it is a bug
edit this:
- (void)postMoodFeed:(NSString *)moodFeed
{
CKRecord *moodRecord = [[CKRecord alloc] initWitenter code herehRecordType:#"Mood"];
moodRecord[#"moodFeed"] = moodFeed`enter code here`
[[[CKContainer defaultContainer] publicCloudDatabase] saveRecord:moodRecord completionHandler:^(CKRecord *record, NSError *error) {
[self queryMyMood];
}];
}
- (void)queryMyMood
{
// currentUserRecordID is fetched from fetchUserRecordIDWithCompletionHandler: of CKContainer
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"creatorUserRecordID = %#", currentUserRecordID];
CKQuery *query = [[CKQuery alloc] initWithRecordType:#"Mood" predicate:predicate];
[[[CKContainer defaultContainer] publicCloudDatabase] performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) {
if (results) {
for (CKRecord *eachRecord in results) {
// Following logs are all __defaultOwner__
NSLog(#"%#", eachRecord.creatorUserRecordID.recordName);
[[[CKContainer defaultContainer] publicCloudDatabase]fetchRecordWithID:eachRecord.creatorUserRecordID completionHandler:^(CKRecord *record, NSError *error) {
// All following logs are "Unknown item" error
NSLog(#"%#", error);
}];
}
}
}];
}

CloudKit: creatorUserRecordID of CKRecord Issue (or Bug?)

After iOS 8.3, if the record is created by the current account, its creatorUserRecordID will be like
CKRecordID: [Some Address]; defaultOwner:(_defaultZone:defaultOwner)
And then if fetch this recordID using fetchRecordWithID:completionHandler: from CKDatabase, it will always return error like
CKError [Some Address]: "Unknown Item" (11/2003); server message = "Record not found"; uuid = [Some UUID]; container ID = [Some Container ID]
I never encounter this issue before.
Is it a bug, or should I fetch record from recordID like this ( defaultOwner ) in other way?
EDIT (add sample code)
- (void)postMoodFeed:(NSString *)moodFeed
{
CKRecord *moodRecord = [[CKRecord alloc] initWithRecordType:#"Mood"];
moodRecord[#"moodFeed"] = moodFeed
[[[CKContainer defaultContainer] publicCloudDatabase] saveRecord:moodRecord completionHandler:^(CKRecord *record, NSError *error) {
[self queryMyMood];
}];
}
- (void)queryMyMood
{
// currentUserRecordID is fetched from fetchUserRecordIDWithCompletionHandler: of CKContainer
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"creatorUserRecordID = %#", currentUserRecordID];
CKQuery *query = [[CKQuery alloc] initWithRecordType:#"Mood" predicate:predicate];
[[[CKContainer defaultContainer] publicCloudDatabase] performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) {
if (results) {
for (CKRecord *eachRecord in results) {
// Following logs are all __defaultOwner__
NSLog(#"%#", eachRecord.creatorUserRecordID.recordName);
[[[CKContainer defaultContainer] publicCloudDatabase]fetchRecordWithID:eachRecord.creatorUserRecordID completionHandler:^(CKRecord *record, NSError *error) {
// All following logs are "Unknown item" error
NSLog(#"%#", error);
}];
}
}
}];
}
EDIT July 2, 2015
That is a bug.
After reporting to Apple, they fixed this issue in iOS 9 Beta 2.
Indeed it looks like new functionality.
What you could do is first testing if the eachRecord.creatorUserRecordID.recordName == "defaultOwner" and if that's the case you could fetch the record for the ID that you got from the currentUserRecordID
But it would be better to not use the creatorUserRecordID for any functionality in your app. You could better add a new CKReference field and always fill it with the currentUserRecordID. Then even if you have a process that migrates data, you would still know who created that record originally.
Maybe this will help: I've found that if you use [CKFetchRecordsOperation fetchCurrentUserRecordOperation] at the beginning of your app's workflow you won't have defaultOwner CKRecordIDs come out of nowhere.

Resources