Calendar & Renewals
Relevant source files
The following files were used as context for generating this wiki page:
- Features/CalendarFeature/Sources/CalendarFeature/Views/Calendar/CalendarView.swift
- Features/CalendarFeature/Sources/CalendarFeature/Views/Calendar/CalendarViewModel.swift
- Packages/Services/Sources/Services/SubscriptionAnalyticsImpl.swift
- Packages/Domain/Tests/DomainTests/BillingCycleTests.swift
- Packages/Services/Tests/ServicesTests/Notification/ReminderDateCalculatorTests.swift
- Packages/SharedUI/Sources/SharedUI/Components/Display/RenewalText.swift
The Calendar & Renewals system provides a visual and data-driven overview of recurring subscription payments. It enables users to track upcoming expenses through a monthly grid interface and detailed renewal lists. The system integrates subscription data with calendar logic to predict future billing dates based on various cycles.
This feature is primary located within the CalendarFeature module and relies on core services for analytics and date calculations. It serves as a critical component for financial planning by surfacing exactly when and how much will be charged to the user.
Sources: Features/CalendarFeature/Sources/CalendarFeature/Views/Calendar/CalendarView.swift, Packages/Services/Sources/Services/SubscriptionAnalyticsImpl.swift:49-70
Architecture and Data Flow
The Calendar system follows the MV (Model-View) pattern, utilizing the SwiftUI @Observable framework for state management. Data flows from the SubscriptionStore through a specialized SubscriptionAnalytics service, which calculates upcoming occurrences of subscriptions for a given month.
The following diagram illustrates the high-level relationship between components.
Sources: Features/CalendarFeature/Sources/CalendarFeature/Views/Calendar/CalendarViewModel.swift:15-35, Packages/Services/Sources/Services/SubscriptionAnalyticsImpl.swift:13-22
Components and Responsibilities
| Component | Responsibility |
|---|---|
CalendarView | Renders the monthly grid, weekday headers, and the list of renewals for selected or all days. |
CalendarViewModel | Manages navigation state (current month), date selection logic, and interacts with analytics services. |
SubscriptionAnalytics | Performs the heavy lifting of projecting subscription startDate into future renewal occurrences. |
UpcomingRenewal | A domain model representing a specific instance of a subscription renewal on a specific date. |
ReminderDateCalculator | Calculates the specific local notification trigger dates based on user-defined offsets. |
| Sources: Features/CalendarFeature/Sources/CalendarFeature/Views/Calendar/CalendarView.swift:65-124, Packages/Services/Sources/Services/SubscriptionAnalyticsImpl.swift:207-238, Packages/Services/Tests/ServicesTests/Notification/ReminderDateCalculatorTests.swift:31-48 |
Renewal Projection Logic
The core of the renewal system is the ability to project recurring dates. The SubscriptionAnalyticsImpl iterates through active subscriptions and calculates every billing event that falls within a requested month's boundaries.
Calculation Process
- Month Boundaries: The service defines the start and end of the target month using
Calendarcomponents. - Filter Active: Subscriptions must have a
startDatebefore the end of the target month and must not be stopped before the start of the month. - Iterative Projection: Starting from the
startDate, the system applies theBillingCycle(daily, weekly, monthly, yearly, or custom) to find every date that falls within the target range. - Days Until: For each projected date, the system calculates the
daysUntilRenewalrelative to the current system time.
Sources: Packages/Services/Sources/Services/SubscriptionAnalyticsImpl.swift:207-238, Packages/Domain/Tests/DomainTests/BillingCycleTests.swift:11-38
Sequence of Renewal Retrieval
User Interface and Interaction
The UI is centered around a TabView that acts as a pager for adjacent months. This allows for smooth programmatic and gesture-based navigation between the previous, current, and next months.
Grid Interaction Features
- Today Navigation: A toolbar item allows users to instantly reset the view to the current date and select "Today".
- Date Selection: Tapping a day in the grid filters the
renewalsListto show only subscriptions renewing on that specific day. - Icon Indicators: The calendar grid can display subscription icons or brand logos directly on the dates. This is configurable via user preferences.
- Grouped Renewals: The list view below the grid groups renewals by date and displays the subscription name, category color, and formatted money amount.
Sources: Features/CalendarFeature/Sources/CalendarFeature/Views/Calendar/CalendarView.swift:101-140, Features/CalendarFeature/Sources/CalendarFeature/Views/Calendar/CalendarViewModel.swift:44-60
Formatted Renewal Strings
The RenewalText component provides human-readable labels for upcoming dates, prioritizing relative terms:
- "Renews Today" (if date matches current day)
- "Renews Tomorrow" (if date is +1 day)
- "Renews on [Date]" (e.g., "15 Mar" for other dates)
Sources: Packages/SharedUI/Sources/SharedUI/Components/Display/RenewalText.swift:23-38
Notification and Reminders
While the calendar provides visual tracking, the system uses the ReminderDateCalculator to determine when to alert the user via local notifications.
| Reminder Type | Offset Calculation | Time of Day |
|---|---|---|
.sameDay | 0 days | 09:00 AM |
.oneDayBefore | -1 day | 09:00 AM |
.threeDaysBefore | -3 days | 09:00 AM |
.oneWeekBefore | -7 days | 09:00 AM |
.twoWeeksBefore | -14 days | 09:00 AM |
Sources: Packages/Services/Tests/ServicesTests/Notification/ReminderDateCalculatorTests.swift:31-137
The calculator ensures that if a renewal occurs on a boundary (e.g., January 1st), an offset like oneWeekBefore correctly crosses the year boundary to December 25th of the previous year. If the calculated notification time has already passed, the system returns nil to prevent stale alerts.
Sources: Packages/Services/Tests/ServicesTests/Notification/ReminderDateCalculatorTests.swift:195-212
Summary
The Calendar & Renewals module effectively bridges persistent data with time-based projections. By centralizing the logic for billing cycles and date arithmetic, the application ensures that users have a reliable, forward-looking view of their financial commitments across different timeframes and categories.