Is HealthKit's verbose and convoluted API driving you mad? Quick! You need a medic!
- Add this line to your application's Gemfile:
gem 'medic'
- Add the following lines to your Rakefile:
app.entitlements['com.apple.developer.healthkit'] = true
app.frameworks += ['HealthKit']
- Run
bundle
andrake
.
To request authorization to read or share (i.e. write) a data type implement the following in viewDidAppear
of your UIViewController
:
if Medic.available?
types = { share: :step_count, read: [:step_count, :date_of_birth] }
Medic.authorize types do |success, error|
NSLog "Success!" if success
end
end
This will open the permissions modal. success
will be false
if the user canceled the prompt without selecting permissions; true
otherwise.
You can subsequently check if you're authorized to share a data type:
Medic.authorized?(:step_count)
Note: For privacy reasons Apple does not allow you to check if you're authorized to read data types.
Coming soon...
HealthKit provides a number of methods for accessing its data, mostly in the form of query objects with verbose initializers that return more HKObject
s with repetitive method names. For example, if I wanted to get the total number of steps taken per day for the last week I could use a HKStatisticsCollectionQuery
like so:
@store = HKHealthStore.new
today = NSDate.date
one_week_ago = NSCalendar.currentCalendar.dateByAddingComponents(NSDateComponents.new.setDay(-7), toDate: today, options: 0)
query = HKStatisticsCollectionQuery.initWithQuantityType(
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount),
quantitySamplePredicate: nil,
options: HKStatisticsOptionCumulativeSum,
anchorDate: one_week_ago,
intervalComponents: (NSDateComponents.new.day = 1)
)
query.initialResultsHandler = ->(query, results, error){
results.enumerateStatisticsFromDate(one_week_ago, toDate: today, withBlock: ->(result, stop){
if quantity = result.sumQuantity
NSLog quantity.doubleValueForUnit(HKUnit.countUnit).to_s
end
})
}
@store.executeQuery(query)
This doesn't make for the most readable code. As a Ruby developer you might find this downright distasteful. Let's check out the Medic equivalent:
options = { options: :sum, anchor: :one_week_ago, interval: :day}
Medic.find_statistics_collection :step_count, options do |statistics|
statistics.each do |stats|
NSLog stats[:sum].to_s
end
end
Now that's more like it! Instead of constructing HKObjectType
by hand we can now just pass in a symbol. We also don't have to work directly with HKStatisticsCollection
anymore. The result is parsed into an array of hashes with reasonable values for us.
Medic provides a finder for each class of HKQuery
:
Provide an easy way to search for new data in the HealthKit store.
@anchor = nil
Medic.find_anchored :step_count, anchor: @anchor do |results, new_anchor|
@anchor = new_anchor
results.each do |sample|
NSLog sample.to_s
end
end
Search for correlations in the HealthKit store.
high_cal = HKQuantity.quantityWithUnit(HKUnit.kilocalorieUnit, doubleValue: 800.0)
greater_than_high_cal = HKQuery.predicateForQuantitySamplesWithOperatorType(NSGreaterThanOrEqualToPredicateOperatorType, quantity: high_cal)
sample_predicates = { dietary_energy_consumed: greater_than_high_cal }
Medic.find_correlations :food, sample_predicates: sample_predicates do |correlations|
correlations.each do |correlation|
NSLog correlation.to_s
end
end
Set up a long-running task on a background queue.
Medic.observe :step_count do |completion, error|
Medic.find_sources :step_count do |sources|
sources.each do |source|
NSLog source
end
end
end
Search for sample data in the HealthKit store.
Medic.find_samples :blood_pressure, sort: :start_date, limit: 7 do |samples|
samples.each do |sample|
NSLog sample.to_s
end
end
Search for the sources (apps and devices) that have saved data to the HealthKit store.
Medic.find_sources :step_count do |sources|
sources.each do |source|
NSLog source
end
end
Perform statistical calculations over the set of matching quantity samples.
Medic.find_statistics :step_count, options: :sum do |statistics|
NSLog statistics.to_s
end
Perform multiple statistics queries over a series of fixed-length time intervals.
options = { options: :sum, anchor: :one_week_ago, interval: :day}
Medic.find_statistics_collection :step_count, options do |statistics|
statistics.each do |stats|
NSLog stats[:sum].to_s
end
end
Characteristic data like biological sex or blood type have their own methods:
Medic.biological_sex # => :male
Medic.date_of_birth # => 1987-11-07 00:00:00 -0800
Medic.blood_type # => :o_negative
If for some reason you need to access the HKSample
objects directly you can use Medic's Query objects:
query_params = { type: :dietary_protein, sort_desc: :start_date, limit: 7 }
query = Medic::SampleQuery.new query_params do |query, results, error|
if results
results.each do |sample|
NSLog "#{sample.startDate} - #{sample.quantity.doubleValueForUnit(HKUnit.gramUnit)}"
end
else
NSLog "no results"
end
end
Medic.execute(query)
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request