Before adding "every N weeks/months" support, found the recurrence
window math was independently hand-written in three places (main app's
TaskItem.isOccurrence, and two separate widget extensions' countdown
progress calculations) with no shared code to keep them in sync.
Extracted the math into Shared/RecurrenceRule.swift (pure Foundation,
no framework deps) compiled into both the app and widget extension
targets via a new project.yml Shared/ source path, and pointed all
three call sites at it. Verified every case is identical to prior
behavior at interval==1 by compiling and running a standalone test
driver directly against the real file with swiftc - 38/38 passing,
including Jan-31-monthly and Feb-29-leap-year-yearly edge cases.
Groundwork only - no recurrenceInterval field on TaskItem yet, the
actual interval feature comes next on top of this.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- KisaniCalWidget: 3 sizes sharing the app's design language
Small: date badge + kisaniCAL. wordmark + task count
Medium: badge + task list (3 items) + workout row
Large: badge + full task list (6 items) + workout row
- WidgetDataStore (Shared/): lightweight Codable types for App Group
UserDefaults cross-process data exchange
- WidgetBridge: TaskViewModel/WorkoutViewModel extensions that sync
data into App Group and call WidgetCenter.reloadAllTimelines()
- ContentView: calls syncWidget() on appear and scene becoming active
- project.yml + entitlements updated; project regenerated via xcodegen