### Query All Task Versions and Print Details Source: https://context7.com/stanfordspezi/spezischeduler/llms.txt Fetch all versions of tasks within a date range and print their effective start date and whether they are the latest version. ```swift import SpeziScheduler // Fetch all versions of a task let allVersions = try scheduler.queryTasks(for: Date.distantPast...Date.distantFuture) let questionnaireVersions = allVersions.filter { $0.id == "daily-questionnaire" } for version in questionnaireVersions { print("Effective from \(version.effectiveFrom), latest: \(version.isLatestVersion)") } ``` -------------------------------- ### Create and Update Tasks with SpeziScheduler Source: https://github.com/stanfordspezi/spezischeduler/blob/main/Sources/SpeziScheduler/SpeziScheduler.docc/SpeziScheduler.md Implement a custom `Module` to manage tasks using the `Scheduler`. This example demonstrates how to create a daily task with a specific schedule and handles potential errors during task creation. ```swift import Spezi import SpeziScheduler class MySchedulerModule: Module { @Dependency(Scheduler.self) private var scheduler init() {} func configure() { do { try scheduler.createOrUpdateTask( id: "my-daily-task", title: "Daily Questionnaire", instructions: "Please fill out the Questionnaire every day.", category: Task.Category("Questionnaire", systemName: "list.clipboard.fill"), schedule: .daily(hour: 9, minute: 0, startingAt: .today) ) } catch { // handle error (e.g., visualize in your UI) } } } ``` -------------------------------- ### Create or Update Task with Custom Metadata Source: https://context7.com/stanfordspezi/spezischeduler/llms.txt Example of creating or updating a weekly task, including scheduling notifications and attaching custom metadata to the Task.Context using the @Property macro. ```swift import SpeziScheduler // Create a weekly task, every Monday at 8am, starting this week do { let (task, didChange) = try scheduler.createOrUpdateTask( id: "weekly-weight-check", title: "Weight Measurement", instructions: "Perform a new weight measurement with your Bluetooth scale.", category: Task.Category("Measurement", systemName: "scalemass.fill"), schedule: .weekly(weekday: .monday, hour: 8, minute: 0, startingAt: .today), completionPolicy: .afterStart, scheduleNotifications: true, notificationThread: .task, tags: ["measurement"] ) { context in // Attach custom metadata to the Task.Context via @Property context.measurementType = .weight } print("Task created or updated: \(didChange), id: \(task.id)") } catch Scheduler.DataError.shadowingPreviousOutcomes { print("Cannot update: would shadow existing outcomes.") } catch { print("Unexpected error: \(error)") } ``` -------------------------------- ### Configure Scheduler Persistence Backends Source: https://context7.com/stanfordspezi/spezischeduler/llms.txt Shows how to initialize the Scheduler with different persistence options: default on-disk, custom on-disk directory, and in-memory for testing. ```swift // Default on-disk storage (recommended for production) let scheduler = Scheduler() // Custom on-disk storage directory let customURL = URL.applicationSupportDirectory.appending(component: "MyApp/Scheduler") let schedulerCustom = Scheduler(persistence: .onDisk(directory: customURL)) // In-memory storage for unit tests let schedulerInMemory = Scheduler(persistence: .inMemory) ``` -------------------------------- ### Scheduler Initialization Source: https://context7.com/stanfordspezi/spezischeduler/llms.txt Explains the different persistence options for initializing the `Scheduler`, including default on-disk storage, custom on-disk directories, and in-memory storage for testing. ```APIDOC ## `Scheduler.init(persistence:)` — Configure storage backend `Scheduler` can be initialized with `.onDisk` (default, stores in `~/Documents/SpeziScheduler`), `.onDisk(directory:)` for a custom path, or `.inMemory` for ephemeral (testing) storage. In a macOS app that uses `.onDisk`, sandboxing must be enabled. ```swift // Default on-disk storage (recommended for production) let scheduler = Scheduler() // Custom on-disk storage directory let customURL = URL.applicationSupportDirectory.appending(component: "MyApp/Scheduler") let schedulerCustom = Scheduler(persistence: .onDisk(directory: customURL)) // In-memory storage for unit tests let schedulerInMemory = Scheduler(persistence: .inMemory) ``` ``` -------------------------------- ### Configure and Create Task with Scheduler Module Source: https://context7.com/stanfordspezi/spezischeduler/llms.txt Demonstrates how to declare the Scheduler as a dependency in a custom Spezi Module and configure a daily task. Handles potential data errors during task creation. ```swift import Spezi import SpeziScheduler // 1. Declare the Scheduler as a dependency in your own Module class MySchedulerModule: Module { @Dependency(Scheduler.self) private var scheduler init() {} func configure() { do { // Creates the task if new, or creates a new version if properties changed try scheduler.createOrUpdateTask( id: "daily-questionnaire", title: "Daily Questionnaire", instructions: "Please fill out the questionnaire every day.", category: Task.Category("Questionnaire", systemName: "list.clipboard.fill"), schedule: .daily(hour: 9, minute: 0, startingAt: .today), completionPolicy: .sameDay, scheduleNotifications: true, tags: ["health", "survey"] ) } catch { // Handle Scheduler.DataError cases: // .invalidContainer, .shadowingPreviousOutcomes, .nextVersionAlreadyPresent print("Failed to configure task: \(error.localizedDescription)") } } } // 2. Register the module in your SpeziAppDelegate class AppDelegate: SpeziAppDelegate { override var configuration: Configuration { Configuration { MySchedulerModule() } } } ``` -------------------------------- ### Scheduler Module Integration Source: https://context7.com/stanfordspezi/spezischeduler/llms.txt Demonstrates how to integrate the `Scheduler` module into a Spezi application by declaring it as a dependency in a custom module and registering it in the SpeziAppDelegate. ```APIDOC ## `Scheduler` — Core module for managing tasks and events `Scheduler` is a Spezi `Module` and the central entry point for all scheduling operations. It manages a versioned, append-only SwiftData store of `Task` objects and provides APIs to create, update, query, and delete tasks and their events. It must be declared as a dependency in a Spezi app delegate or another module. ```swift import Spezi import SpeziScheduler // 1. Declare the Scheduler as a dependency in your own Module class MySchedulerModule: Module { @Dependency(Scheduler.self) private var scheduler init() {} func configure() { do { // Creates the task if new, or creates a new version if properties changed try scheduler.createOrUpdateTask( id: "daily-questionnaire", title: "Daily Questionnaire", instructions: "Please fill out the questionnaire every day.", category: Task.Category("Questionnaire", systemName: "list.clipboard.fill"), schedule: .daily(hour: 9, minute: 0, startingAt: .today), completionPolicy: .sameDay, scheduleNotifications: true, tags: ["health", "survey"] ) } catch { // Handle Scheduler.DataError cases: // .invalidContainer, .shadowingPreviousOutcomes, .nextVersionAlreadyPresent print("Failed to configure task: \(error.localizedDescription)") } } } // 2. Register the module in your SpeziAppDelegate class AppDelegate: SpeziAppDelegate { override var configuration: Configuration { Configuration { MySchedulerModule() } } } ``` ``` -------------------------------- ### Display Event Details with InstructionsTile Source: https://context7.com/stanfordspezi/spezischeduler/llms.txt Use InstructionsTile to display event information. It supports basic display, action buttons, custom alignment, and custom headers/footers. ```swift InstructionsTile(event) ``` ```swift InstructionsTile(event) { try? event.complete() } ``` ```swift InstructionsTile(event, alignment: .center) { try? event.complete() } more: { VStack(alignment: .leading, spacing: 12) { Text("Step-by-step instructions") .font(.headline) Text("1. Open the app\n2. Follow the on-screen prompts\n3. Submit your response.") } .padding() } ``` ```swift InstructionsTile( event, footerVisibility: .showAlways, header: { HStack { Image(systemName: "heart.fill").foregroundStyle(.red) Text(event.task.title).font(.headline) } }, footer: { Button("Complete") { try? event.complete() } .buttonStyle(.borderedProminent) } ) ``` -------------------------------- ### Create Task with Specific Completion Policy Source: https://context7.com/stanfordspezi/spezischeduler/llms.txt Creates or updates a task with a defined completion policy, controlling when the event can be marked as complete. Also demonstrates how to check when completion becomes allowed based on a policy. ```swift import SpeziScheduler // .sameDay — allowed any time on the day the event occurs // .afterStart — allowed after the event's start time, forever // .sameDayAfterStart — allowed after start time, but only on the same day // .duringEvent — allowed only while the event is currently occurring // .anytime — always allowed try scheduler.createOrUpdateTask( id: "medication-reminder", title: "Take Medication", instructions: "Take your evening medication.", schedule: .daily(hour: 20, minute: 0, startingAt: .today), completionPolicy: .duringEvent // must complete within the event window ) // Check when completion becomes allowed / disallowed let policy = AllowedCompletionPolicy.afterStart if let allowedAt = policy.dateOnceCompletionIsAllowed(for: event) { print("Completion allowed from: \(allowedAt)") } ``` -------------------------------- ### Navigate Task Version Chain Source: https://context7.com/stanfordspezi/spezischeduler/llms.txt Find the latest version of a task and navigate to its first version and previous version, printing their effective dates. ```swift // Navigate the version chain if let latest = questionnaireVersions.first(where: { $0.isLatestVersion }) { let first = latest.firstVersion print("First version effective from: \(first.effectiveFrom)") print("Current version effective from: \(latest.effectiveFrom)") if let prev = latest.previousVersion { print("Previous version: \(prev.effectiveFrom)") } } ``` -------------------------------- ### Create Task with Notifications Enabled Source: https://context7.com/stanfordspezi/spezischeduler/llms.txt Create or update a task with a daily schedule and enable notifications. Notifications are grouped by task in Notification Center and have a specified display time. ```swift // Create a task with notifications enabled try scheduler.createOrUpdateTask( id: "med-reminder", title: "Evening Medication", instructions: "Take your medication.", schedule: .daily(hour: 21, minute: 0, startingAt: .today), scheduleNotifications: true, notificationThread: .task, // group by task in Notification Center notificationTime: NotificationTime(hour: 21, minute: 0) ) ``` -------------------------------- ### Scheduler.queryTasks(for:predicate:sortBy:fetchLimit:prefetchOutcomes:) Source: https://context7.com/stanfordspezi/spezischeduler/llms.txt Retrieves all versions of tasks that are effective within a given date range. This method accounts for task versioning and can filter, sort, and limit the results. ```APIDOC ## `Scheduler.queryTasks(for:predicate:sortBy:fetchLimit:prefetchOutcomes:)` — Query task versions Returns all `Task` versions effective within the given date range. Since tasks are versioned, a single logical task may appear multiple times if it was updated during the range. Supports both `Range` and `ClosedRange`. ### Method `queryTasks(for:predicate:sortBy:fetchLimit:prefetchOutcomes:)` ### Parameters - **for** (`Range` or `ClosedRange`): The date range for which tasks should be effective. - **predicate** (`Predicate`, optional): An optional predicate to filter tasks. - **sortBy** (`[SortDescriptor]`, optional): An array of sort descriptors to order the results. - **fetchLimit** (`Int`, optional): The maximum number of tasks to return. - **prefetchOutcomes** (`Bool`, optional): Whether to prefetch task outcomes. ### Request Example ```swift import SpeziScheduler import Foundation let startOfWeek = Calendar.current.date(from: Calendar.current.dateComponents([.yearForWeekOfYear, .weekOfYear], from: .now))! let endOfWeek = Calendar.current.date(byAdding: .day, value: 7, to: startOfWeek)! do { let tasks = try scheduler.queryTasks( for: startOfWeek..`. An optional `Predicate` allows pre-filtering which tasks are considered. ### Method `queryEvents(for:predicate:)` ### Parameters - **for** (`Range`): The date range to query events within. - **predicate** (`Predicate`, optional): An optional predicate to filter tasks. ### Request Example ```swift import SpeziScheduler import Foundation let today = Calendar.current.startOfDay(for: .now) let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: today)! do { let events = try scheduler.queryEvents(for: today..) -> [Occurrence]` ### Request Example ```swift import SpeziScheduler import Foundation // One-time event tomorrow at noon let nextNoon = Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: .tomorrow)! let onceSchedule: Schedule = .once(at: nextNoon, duration: .duration(.seconds(3600))) // Daily at 8:30 AM, indefinitely let dailySchedule: Schedule = .daily(hour: 8, minute: 30, startingAt: .today) // Every 2 weeks on Wednesday at 10 AM, stopping after 10 occurrences let biweeklySchedule: Schedule = .weekly( interval: 2, weekday: .wednesday, hour: 10, minute: 0, startingAt: .today, end: .afterOccurrences(10) ) // Monthly on the 1st at 9 AM let monthlySchedule: Schedule = .monthly(day: 1, hour: 9, minute: 0, startingAt: .today) // Yearly on July 4th at midnight let yearlySchedule: Schedule = .yearly(month: 7, day: 4, hour: 0, minute: 0, startingAt: .today) // Custom recurrence: first weekend day of each month var customRule = Calendar.RecurrenceRule.monthly(calendar: .current, end: .afterOccurrences(6)) customRule.weekdays = [.nth(1, .saturday), .nth(1, .sunday)] customRule.setPositions = [1] let customSchedule = Schedule(startingAt: .today, recurrence: customRule) // Retrieve occurrences in a range let occurrences = dailySchedule.occurrences(in: Date.now..