Home > Blockchain >  Swift: Working with Calendar.component causes App to freeze without any Runtime Error?
Swift: Working with Calendar.component causes App to freeze without any Runtime Error?

Time:09-12

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.

  • Related