Is HKMetadataKeyWasUserEntered broken? I keep getting nil when there is data in the health app - ios

I'm trying to grab data from the Health App. Specifically data that the user did not enter in themselves. For instance, I have an iPhone 6+ that logs the amount of steps that I take. There is also an option to add the data manually; If you add the data manually, the health app marks the data as "user added".
Here's what's confusing me. Let's say I added a step count of 22. When I query the data using HKStatisticsQuery with a predicate of
HKQuery.predicateForObjectsWithMetadataKey(HKMetadataKeyWasUserEntered, allowedValues: [true])
I get the correct result of 22 steps, since I set the allowedValues to true and that I added this myself. However, when I try to set allowedValues to false, I get no results
HKQuery.predicateForObjectsWithMetadataKey(HKMetadataKeyWasUserEntered, allowedValues: [false])
I do indeed have the step data in the health app, but it returned no results.

Check for the below possible areas to fix it:
did you authorized your application to access Steps data from HealthKit?
If your quering for steps count from your application without authorizing with HealthKit, then HealthKit will not return any exception it will simply returns the steps count which was entered from your application only if available, otherwise returns nil.
Before going to query health data, check for the authorization status for steps count using authorizationStatusForType: method available with HKHealthStore class.
Update 1:
My observations on wasUserEntered key is:
If user entering steps data from HEALTH app, respective HKQuantitySample stores metadata dictionary along with HKWasUserEntered key as TRUE automatically.
If user entering steps data from other than Apple's HEALTH app, respective health/fitness device or our application should send metadata dictionary with key HKWasUserEntered along with value as either TRUE/FALSE. Otherwise, the metadata property will contain nil object. Hence, Apple is not applying predicate(predicate contains metadata key) on the data which don't have metadata with it.
For debugging this metadata, try to print your HKQuantitySampleObject.metadata
Apple's implementation on metadata Vs NSPredicate:
If Health data observed from Health/fitness devices, HealthKit is not adding the metadata dictionary to the respective health record.
In case of Health applications other than Apple's Health app, the developer should manually add the metadata dictionary for his record of health data.
If there is no metadata for a specific health record and NSPredicate have a constraint on metadata then, HealthKit completely omitting to validate such records.
Finally,
It is advised to use
(instancetype)quantitySampleWithType:(HKQuantityType *)quantityType
quantity:(HKQuantity *)quantity
startDate:(NSDate *)startDate
endDate:(NSDate *)endDate
metadata:(NSDictionary *)metadata;
instead of
+ (instancetype)quantitySampleWithType:(HKQuantityType *)quantityType
quantity:(HKQuantity *)quantity
startDate:(NSDate *)startDate
endDate:(NSDate *)endDate;
to add metadata.
Reporting Apple regarding this bug that, predicate(which contains metadata key) should be applied on all the data irrespective of checking for metadata exists or not.

It's been a while and you have probably solved this by now, but I think it is worth posting here in case someone else comes across the same issue.
Like Bhanu mentioned, data created by Apple themselves doesn't seem to have the HKWasUserEntered entry as part of its metadata.
But Apple never claims to do it anyway in their docs, so the proper query would be one that filtered out items with HKWasUserEntered == true if that key was set, but also gave back everything else that had no metadata associated.
The ideia is that metadata is a Dictionary, and that type returns nil if a key does not exist. So there are three possible scenarios:
HKMetadataKeyWasUserEntered == nil
HKMetadataKeyWasUserEntered == false
HKMetadataKeyWasUserEntered == true
Out of those, you just want to filter out the last one, since that explicitly tells you that data was entered but the user.
This can be done using the + predicateForObjectsWithMetadataKey:operatorType:value: convenience method on HKQuery, like this:
HKQuery.predicateForObjects(withMetadataKey: HKMetadataKeyWasUserEntered, operatorType: .notEqualTo, value: true)
But of course, your initial question was: "Is HKMetadataKeyWasUserEntered broken?". And I don't believe it is, since, like mentioned, Apple never claims to write that key for their own values. :)

Related

Realm Swift: Question about Query-based public database

I’ve seen all around the documentation that Query-based sync is deprecated, so I’m wondering how should I got about my situation:
In my app (using Realm Cloud), I have a list of User objects with some information about each user, like their username. Upon user login (using Firebase), I need to check the whole User database to see if their username is unique. If I make this common realm using Full Sync, then all the users would synchronize and cache the whole database for each change right? How can I prevent that, if I only want the users to get a list of other users’ information at a certain point, without caching or re-synchronizing anything?
I know it's a possible duplicate of this question, but things have probably changed in four years.
The new MongoDB Realm gives you access to server level functions. This feature would allow you to query the list of existing users (for example) for a specific user name and return true if found or false if not (there are other options as well).
Check out the Functions documentation and there are some examples of how to call it from macOS/iOS in the Call a function section
I don't know the use case or what your objects look like but an example function to calculate a sum would like something like this. This sums the first two elements in the array and returns their result;
your_realm_app.functions.sum([1, 2]) { sum, error in
if let err = error {
print(err.localizedDescription)
return
}
if case let .double(x) = result {
print(x)
}
}

Only writing data to firebase if information doesn't exist - Swift iOS

