I'm trying to get the number of workouts performed within the week and display the count in a view and also display the workout like you see in the Apple Fitness app. I'm having issues reading the data and displaying it. I'm trying to only get functional strength training and traditional strength training workouts. Below is the code I'm using. I'm omitting the authorization method because I have that with other health variables I'm getting. Also, when I try to get the workoutActivity type to display, nothing is showing up. I've looked over apple's healthkit documentation but getting a big confused/lost as a beginner, particularly with the workout data. Any help would be appreciated
class HealthStoreViewModel: ObservableObject {
var selectedWorkoutQuery: HKQuery?
#Published var muscleStrength: [HKWorkout] = [HKWorkout]()
func getStrengthTrainingWorkouts() {
let date = Date()
let startDate = Calendar.current.dateInterval(of: .weekOfYear, for: date)?.start
let datePredicate = HKQuery.predicateForSamples(withStart: startDate, end: nil, options: .strictStartDate)
let traditionalStrengthTrainingPredicate = HKQuery.predicateForWorkouts(with: .traditionalStrengthTraining)
let functionalStrengthTrainingPredicate = HKQuery.predicateForWorkouts(with: .functionalStrengthTraining)
let strengthCompound = NSCompoundPredicate(andPredicateWithSubpredicates: [datePredicate, traditionalStrengthTrainingPredicate, functionalStrengthTrainingPredicate])
let selectedWorkoutQuery = HKSampleQuery(sampleType: HKWorkoutType.workoutType(), predicate: strengthCompound, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { strengthQuery, samples, error in
guard let samples = samples else {
fatalError("An error has occured \(error?.localizedDescription)")
}
DispatchQueue.main.async {
if let workouts = samples as? [HKWorkout] {
for workout in workouts {
self.muscleStrength.append(workout)
}
}
}
}
self.healthStore?.execute(selectedWorkoutQuery)
}
Here is the view I would like to display the count and workouts but nothing is showing
struct MuscleView: View {
#ObservedObject var healthStoreVM: HealthStoreViewModel
var body: some View {
List(healthStoreVM.muscleStrength, id: \.self) {
workout in
Text("\(workout.workoutActivityType.rawValue)")
}
}
}
refer the link here. You have this workoutActivityType property in HKWorkout objects. The error thrown in valid as you are trying to assign a HKWorkout class object into a Enum definition of HKWorkoutActivityType
self.muscleStrength.append(workouts.workoutActivityType)
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 days ago.
Improve this question
I'm looking to display the workouts performed similar to what Apple Fitness displays. However, I want to get traditional strength training and functional strength training workouts particularly. I would also like to get the number of workouts performed within the week. I'm having issues reading the data and displaying it. Below is the code I'm using. I'm omitting the authorization method because I have that with other health variables I'm getting. Also, when I try to get the workoutActivity type to display, nothing is showing up. I've looked over apple's healthkit documentation but getting a big confused/lost as a beginner, particularly with the workout data. I've attached pictures as a reference on what I would like to accomplish
.
class HealthStoreViewModel: ObservableObject {
var selectedWorkoutQuery: HKQuery?
#Published var muscleStrength: [HKWorkout] = [HKWorkout]()
func getStrengthTrainingWorkouts() {
let date = Date()
let startDate = Calendar.current.dateInterval(of: .weekOfYear, for: date)?.start
let datePredicate = HKQuery.predicateForSamples(withStart: startDate, end: nil, options: .strictStartDate)
let traditionalStrengthTrainingPredicate = HKQuery.predicateForWorkouts(with: .traditionalStrengthTraining)
let functionalStrengthTrainingPredicate = HKQuery.predicateForWorkouts(with: .functionalStrengthTraining)
let strengthCompound = NSCompoundPredicate(andPredicateWithSubpredicates: [datePredicate, traditionalStrengthTrainingPredicate, functionalStrengthTrainingPredicate])
let selectedWorkoutQuery = HKSampleQuery(sampleType: HKWorkoutType.workoutType(), predicate: strengthCompound, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { strengthQuery, samples, error in
guard let samples = samples else {
fatalError("An error has occured \(error?.localizedDescription)")
}
DispatchQueue.main.async {
if let workouts = samples as? [HKWorkout] {
for workout in workouts {
self.muscleStrength.append(workout)
}
}
}
}
self.healthStore?.execute(selectedWorkoutQuery)
}
Here is the view I would like to display the count and workouts but nothing is showing
struct MuscleView: View {
#ObservedObject var healthStoreVM: HealthStoreViewModel
var body: some View {
List(healthStoreVM.muscleStrength, id: \.self) {
workout in
Text("\(workout.workoutActivityType.rawValue)")
}
}
}
I am attempting to retrieve heart rate for all workouts completed and also start and stop times for the workout and return all values as Times or Quantity items in my JSON result. I have been able to get all workouts by querying for duration greater than 0, but now I would like to get this additional info.
let workoutPredicate = HKQuery.predicateForWorkouts(with: .greaterThanOrEqualTo, duration: 1)
let compound = NSCompoundPredicate(andPredicateWithSubpredicates:
[workoutPredicate])
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate,
ascending: true)
let query = HKSampleQuery(
sampleType: .workoutType(),
predicate: compound,
limit: 0,
sortDescriptors: [sortDescriptor]) { query, samples, error in
DispatchQueue.main.async {
guard let finalSamples = samples as? [HKWorkout] else {
result()
return
}
result(finalSamples.map { sample -> NSDictionary in
return ["duration" : sample.duration, "totalDistance": sample.totalDistance?.doubleValue(for: HKUnit.mile()) as Any, "totalEnergyBurned": sample.totalEnergyBurned?.doubleValue(for: HKUnit.kilocalorie()) as Any]
})
}
}
HKHealthStore().execute(query)
I would like to get the heart rate data for the workout and workout start/end time.
A little late but start & end date/time per workout should be easy with:
print("started #: \(sample.startDate), ended #: \(sample.endDate)")
Did you manage to get heart rates (min/max/avg) per workout? I am also interested to get this & can't seem to figure out if this is possible or not?
I am creating an app that provides workout information. On apple watch i have three labels that actively shows HEARTRATE ,DISTANCE TRAVELLED and CALORIES BURNED. Things work fine with apple watch, however the area where things go bad is when i try to show this data on iphone in realtime.
APPLE WATCH PART :-
I started workout on apple watch and saved it using folling code
func startWorkoutSession() {
// Start a workout session with the configuration
if let workoutConfiguration = configuration {
do {
workoutSession = try HKWorkoutSession(configuration: workoutConfiguration)
workoutSession?.delegate = self
workoutStartDate = Date()
healthStore.start(workoutSession!)
} catch {
// ...
}
}
}
Then i save that workout on apple watch using following code :-
func saveWorkout() {
// Create and save a workout sample
let configuration = workoutSession!.workoutConfiguration
let isIndoor = (configuration.locationType == .indoor) as NSNumber
print("locationType: \(configuration)")
let workout = HKWorkout(activityType: configuration.activityType,
start: workoutStartDate ?? Date(),
end: workoutEndDate ?? Date(),
workoutEvents: workoutEvents,
totalEnergyBurned: totalEnergyBurned,
totalDistance: totalDistance,
metadata: [HKMetadataKeyIndoorWorkout:isIndoor]);
healthStore.save(workout) { success, _ in
if success {
self.addSamples(toWorkout: workout)
}
}
// Pass the workout to Summary Interface Controller
// WKInterfaceController.reloadRootControllers(withNames: ["StopPauseInterfaceController"], contexts: [workout])
if #available(watchOSApplicationExtension 4.0, *){
// Create the route, save it, and associate it with the provided workout.
routeBuilder?.finishRoute(with: workout, metadata: metadata) { (newRoute, error) in
guard newRoute != nil else {
// Handle the error here...
return
}
}
}
else {
// fallback to earlier versions
}
}
Then i added samples to those workouts using following code :-
func addSamples(toWorkout workout: HKWorkout) {
// Create energy and distance samples
let totalEnergyBurnedSample = HKQuantitySample(type: HKQuantityType.activeEnergyBurned(),
quantity: totalEnergyBurned,
start: workoutStartDate!,
end: workoutEndDate ?? Date())
let totalDistanceSample = HKQuantitySample(type: HKQuantityType.distanceWalkingRunning(),
quantity: totalDistance,
start: workoutStartDate!,
end: workoutEndDate ?? Date())
// Add samples to workout
healthStore.add([totalEnergyBurnedSample, totalDistanceSample], to: workout) { (success: Bool, error: Error?) in
if success {
// Samples have been added
}
}
}
IPHONE PART :-
This is where things get worse. I try to access the healthkit store using HKSampleObjectQuery and It just returns static values. P.S - I am fetching healthkit store every one second for latest data using NSTimer.
The code which is called every second is :-
func extractDataFromHealthStore(identifier : HKQuantityTypeIdentifier,completion: #escaping (String) -> ()) {
let heightType = HKSampleType.quantityType(forIdentifier:identifier)!
let distantPastDate = Date.distantPast
let endDate = Date()
let predicate = HKQuery.predicateForSamples(withStart: distantPastDate, end: endDate, options: .strictStartDate)
// Get the single most recent Value
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)
let query = HKSampleQuery(sampleType: heightType, predicate: predicate, limit: 1, sortDescriptors: [sortDescriptor]) { (query, results, error) in
if let result = results?.first as? HKQuantitySample{
print("Height => \(result.quantity)")
completion("\(result.quantity)")
}else{
print("OOPS didnt get height \nResults => \(String(describing: results)), error => \(error)")
completion("")
}
}
self.healthStore.execute(query)
}
Please guide Me if my whole approach is wrong, or if it is right then what is the mistake at my end.
Let's say we have some steps data in healthkit which was auto detected by iphone and written to healthkit. And we have some steps data was enetred by user manually. Now I can differentiate between these steps using this code
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount) // The type of data we are requesting
let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let newDate = cal.startOfDayForDate(date)
print(newDate)
let predicate = HKQuery.predicateForSamplesWithStartDate(newDate, endDate: NSDate(), options: .None) // Our search predicate which will fetch all steps taken today
let query = HKSampleQuery(sampleType: type!, predicate: predicate, limit: 0, sortDescriptors: nil) { query, results, error in
var steps: Double = 0
//if result.metadata!["HKWasUserEntered"]! == 1{
if results?.count > 0
{
for result in results as! [HKQuantitySample]
{
print("Steps \(result.quantity.doubleValueForUnit(HKUnit.countUnit()))")
print()
// checking and truncating manually added steps
if result.metadata != nil {
}
else{
steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
}
}
print(steps)
}
completion(steps, error)
//}
}
executeQuery(query)
But Lets say if some other app writes some some step's data to healthkit like some fitness app.
Now I am able to read steps with device icon(auto recorded), but
How can I read steps with UP icon as well ?
(UP) is and fitness application by JawBone.
So I am having a few bugs and errors in my ViewController code in my app. First, in my line:
let predicate = HKQuery.predicateForSamplesWithStartDate(startDate,endDate: endDate ,options: .None)
I get the following error: use of unresolved identifier 'endDate'
This is weird. Is it because I have not made them into NSDate objects? How would I go about doing that?
As for my second question, it is not exactly a bug, I simply do not know how to do something. I have connected my label from the storyboard into my view controller. I want to get the data I collect to simply print onto the screen in the label. I know how to do this in most situations but I am lost as to how I can do this within my current configuration. I don't want the user to press a button or anything, I just want it to automatically display by default...Any help is greatly appreciated!!!
I have included all my code below:
import UIKit
import HealthKit
class ViewController: UIViewController {
#IBOutlet weak var displayData: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// create store
let healthStore = HKHealthStore()
// create an object type to request an authorization for a specific category, here is SleepAnalysis
if let sleepType = HKObjectType.categoryTypeForIdentifier(HKCategoryTypeIdentifierSleepAnalysis) {
let setType = Set<HKSampleType>(arrayLiteral: sleepType)
healthStore.requestAuthorizationToShareTypes(setType, readTypes: setType, completion: { (success, error) -> Void in
// here is your code
})
}
if let sleepType = HKObjectType.categoryTypeForIdentifier(HKCategoryTypeIdentifierSleepAnalysis) {
// we create a predicate to filter our data
let predicate = HKQuery.predicateForSamplesWithStartDate(startDate,endDate: endDate ,options: .None)
// I had a sortDescriptor to get the recent data first
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
// we create our query with a block completion to execute
let query = HKSampleQuery(sampleType: sleepType, predicate: predicate, limit: 30, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
endDate is undefined - you have not actually created a variable that represents endDate - thus the compiler is telling you that. And, also, startDate is also undefined, even though the compiler hasn't told you that yet. In the least, you need to create NSDates.
let startDate = NSDate()
let endDate = NSDate()
More specifically, you'll need to create them in the range for which you want to query.
Regarding showing the data by default, simply set the UILabel's text in viewDidLoad. I'm assuming that you want to set this text in response to the HealthKit query? If so, you can still do that in viewDidLoad
So, to use your query, it needs to be provided to executeQuery on healthStore Here's some code that you could use with what you posted:
healthStore.executeQuery(HKSampleQuery(
sampleType: sleepType,
predicate: predicate,
limit: 30,
sortDescriptors: [sortDescriptor],
resultsHandler: { (query: HKSampleQuery!, results: [AnyObject]!, err: NSError?) -> Void in
if err != nil {
// ERROR Occurred, handled it
println(err)
return
}
var labelText = ""
for result in results as [HKQuantitySample]! {
// SUCCESS, use results here
labelText += result
}
displayData.text = labelText
}
))