1

I want to query samples from HealthKit but in order to prevent inaccurate or manipulated data I don't want samples that were written to health by other apps. Does anyone have any idea what predicate I can use to filter out data from all apps or to only allow data from devices? Thanks in advance.

Edit: I've realized that apps can save data to health with an HKDevice included. So filtering out samples that don't have devices won't work.

Myk
  • 979
  • 6
  • 16

3 Answers3

1

If what you want to do is exclude manually entered data, see this answer: Ignore manual entries from Apple Health app as Data Source

Samples that were added to HealthKit by the user via Health will have the HKMetadataKeyWasUserEntered key.

Allan
  • 7,039
  • 1
  • 16
  • 26
  • I do want to exclude manual data thanks, but this post was more about excluding data from apps. I want to be able to ignore samples from an app that simulates heart rate for example. – Myk Jun 22 '18 at 19:55
  • Do you have specific examples of apps that simulate heart rate? Besidse manual entry, what would the purpose be of an app that stores data that was not recorded by a sensor in HealthKit? If you’re worried about cheating in some kind of competition, there’s nothing preventing apps that are simulating data from using the local HKDevice for their data. – Allan Jun 22 '18 at 23:38
  • No I don't have any examples, but in my own app I'm able to store heart rate samples directly into health with just a Double. I haven't put much thought into what legitimate purpose such an app could have but right off the top of my head I can imagine one would be useful for debugging purposes. I AM worried about cheating actually, and you're right that simulated data could have the local HKDevice. As you can see from my edited answer though I've added a check for the "com.apple.health" bundle identifier. I assume apps are unable to simulate apples bundle id but correct me if I'm wrong. – Myk Jun 22 '18 at 23:50
1

You can filter out the results that are not stored by apple in your query instead of iterating all the results.

first, you need to get all the sources for your desired type.

let query = HKSourceQuery(sampleType: type,
                                  samplePredicate: predicate) {
                                    query, sources, error in
                                    
                                    // error handling ...
                                    
                                    // create a list of your desired sources
                                    let desiredSources = sources?.filter { 
                                          !$0.bundleIdentifier.starts(with: "com.apple.health")
                                     }
                                    
                                    // now use that list as a predicate for your query
                                    let sourcePredicate = HKQuery.predicateForObjects(from: desiredSources!)

                                    // use this predicate to query for data

        }
        

you can also combine other predicates using NSCompoundPredicate

Mohammad Rahchamani
  • 5,002
  • 1
  • 26
  • 36
0

I'm still open to suggestions and alternate solutions but here is my work-around since I was unable to figure out how to use a predicate to get the job done.

 let datePredicate = HKQuery.predicateForSamples(withStart:Date(), end: nil, options: [])

 let sampleQuery = HKAnchoredObjectQuery(type: sampleType,
                                               predicate: predicate,
                                               anchor: nil,
                                               limit: Int(HKObjectQueryNoLimit)) { query,
                                                                                   samples,
                                                                                   deletedObjects,
                                                                                   anchor,
                                                                                   error in
        if let error = error {
            print("Error performing sample query: \(error.localizedDescription)")
            return
        }

        guard let samples = samples as? [HKQuantitySample] else { return }

        // this line filters out all samples that do not have a device
        let samplesFromDevices = samples.filter { 
            $0.device != nil && $0.sourceRevision.source.bundleIdentifier.hasPrefix("com.apple.health") 
        }
        doStuffWithMySamples(samplesFromDevices)
    }

As you can see, I just filter the data once it comes through rather than doing it before-hand.

Edit: Seems like the sources listed in health are separated into apps and actual devices. Not 100% sure how they do this but it seems like the sources under the device section all have a bundle identifier prefixed with com.apple.health. Hopefully this works.

Myk
  • 979
  • 6
  • 16