I want to only update the values email, firstname and lastname if they are blank.
I need this so that if the user decides to change these in the settings, they are not overwritten every time the user logs in with facebook.
Any solutions to check if the fields are blank without a datasnapshot? Trying to maximise efficency.
Current code when user signs in with facebook?
Database Structure for each user:
One way to do this is using a firebase transaction.
A transaction allows you to check the current value of a DB reference before you set/update it. It's main use case is preventing multiple concurrent updates from multiple sources but it can be used for this case as well - read and then write.
In the transaction block you get the value of the DB ref you're transacting on & can check that the value is null (hence 'create' case) -> then update it as required and return TransactionResult.success(withValue: newData).
If the object is already set you simply abort the transaction with TransactionResult.abort() and no write to the DB is executed.
Another option, that doesn't require a read/write, is to set a Firebase database rule on the relevant ref that will only allow write if the previous value was null:
"refPath": {
".write": "data.val() == null && newDataval() != null"
}
Writing a second time to the DB for an existing ref will fail.
I'd go with the transaction - more expressive of the requirement in the client code.
In firebase the only way you have to check if the current value of your fields in your database are empty is to fetch them before you are setting them.
You can check the field is empty only by fetching them.Then Use this code to update a particular value
ref.child("yourKey").child("yourKey").updateChildValues(["email": yourValue])

How to make group of User Defaults in swift?

I want to store group of data into user defaults so that deleting that group deletes all the data in it. For example i want to store login related data in USER_DEFAULTS_LOGIN and all subscription related data in USER_DEFAULTS_SUBSCRIPTION. Now if I delete USER_DEFAULTS_LOGIN group it does not affects USER_DEFAULTS_SUBSCRIPTION group. This was possible in android preference as creation of preference requires name. I guess there should be similar solution in ios. I have heard of 'domains' and 'suits' but does not understands there purpose. Kindly help.
UserDefaults API provides a way to set data for a key. In your example, you would define your keys to be USER_DEFAULTS_LOGIN and USER_DEFAULTS_SUBSCRIPTION. The data you associate with those keys can be one of several types - bool, number, string, URL, date, array, dictionary, or a bag-of-bytes (NSData).
See https://developer.apple.com/reference/foundation/userdefaults for more details on types and API usage.

How do I filter Purchase Order query in QBXML to only return records that are not fully received?

When doing a PurchaseOrderQuery in QBXML I am trying to get Quickbooks to only return purchase orders that are not yet processed (i.e. "IsFullyReceived" == false). The response object contains the IsFullyReceived flag, but the query object doesn't seem to have a filter for it??
This means I have to get every single Purchase Order whether or not it's received, then do the filtering logic in my application - which slows down Web Connector transactions.
Any ideas?
Thanks!
You can't.
The response object contains the IsFullyReceived flag, but the query object doesn't seem to have a filter for it??
Correct, there is no filter for it.
You can see this in the docs:
https://developer-static.intuit.com/qbSDK-current/Common/newOSR/index.html
This means I have to get every single Purchase Order whether or not it's received, then do the filtering logic in my application - which slows down Web Connector transactions.
Yep, probably.
Any ideas?
Try querying for only Purchase Orders changed or modified (ModifiedDateRangeFilter) since the last time you synced.
Or, instead of pulling every single PO, keep track of a list of POs that you think may not have been received yet, and then only query for those specific POs based on RefNumber.
Or, watch the ItemReceipt and BillPayment objects, and use that to implement logic about which POs may have been recently filled, since BillPayment andItemReceipt` objects should get created as the PO is fulfilled/received.

Any (or best) way to get records from CloudKit that aren't on the device?

I have the following predicate:
let predicate = NSPredicate(format: "NOT (recordID in %#)", recordIDs)
-- recordIDs is an array of CKRecordID objects corresponding to the CKRecords on the device
...that produces a runtime error about the predicate. If I change the predicate format string to something else, the query runs fine. I have the "Query" checkbox checked for all the metadata for this record type in CloudKit.
According to CKQuery documentation:
Key names used in predicates correspond to fields in the currently evaluated record. Key names may include the names of the record’s metadata properties such as "creationDate” or any data fields you added to the record.
According to CKRecord documentation, these are the available metadata for querying:
recordID, recordType, creationDate, creatorUserRecordID, modificationDate, lastModifiedUserRecordID, recordChangeTag
You can use the creation date:
NSPredicate(format: "creationDate > %#", dateLastFetched)
After you pull the records down to the device and save them, save the dateLastFetched and use it for subsequent fetches.
Edit: Be sure to enable the creationDate query index on the CloudKit dashboard (it is not enabled by default like many other indexes)
This is an old question, so I'm not sure if it existed at the time, but the correct way to do this now is to use Apple's built-in server change token support.
Making a giant query including all existing record ID's on the device is going to be slow, and picking a date is going to be imprecise.
The right way to do this is to use CKFetchRecordZoneChangesOperation and pass in a CKServerChangeToken using the operation's configurationsByRecordZoneID property.
The first time you call, pass a nil change token. As records are received, CloudKit will call recordZoneChangeTokensUpdatedBlock with the latest change token. Persist the latest token so the next time you need records from the server, you can just pass in your most recent token and get only what's changed since then.
Enable the meta data index by clicking here:

Resources