Chapter 10 HIITFit challenge folder

Hi. So I have been following along the excellent tutorial until Chapter 10 when we are asked to do the challenge to fix the design of HIITFit. The project supplied in the challenge directory contains some new code that I especially do not understand and I was hoping for some clarification.
My question is on the code that was written in the .onAppear modifier, I will paste it below. I was adding my own comments to better understand what is going on

var body: some View {
GeometryReader { geometry in
VStack {
HStack {
ForEach(0…<7) { index in
bar(day: index, size: geometry.size)
}
}
Divider()
Spacer()
}
.onAppear {
days = Date().lastSevenDays //array of dates from past week
exercisesForWeek = ExerciseDay
let counts: [Int] = days.map { day in
let foundDate = exercisesForWeek.filter {
$0.date.yearMonthDay == day.yearMonthDay
}
return foundDate.first?.exercises.count ?? 0
//returns the number of exercises completed for the first day
}
assert(counts.count == 7)
// remap values to 0 to maxBarHeight
let maxValue = max(counts.max() ?? 0, 1)
countsForWeek = counts.map {
$0 * maxBarHeight / maxValue
}
}

I understand that foundDate is an array of ExerciseDays, an array of exercises that were completed in the past week, and that counts as an array of integers, is an array of the number of exercises that were completed in the first day, the first ExerciseDay element

return foundDate.first?.exercises.count ?? 0

My question is, what is the purpose of counts as a value, since it is the number of exercises done on the first day? And given that our development data does not have 7 exercises, what is the purpose of the assert line?

assert(counts.count == 7)

The assertion is going to fail every time, and I am positively flummoxed as to the logic behind this code. Any help would be appreciated, if I hope to reproduce what I have learnt in an app of my own I feel I have to understand the logic behind the supplied code.

Thanks in advance

Hi @supernova2108, and welcome to the forum :smiley: !

You can format your code blocks using a backtick `

Use three of them before and after the code block

This is a code block surrounded by ```

It means that the post renderer won’t try and do clever things, for example, it lost the important line:

exercisesForWeek = [ExerciseDay](history.exerciseDays.prefix(7))

As for your question, I hope I can resolve the confusion.

Going through the code:

days is an array that contains seven dates for the last seven days. eg [2021-11-11, 2021-11-12, ...., 2021-11-17]

exercisesForWeek is an array that contains a maximum of seven exercises in the format:

id: BE209DAC-CC72-42FA-B01B-80DB3CF50D09, 
date: 2021-11-17 01:18:04 +0000, 
exercises: ["Step Up"]

The chart drawing is easier if there are always seven data points for the week, one for the count of each day, but if you’ve only done one exercise, then you only have one data point.

So in an ideal world, counts will contain seven entries and then the chart drawing is easier. That’s what the assert does - it makes sure there are seven entries in counts.

let counts: [Int] = days.map { day in
}

This code will iterate through days. We already know that there are seven entries in days, so there will be seven iterations, each using day. Therefore counts will end up having seven entries.

let foundDate = exercisesForWeek.filter {
   $0.date.yearMonthDay == day.yearMonthDay
}

Here you filter exercisesForWeek so that foundDate is an array that contains only the exercises performed on the day currently being processed in the map loop.

If there are no entries for a particular day, then foundDate will be empty, otherwise it will contain the single ExerciseDay for that day.

return foundDate.first?.exercises.count ?? 0

You then get the first entry in foundDate. If foundDate is empty, then there is no first entry. In that case, the zero after the ?? is returned.
Otherwise, the first entry’s count of exercises performed is returned.

It’s quite complex Swift code. An alternative, less Swifty code would be:

var counts: [Int] = []
for day in days {
   var countForDay = 0
   for exerciseDay in exercisesForWeek {
      if exerciseDay.date.yearMonthDay == day.yearMonthDay {
         countForDay = exerciseDay.exercises.count
      }
    }
  counts.append(countForDay)
}

This code is perfectly acceptable. However, variables rather than constants are inherently unsafe, as at any time you can write to counts. In a longer piece of code, you might not pick up that you are writing to counts when you’re not supposed to.

That’s why let counts = days.map... is preferred to the for loop and appending to counts.

I hope that helps - if you’re still confused, please ask further questions.

2 Likes

@supernova2108 Please definitely check out the following Swift tutorials when you actually get a chance in order to understand even better how both map(_:) and filter(_:) really work under the hood after all:

I really do hope they help!

1 Like