-
Notifications
You must be signed in to change notification settings - Fork 187
RecurrenceRule produces incorrect sequences for start dates with non-zero nanosecond components #1283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This was referenced May 8, 2025
lukaskollmer
added a commit
to StanfordSpezi/SpeziScheduler
that referenced
this issue
May 12, 2025
# `observeNewOutcomes` observation API, fix `.once` schedules ## ♻️ Current situation & Problem This PR marks the Scheduler's `sinkDidSavePublisher(into:)` function as `@_spi(APISupport) public`, thereby allowing other Spezi packages to observe changes to the scheduler's internal ModelContext. The specific use case here is SpeziStudy wanting to be able to run code in response to specific `Event`s getting marked as completed. It also adds a new `@_spi(APISupport) public func observeNewOutcomes(_ handler: @mainactor @escaping (Outcome) -> Void) -> AnyObject` to the Scheduler, which allows registering a callback that will get invoked every time a new `Outome` is added to the scheduler. Note: This API is intentionally closure-based, rather than being eg an `AsyncStream` as discussed below. The reason for this is that `Outcome` isn't sendable, but the `AsyncStream.Continuation`'s yield function operates on a `sending` parameter. Additionally, this PR attempts to fix #56 and fix #64. Additionally, this PR attempts to work around swiftlang/swift-foundation#1283, which currently results in using eg `Date.now` as the start date of some `Schedule`s producing incorrect schedule occurrences. ### Alternatives considered Instead of directly exposing the "raw" did change notification (which IMO we should still do, anwyay), we could also give the `Scheduler` directly a nice API for observing changes to it's underlying SwiftData model. For example, we could have a function `func completions(of task: Task) -> some AsyncSequence<Outcome, Never>` that returns an `AsyncStream` which yields individual `Outcomes` for the task, as they are saved into the context. (The immediate first issue here would bw how this should handle new Task versions getting introduced while there still are active observers on the now-outdated previous version...) IMO we should go with the simpler change from this PR first, and then maybe add a more sophisticated API in the future, should we need to. ## ⚙️ Release Notes - add `@_spi(APISupport) public func observeNewOutcomes(_ handler: @mainactor @escaping (Outcome) -> Void) -> AnyObject` - restored the ability to use `.once` schedules (it no longer crashes) - removed several `Duration` extensions (e.g.: `Duration.days(_:)`, `.weeks(_:)`, etc). these have been moved to [SpeziFoundation](https://github.com/StanfordSpezi/SpeziFoundation) ## 📚 Documentation is documented ## ✅ Testing - the UITests now schedule a `.once` task; the fact that this isn't crashing the app is in itself already a regression test - additionally, we have a View that registers a new-outcome observer with the scheduler, completes an event, and checks that the observer has been called with an outcome belonging to the just-completed event ## 📝 Code of Conduct & Contributing Guidelines By creating and submitting this pull request, you agree to follow our [Code of Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md): - [x] I agree to follow the [Code of Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md).
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
SF-0009 states that the first element in a sequence of
Date
occurrences computed from aRecurrenceRule
is always the start date (see SF-0009#Usage):However, this currently is only the case for start dates with a 0 nanosecond component. Any other start date (e.g. also
Date.now
, unless you happen to be creating the date exactly at the start of a second, which admittedly is rather unlikely), will result inrecurrences(of:)
returning a sequence that will skip the start date.The reason for this is that
Calendar.DatesByRecurring.Iterator
internally usesCalendar._dates(startingAfter:)
, which in turn usesCalendar._unadjustedDates(after:)
, which only producesDate
s with whole-integer seconds.The tests currently don't catch this, since all RecurrenceRule tests only use dates with whole-integer components as input.
The text was updated successfully, but these errors were encountered: