0

I'm trying to display a users weekly steps from HealthKit (broken down into daily eg Monday - 1200, Tuesday - 800), I've made a View model that gets all the data, if I do not add a delay on the data in my view, it wont print, but now my chart is not displaying at all? Any help would be appreciated

//View

import SwiftUI
import Charts

struct DashboardView: View {
    var body: some View {
        @ObservedObject var healthKitManager = HealthKitManager()
        @State var stepsData = healthKitManager.weeklySteps
        @State var showChart = false
        
        VStack {
          
            VStack {
                
                HStack{
                    Text("Hello")
                        .foregroundColor(Color.black)
                        .font(.largeTitle)
                    Text("Leander ")
                        .foregroundColor(Color.black)
                        .font(.largeTitle)
                        .fontDesign(.serif)
                        .fontWeight(.bold)
                }
                .padding(10)
                
                VStack{
                    Text("WANT TO SEE YOUR NEARBY USERS?")
                        .padding(.leading, 70)
                        .font(.subheadline)
                        .foregroundColor(Color("purple"))
                    Divider()
                        .frame(minHeight: 1)
                        .overlay(Color("Green"))
                        .padding(.leading, 70)
                    
                    Text("WANT TO SEE CONNETIONS MADE?")
                        .font(.subheadline)
                        .padding(.leading, 70)
                        .foregroundColor(Color("Green"))
                }
                .padding(.vertical, 3)
                
                VStack {
                
                    Text("Connections made:")
                    Spacer()
                        .frame(height: 20)
                    Chart {
                        ForEach(stepsData, id: \.day) { item in
                            LineMark(
                                x: .value("date", item.day),
                                y: .value("connections", item.steps)
                            )
                        }
                    }
                    .frame(maxHeight: 150)
                    Spacer()
                    Text("Distance walked:")
                        .padding(.bottom, 5)
                    
                }
                .padding(20)
                
            }
            .frame(maxWidth: .infinity, maxHeight: 900, alignment: .top)
            .padding(20)
            .background(Color.white)
//            .cornerRadius(radius: 170.0, corners: [.topLeft])
          

        }

        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)

        .background(Color("Green"))
        .onAppear{
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.0){
    
                print("HERE: \(healthKitManager.weeklySteps)")
                stepsData = healthKitManager.weeklySteps
                showChart = true

                }
        }
    }
}
struct DashboardView_Previews: PreviewProvider {
    static var previews: some View {
        DashboardView()
    }
}

// View Model

import Foundation
import HealthKit

class HealthKitManager: ObservableObject {
    let healthStore: HKHealthStore = HKHealthStore()
    let steps = HKQuantityType(.stepCount)
    
    //Steps tesster
    var thisWeekSteps: [Int: Int] = [1: 0, 2: 0, 3: 0,
                                       4: 0, 5: 0, 6: 0, 7: 0]
    var weeklySteps: [StepsModel] = []
    init(){
        if(HKHealthStore.isHealthDataAvailable()){
            
            
            let heartBeat = HKQuantityType(.heartRate)
            
            
            let healtTyoes: Set = [steps, heartBeat]
            
            Task{
                do{
                    try await healthStore.requestAuthorization(toShare: [], read: healtTyoes)
                    fetchSteps()
                }catch{
                    print("Error retrieving data")
                }
            }
        }
    }
    
        func fetchSteps() {
            guard let stepCountType = HKQuantityType.quantityType(forIdentifier: .stepCount) else {
                return
            }
            
            let today = Date()
            let startOfWeek = Calendar.firstWeekday
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "EEEE"
            
            guard let endOfWeek = Calendar.current.date(byAdding: .day, value: 6, to: startOfWeek) else {
                print("Failed to calculate the end date of the week.")
                return
            }
    
            let predicate = HKQuery.predicateForSamples(
                withStart: startOfWeek,
                end: endOfWeek,
                options: .strictStartDate
            )
            
            let query = HKStatisticsCollectionQuery(
                quantityType: stepCountType,
                quantitySamplePredicate: predicate,
                options: .cumulativeSum,
                anchorDate: startOfWeek,
                intervalComponents: DateComponents(day: 1)
            )
    
            query.initialResultsHandler = { _, result, error in
                guard let result = result, error == nil else {
                    print("Error getting steps \(error?.localizedDescription ?? "")")
                    return
                }
    
    
                result.enumerateStatistics(from: startOfWeek, to: today) { statistics, _ in
                    if let quantity = statistics.sumQuantity() {
                        let steps = Int(quantity.doubleValue(for: HKUnit.count()))
                        let date = statistics.startDate
                        let dayOfWeek = dateFormatter.string(from: date)
                        self.weeklySteps.append(StepsModel(day: dayOfWeek, steps: steps))
                        print("\(dayOfWeek) - \(steps)")
                    }
                    
                }
//
//                result.enumerateStatistics(from: startOfWeek, to: endOfWeek) { statistics, _ in
//                    if let quantity = statistics.sumQuantity() {
//                        let steps = Int(quantity.doubleValue(for: HKUnit.count()))
//                        let day = calendar.component(.weekday, from: statistics.startDate)
//                        self.thisWeekSteps[day] = steps
//                        print("day: \(day) - steps: \(steps)")
//                    }
//                }
            }
    
            healthStore.execute(query)
        }
    
