Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

rework notification scheduling and event completion #58

Merged
merged 36 commits into from
Mar 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2a4a105
rework notification scheduling and event completion
lukaskollmer Feb 23, 2025
fc676eb
cleanup
lukaskollmer Feb 24, 2025
8fce68c
ooof
lukaskollmer Feb 24, 2025
d38d123
incorporate some feedback
lukaskollmer Feb 24, 2025
c099334
lint
lukaskollmer Feb 24, 2025
fee11fe
change test
lukaskollmer Feb 24, 2025
81b3d8b
Event.isCompleted, improve the `try? event.complete()`
lukaskollmer Feb 24, 2025
31c0673
x
lukaskollmer Feb 24, 2025
f02e0c5
improve notification scheduling
lukaskollmer Feb 24, 2025
569b63f
update deps
lukaskollmer Feb 24, 2025
15e23e9
skip test on visionOS (doesn't work bc you can't `swipeDown()`)
lukaskollmer Feb 24, 2025
cf68d45
lint
lukaskollmer Feb 24, 2025
1a84502
x
lukaskollmer Feb 24, 2025
fa2373e
x
lukaskollmer Feb 24, 2025
dd98982
roll back
lukaskollmer Feb 24, 2025
8147fd1
update deps
lukaskollmer Feb 24, 2025
e2dadc4
move UITests app to Swift 6, visionOS 2
lukaskollmer Feb 24, 2025
e799e61
ugh
lukaskollmer Feb 24, 2025
363edc1
add `Scheduler/queryEvents(forTaskWithId:in:)`
lukaskollmer Feb 24, 2025
72364b9
fix
lukaskollmer Feb 24, 2025
8c309f8
EventQuery: expose range via binding
lukaskollmer Feb 25, 2025
3ec9921
Make TodayList effectively an alias for the EventScheduleList
lukaskollmer Feb 25, 2025
90b126a
changes
lukaskollmer Feb 25, 2025
d63595b
x
lukaskollmer Feb 25, 2025
44e2083
Improve Task/Event Notification Scheduling
lukaskollmer Feb 25, 2025
db09ce7
small changes
lukaskollmer Feb 25, 2025
892f135
improve notification scheduling a bit further
lukaskollmer Feb 26, 2025
5b5d5a4
cleanup
lukaskollmer Feb 26, 2025
629b5bc
fix
lukaskollmer Feb 26, 2025
dfb96e4
try to enable code coverage for all targets
lukaskollmer Feb 26, 2025
9e2a076
limit UITests code coverage checking to SpeziScheduler and SpeziSched…
lukaskollmer Feb 26, 2025
58b3263
clean up some code
lukaskollmer Feb 28, 2025
579c902
add test case for ahead-of-time event completion
lukaskollmer Feb 28, 2025
5c011a7
lint
lukaskollmer Feb 28, 2025
58ed7dc
hmmm
lukaskollmer Feb 28, 2025
177e850
improve predicate
lukaskollmer Mar 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ let package = Package(
.package(url: "https://github.com/StanfordSpezi/Spezi", from: "1.8.0"),
.package(url: "https://github.com/StanfordSpezi/SpeziViews", from: "1.7.0"),
.package(url: "https://github.com/StanfordSpezi/SpeziStorage", from: "2.1.0"),
.package(url: "https://github.com/StanfordSpezi/SpeziNotifications.git", from: "1.0.2"),
.package(url: "https://github.com/StanfordSpezi/SpeziNotifications.git", from: "1.0.5"),
.package(url: "https://github.com/apple/swift-algorithms.git", from: "1.2.0"),
.package(url: "https://github.com/swiftlang/swift-syntax", from: "600.0.0-prerelease-2024-08-14"),
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing.git", from: "1.17.2")
Expand Down
28 changes: 14 additions & 14 deletions Sources/SpeziScheduler/EventQuery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ public struct EventQuery {

/// Binding to the `EventQuery`.
public struct Binding {
/// The range for which the query fetches events.
public let range: Range<Date>

/// An error encountered during the most recent attempt to fetch events.
///
/// This property contains the error from the most recent fetch. It is `nil` if the most recent fetch succeeded.
Expand All @@ -86,7 +89,7 @@ public struct EventQuery {

private let configuration: Configuration
private let storage = Storage()
private var binding = Binding()
private var binding: Binding

/// The fetched events.
///
Expand All @@ -107,12 +110,13 @@ public struct EventQuery {
/// Create a new event query.
/// - Parameters:
/// - range: The date range to query events for.
/// - predicate: An additional ``Task`` predicate.
/// - predicate: Optional `Predicate` allowing you to filter which events should be included in the query, based on their ``Task``.
public init(
in range: Range<Date>,
predicate: Predicate<Task> = #Predicate { _ in true }
) {
self.configuration = Configuration(range: range, taskPredicate: predicate)
configuration = Configuration(range: range, taskPredicate: predicate)
binding = Binding(range: range)
}
}

Expand Down Expand Up @@ -155,23 +159,19 @@ extension EventQuery: DynamicProperty {
// We always keep track of the set of models we are interested in. Only if that changes we query the "real thing".
// Creating `Event` instances also incurs some overhead and sorting.
// Querying just the identifiers can be up to 10x faster.
let anchor = try measure(name: "Event Anchor Query") {
try scheduler.queryEventsAnchor(for: configuration.range, predicate: configuration.taskPredicate)
}
let anchor = try scheduler.queryEventsAnchor(for: configuration.range, predicate: configuration.taskPredicate)

guard anchor != storage.fetchedIdentifiers else {
binding.fetchError = nil
return
}

let events = try measure(name: "Event Query") {
// Fetch also has a `batchSize` property we could explore in the future. It returns the results as a `FetchResultsCollection`.
// It isn't documented how it works exactly, however, one could assume that it lazily loads (or just initializes) model objects
// when iterating through the sequence. However, it probably doesn't really provide any real benefit. Users are expected to be interested
// in all the results they query for (after all the provide a predicate). Further, we would need to adjust the underlying
// type of the property wrapper to return a collection of type `FetchResultsCollection`.
try scheduler.queryEvents(for: configuration.range, predicate: configuration.taskPredicate)
}
// Fetch also has a `batchSize` property we could explore in the future. It returns the results as a `FetchResultsCollection`.
// It isn't documented how it works exactly, however, one could assume that it lazily loads (or just initializes) model objects
// when iterating through the sequence. However, it probably doesn't really provide any real benefit. Users are expected to be interested
// in all the results they query for (after all the provide a predicate). Further, we would need to adjust the underlying
// type of the property wrapper to return a collection of type `FetchResultsCollection`.
let events = try scheduler.queryEvents(for: configuration.range, predicate: configuration.taskPredicate)

storage.fetchedEvents = events
storage.fetchedIdentifiers = anchor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ extension Schedule {
enum NotificationMatchingHint: Codable, Sendable, Hashable {
case components(hour: Int, minute: Int, second: Int, weekday: Int?)
case allDayNotification(weekday: Int?)

func dateComponents(calendar: Calendar, allDayNotificationTime: NotificationTime) -> DateComponents {
switch self {
case let .components(hour, minute, second, weekday):
Expand All @@ -25,7 +25,8 @@ extension Schedule {
}
}
}



static func notificationTime(for start: Date, duration: Duration, allDayNotificationTime: NotificationTime) -> Date {
if duration.isAllDay {
let time = allDayNotificationTime
Expand All @@ -37,7 +38,8 @@ extension Schedule {
return start
}
}



static func notificationMatchingHint( // swiftlint:disable:this function_parameter_count
forMatchingInterval interval: Int,
calendar: Calendar,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ extension SchedulerNotifications {
/// The reverse dns notation use as a prefix for all notifications scheduled by SpeziScheduler.
static nonisolated let baseNotificationId = "edu.stanford.spezi.scheduler.notification"

/// /// The reverse dns notation use as a prefix for all task-level scheduled notifications (calendar trigger).
/// The reverse dns notation use as a prefix for all task-level scheduled notifications (calendar trigger).
static nonisolated let baseTaskNotificationId = "\(baseNotificationId).task"

/// /// The reverse dns notation use as a prefix for all event-level scheduled notifications (interval trigger).
/// The reverse dns notation use as a prefix for all event-level scheduled notifications (interval trigger).
static nonisolated let baseEventNotificationId = "\(baseNotificationId).event"

/// Retrieve the category identifier for a notification for a task, derived from its task category.
Expand Down
Loading