Jakub Kliský
Jakub Kliský

Reputation: 129

How can I dynamically work with FetchRequest data in SwiftUI view

I am trying to create a calorie counter view using SwiftUI where workout data is fetched from Core Data and besides writing all of the data out I want to get total number of burnt calories for today. I was thinking of going through the fetched data, check whether its createdAt attribute equals current Date() and if so add it up to sum of self.totalCaloriesBurntToday.

I got an error that ForEach of this type does not conform to certain protocols:

Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols

Here is my code:

import SwiftUI

struct ProgressBar: View {
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(
        entity: WorkoutInputData.entity(),
        sortDescriptors: []
    ) var workoutData: FetchedResults<WorkoutInputData>

    @State private var totalCaloriesBurntToday : Int


    var body: some View {
        ForEach(workoutData) { singleWorkout in
            if singleWorkout.createdAt == Date(){
                self.totalCaloriesBurntToday += Int(singleWorkout.caloriesBurnt)!
            }
        }
        return Text("\(self.totalCaloriesBurntToday)")
    }
}

I want to output the sum later and want it to change dynamically. Everytime some data object is added or deleted the sum would automatically adjust.

I have tried to go around this using UserDefaults to save the calories there, but there is the problem that the change of data in UserDefaults does not automatically push for changes in the view and hence it is not dynamic.

Upvotes: 2

Views: 337

Answers (1)

pawello2222
pawello2222

Reputation: 54436

The SwiftUI's ForEach you used has a return type of Content, which means for every item in the loop it has to return some View. It's not the same as a simple foreach loop.

You can use reduce to calculate totalCaloriesBurnt and filter to choose only today's workouts.

For comparing dates it's better to use Calendar.compare:

Calendar.current.compare(singleWorkout.createdAt, to: Date(), toGranularity: .day) == .orderedSame

Summing up your code can look like this:

workoutData
  .filter { Calendar.current.compare($0.createdAt, to: Date(), toGranularity: .day) == .orderedSame }
  .reduce(0) { $0 + Int($1.caloriesBurnt)! }

Upvotes: 1

Related Questions