StanfordSpezi / SpeziScheduler

Scheduler Module for the Stanford Spezi Ecosystem
https://swiftpackageindex.com/StanfordSpezi/SpeziScheduler/documentation/
MIT License
3 stars 3 forks source link

Bug report: SpeziScheduler Multiple Tasks Error #36

Closed karansoin closed 3 months ago

karansoin commented 4 months ago

Description

When I try to add multiple tasks in a single day in the schedule that uses SpeziScheduler, running the app causes it to crash, with the error message "Fatal error: Duplicate keys of type 'Event' were found in a Dictionary. This usually means either that the type violates Hashable's requirements, or that members of such a dictionary were mutated after insertion. The template app's PICSScheduler file (our project's version of it) initially has only the single task with static var SocialSupportTask: SpeziScheduler.Task, but I modified that to include 3 tasks instead of just 1, so it now includes: static var PHQ4Task: SpeziScheduler.Task, static var EQ5D5LTask: SpeziScheduler.Task, and static var MiniNutritionalTask: SpeziScheduler.Task

And, the final convenience init is to incorporate: convenience init() { self.init(tasks: [Self.PHQ4Task, Self.EQ5D5LTask, Self.MiniNutritionalTask])}

These changes seem to have been enough to cause the error.

Reproduction

The error is also inconsistent as it produces the error sometimes when you complete just the first task, and sometimes when you complete the first task without error but then doing the second task causes the error. There are also when all three tasks can be completed without errors, but that is a more rare occurrence

Expected behavior

Each of the tasks should be completed without errors, with each task's updated completion text and icon showing up. Multiple tasks as defined in the PICSScheduler should be allowed to show up.

Additional context

No response

Code of Conduct

Supereg commented 4 months ago

Thanks for reporting this issue.

As discussed in class today, this line is probably the problem, as the state might change within the lifetime of an event: https://github.com/StanfordSpezi/SpeziScheduler/blob/adf793cb47dc199f8ae88f5c719f4d3ba06a4c4e/Sources/SpeziScheduler/Model/Event.swift#L233

While we don't really use the Hashable conformance within SpeziScheduler or the Application, SwiftUI does some magic when properties of a view conform to Hashable (e.g., to identify views and check if any contents of them changed). That's my current understanding of the problem and why the App crashes in the end. I started the App within the Debugger and set a breakpoint in the Hashable implementation and the hash method was always called from within the SwiftUI framework.

To resolve the issue I would propose to remove the line and replace it having the scheduledAt time. So an Event is uniquely identified by its Task Id and the Date it is scheduled at:

// hash(into:) implementation ...
hasher.combine(id)
hasher.combine(scheduledAt)

Do you want to tackle fixing this problem? As part of this change, it would be great to add a small unit test inside SchedulerTests to verify that two instances of Event produce the same hash (scheduled vs. completed). You may look into the Swift Hasher to create hashes by your own 👍

PSchmiedmayer commented 4 months ago

Thank you for the context @Supereg! It would be great to see this as a small contribution @karansoin!