Context:
I have a custom Day Struct
which internally uses a Swift Date Property
. I would this Struct
be able to check, if another given day is the same weekday as the Day Struct
itself. However, every time this Code
gets executed, my App
freezes without any Runtime Error
.
Code
struct Day: Hashable {
private let calendar: Calendar = .current
let date: Date
static func == (lhs: Day, rhs: Day) -> Bool {
return Calendar.current.isDate(lhs.date, inSameDayAs: rhs.date)
}
func isSameDayOfTheWeek(as day: Day) -> Bool {
// Replacing this Line to RETURN TRUE prevents the Crash.
return calendar.component(.weekday, from: self.date) == calendar.component(.weekday, from: day.date)
}
}
enum Time: Int16, Comparable, Identifiable, CaseIterable {
case morning, noon, evening, night
var id: Int16 { self.rawValue }
var name: String { ... }
}
struct ToDo: Hashable, Identifiable {
let id: UUID = UUID()
let block: Block // NSManagedObject
let day: Day
let time: Time
}
struct ToDoView: View {
@FetchRequest(sortDescriptors: [SortDescriptor(\.creationTS)]) private var blocks: FetchedResults<Block>
var body: some View {
ForEach(toDos) { toDo in
Text(toDo.name)
}
}
private var toDos: [ToDo] {
var toDos: [ToDo] = []
for block in blocks {
let startDay: Day = block.safeStartDay
var day: Day = .today
while day >= startDay {
// Removing this Line prevents the Crash.
guard startDay.isSameDayOfTheWeek(as: day) else { continue }
for time in Time.allCases {
toDos.append(ToDo(block: block, day: day, time: time))
}
day = day - 1
}
}
return toDos
}
}
Question
How can I solve this Crash
and still be able to check if another given day is the same weekday as the Day Struct
itself?
CodePudding user response:
Notice that in the loop, if isSameDayOfTheWeek
returns false, then the loop never exits because you don't change day
before continue.
A quick fix is to just add day = day - 1
before continue
, but a much better solution would be to conform Day
to Strideable
, so that you could do something like:
for day in stride(from: .today, through: startDay, step: -1) {
...
}
Then just doing continue
would work as you had expected.
If I understand correctly what you are trying to do here, you can implement Strideable
like this:
func distance(to other: Day) -> Int {
calendar.dateComponents([.day], from: self.date, to: other.date).day!
}
func advanced(by n: Int) -> Day {
guard let date = calendar.date(byAdding: .day, value: n, to: self.date) else {
fatalError("No such date: \(n) days after \(self.date)")
}
return Day(date: date)
}
A note on the calendar
: by initialising it to current
, two instances of your struct could have end up with different calendars which have different timezones if the user decides to change it at some point.
let day1 = Day(...)
// some time later...
let day2 = Day(...)
Then comparing day1
and day2
could have some unexpected results:
day1.isSameDayOfTheWeek(as: day2) // true, because they are in the same day in day1's timezone
day2.isSameDayOfTheWeek(as: day1) // false, because they are not in the same day in day2's timezone
I would recommend using a fixed, static
, calendar.