Yesterdays tutorial was about the HKActivitySummaryQuery which allows your app to seek permission from the user to access the exercise ring, and all history and details associated with it. The query is one of the simplest available in HealthKit. The only parameter you can pass in is a predicate to specify a range of dates, or a single date. If you set it to nil, it will read all activity of the user.
In todays tutorial we will look at the long-running query that is available for HKActivitySummaryQuery. This particular version of the query keeps running in the background and monitors for changes. Each time the health store is updated with new data; either stand, exercise, or active calories; the update handler will be called which will allow you to refresh your views, should you need to.
As well as looking at the query, we will also add a UIView to the storyboard and set this up as a HKActivityRingView to display the rings on the view.
Adding the HKActivityRingView to the Storyboard
In many cases you might want to manually instantiate the HKActivityRingView so that you can programatically place one or more of them on the view. For this example we’ll use the storyboard. The first step is to drag out a UIView to the view controller.
When this has been dragged out, resize it so that it’s a square box. The size doesn’t matter. It can be large or small. The HKActivityRingView will fill the available space to the smallest dimension. If you have a rectangle for the UIView, the HKActivityRingView will still be a circle, but the borders of the rectangle will be rounded.
When this is done, set the custom class to HKActivityRingView. This will need to be typed in exactly because there is no auto completion on this particular class name for some reason.
When done, you need to ctrl+drag from the HKActivityRingView to the View Controller. I inserted the outlet on line 16 just after the healthStore was declared. I called it “activityRingView”.
Now that the view is ready, you need to add some code to the View Controller class to tie it all together and make it work.
The first step is to import HealthKitUI:
import UIKit import HealthKit import HealthKitUI
Line 3 is the new line here. This lets us work with the HKActivityRingView.
The next task is to update the testActivitySummaryQuery function:
func testActivitySummaryQuery() { if #available(iOS 9.3, *) { let query = HKActivitySummaryQuery.init(predicate: nil) { (query, summaries, error) in let calendar = Calendar.current for summary in summaries! { let dateComponants = summary.dateComponents(for: calendar) let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" let date = dateComponants.date print("Date: \(dateFormatter.string(from: date!)), Active Energy Burned: \(summary.activeEnergyBurned), Active Energy Burned Goal: \(summary.activeEnergyBurnedGoal)") print("Date: \(dateFormatter.string(from: date!)), Exercise Time: \(summary.appleExerciseTime), Exercise Goal: \(summary.appleExerciseTimeGoal)") print("Date: \(dateFormatter.string(from: date!)), Stand Hours: \(summary.appleStandHours), Stand Hours Goal: \(summary.appleStandHoursGoal)") print("----------------") } } query.updateHandler = { query, summaries, error in DispatchQueue.main.async { () -> Void in self.activityRingView.setActivitySummary(summaries?.first, animated: true) } } healthStore.execute(query) } else { // Fallback on earlier versions } }
The only lines we have added here are lines 20-24. Here we set the updateHandler property of the query, and include the closure which has the query, the summaries, and an error.
Because we are updating the view, we need to dispatch on the main queue. This is how we declare this on line 21.
On line 22 we call the setActivityRingView instance method and provide it with the first item in the summaries array, which happens to be todays data. Alternatively, we could have specified a predicate earlier and just queried todays information only.
When you run the app now, you will see an empty activity ring on screen, and after all data has been processed, it will update on the view. With this being a long-running query, the activity rings will stay updated each time your watch updates active calories, exercise minutes, or stand hours.
Closing
Todays tutorial was relatively simple. The updateHandler is very similar to the regular resultsHandler, with the only difference being that on the updateHandler, you are updated each time changes are made, but with the resultsHandler you execute the query and the snapshot is taken, but not updated if changes are made. You would need to re-run the query to get the updated information using just the resultsHandler.
You can download the full project here.
Leave a Reply
You must be logged in to post a comment.