    func readStepCountThisWeek() {
        guard let stepCountType = HKQuantityType.quantityType(forIdentifier: .stepCount) else {
            return
        }
        let calendar = Calendar.current
        let today = calendar.startOfDay(for: Date())
//        let startOfWeek = Calendar.firstWeekday
        
        // Find the start date (Monday) of the current week
        guard let startOfWeek = calendar.date(from: calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: today)) else {
            print("Failed to calculate the start date of the week.")
            return
        }
        
        // Find the end date (Sunday) of the current week
        guard let endOfWeek = calendar.date(byAdding: .day, value: 6, to: startOfWeek) else {
            print("Failed to calculate the end date of the week.")
            return
        }
        
        let predicate = HKQuery.predicateForSamples(
            withStart: startOfWeek,
            end: today,
            options: .strictStartDate
        )
        
        let query = HKStatisticsCollectionQuery(
            quantityType: stepCountType,
            quantitySamplePredicate: predicate,
            options: .cumulativeSum, // fetch the sum of steps for each day
            anchorDate: startOfWeek,
            intervalComponents: DateComponents(day: 1) // interval to make sure the sum is per 1 day
        )
        
        query.initialResultsHandler = { _, result, error in
            guard let result = result else {
                if let error = error {
                    print("An error occurred while retrieving step count: \(error.localizedDescription)")
                }
                return
            }
            
            result.enumerateStatistics(from: startOfWeek, to: endOfWeek) { statistics, _ in
                if let quantity = statistics.sumQuantity() {
                    let steps = Int(quantity.doubleValue(for: HKUnit.count()))
                    let day = calendar.component(.weekday, from: statistics.startDate)
                    self.thisWeekSteps[day] = steps
                
                    print("day: \(day) - steps: \(steps)")
                }
            }
        }
        
        healthStore.execute(query)
    }
}


extension Calendar {
    static var firstWeekday: Date {
        let calendar = Calendar.current
        let now = Date()
        let beginningOfWeek = calendar.dateInterval(of: .weekOfYear, for: now)?.start
        return beginningOfWeek ?? now
    }
}

// Data Model

import Foundation

struct StepsModel: Identifiable, Codable, Equatable {
    var id = UUID()
    var day: String
    var steps: Int
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
DevAcc
  • 93
  • 9
  • Have a look at this link, it gives you some good examples of how to manage data in your app: [monitoring data](https://developer.apple.com/documentation/swiftui/monitoring-model-data-changes-in-your-app) . Hint, you should be using `@StateObject var healthKitManager = HealthKitManager()` and have some `@Published var ...` in it. – workingdog support Ukraine Aug 09 '23 at 07:05
  • When I change it to @StateObject, my onAppear gets a warning of "Accessing StateObject's object without being installed on a View. This will create a new instance each time.", regardless though, my chart actually never shows up because it keeps initiating with an empty Array, which is weird. – DevAcc Aug 09 '23 at 07:19
  • currently the `View` is not `observing` anything from your `HealthKitManager`. So, instead of using `@State var stepsData = healthKitManager.weeklySteps`, you could try using `@Published var weeklySteps: [StepsModel] = []` in your `class HealthKitManager` and use `Chart { ForEach(healthKitManager.weeklySteps, id: \.day) { item in ...}`. Read the info at [monitoring data](https://developer.apple.com/documentation/swiftui/monitoring-model-data-changes-in-your-app) – workingdog support Ukraine Aug 09 '23 at 08:14
  • That warning means exactly what it says, your wrapper is not in a View. Most wrappers have no effect on anything except for Views some Combine wrappers are the exception but that is likely a huge reason why you are not getting updates – lorem ipsum Aug 09 '23 at 12:01
  • 1
    Note, you should not have `@ObservedObject var healthKitManager ...` and any `@State var stepsData ...` etc inside the **body** of `DashboardView`, they should be outside of the **body**. – workingdog support Ukraine Aug 09 '23 at 12:44
  • @workingdogsupportUkraine That was my problem, thank you so much! – DevAcc Aug 09 '23 at 20:47

0 Answers0