2

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.

1
  • 1
    I don’t think ForEach is the right way here, you should use a high order function like reduce instead. Also note that Datr() will not only give you today’s date but also the current time so it is quite unlike that the == comparison will yield much results Commented May 30, 2020 at 19:33

1 Answer 1

1

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)! }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.