Local Notifications & Reminders
Relevant source files
The following files were used as context for generating this wiki page:
- Packages/Services/Sources/Services/Notification/NotificationServiceImpl.swift
- Features/ExpensesFeature/Sources/ExpensesFeature/Views/SubscriptionForm/SubscriptionFormViewModel.swift
- Features/ExpensesFeature/Sources/ExpensesFeature/Views/SubscriptionForm/SubscriptionFormView.swift
- Features/ExpensesFeature/Tests/ExpensesFeatureTests/Views/SubscriptionForm/SubscriptionFormReminderTests.swift
- Features/ExpensesFeature/Sources/ExpensesFeature/Views/SubscriptionDetail/SubscriptionDetailView.swift
The Local Notifications & Reminders system in Trackurr is designed to alert users about upcoming subscription renewals. By leveraging the iOS UserNotifications framework, the app schedules local alerts based on user-defined lead times, ensuring users can manage their recurring expenses proactively.
This system is integrated directly into the subscription lifecycle, triggering during the creation, modification, or cancellation of subscriptions. It provides a non-intrusive user experience by deferring permission requests until a user actively chooses to set a reminder.
System Architecture
The notification system follows a service-oriented architecture, where the UI layer interacts with a specialized service implementation to manage the scheduling and cancellation of alerts.
Components
| Component | Description |
|---|---|
NotificationServiceImpl | The core service implementation that interfaces with UNUserNotificationCenter. |
SubscriptionFormViewModel | Manages the logic for selecting reminders and triggering schedules during subscription saves. |
ReminderDateCalculator | Utility used by the service to determine the exact Date a notification should fire based on a renewal date and reminder preference. |
Subscription Domain Model | Contains the reminder property (of type Reminder) and the nextRenewalDate used as inputs. |
Sources: NotificationServiceImpl.swift:8-12, SubscriptionFormViewModel.swift:15-18
Logic Flow for Scheduling
The following diagram illustrates how a reminder is scheduled when a user saves a subscription:
Notification Service Implementation
The NotificationServiceImpl acts as the primary bridge to the iOS UserNotifications framework. It is responsible for authorization, scheduling requests, and clearing pending alerts.
Key Functions
requestPermission(): Requests authorization for alerts, sounds, and badges. Sources: NotificationServiceImpl.swift:20-26permissionStatus(): Returns the currentNotificationPermissionStatus(authorized, denied, notDetermined, or provisional). Sources: NotificationServiceImpl.swift:28-44scheduleReminder(for:): Processes aSubscriptionobject, calculates the trigger date, and adds aUNNotificationRequestto the system. It uses theSubscription.id.uuidStringas the notification identifier to allow for easy updates and cancellations. Sources: NotificationServiceImpl.swift:48-81cancelReminder(forSubscriptionId:): Removes pending notifications for a specific subscription using its UUID. Sources: NotificationServiceImpl.swift:85-87
Notification Content
Reminders are configured with the following content:
- Title: "Subscription Renewal"
- Body: "[Subscription Name] renews on [Formatted Date]"
- Sound: Default system sound
- Category:
subscription_reminder
Sources: NotificationServiceImpl.swift:60-67
UI Integration and User Experience
The notification system is exposed to the user through the SubscriptionFormView and SubscriptionDetailView.
Permission Management
To ensure a smooth onboarding experience, the app does not request notification permissions on the first launch. Instead, the request is deferred until the user attempts to set a non-none reminder.
Lifecycle Events
- On Appear: The view model refreshes the permission status to ensure the UI accurately reflects the system settings (e.g., if the user manually enabled them in the iOS Settings app). Sources: SubscriptionFormViewModel.swift:261-264
- On Save: The system automatically schedules or cancels the reminder based on the final form state. If a subscription is edited and the reminder is removed, the previous notification is explicitly cancelled. Sources: SubscriptionFormViewModel.swift:277-286
- On Stop/Resume: When a subscription is "Stopped" via the
SubscriptionDetailView, reminders are effectively disabled as the service checks for theisStoppedflag before scheduling. Sources: NotificationServiceImpl.swift:50, SubscriptionDetailView.swift:225-245
Testing Reminders
The system is heavily tested using SubscriptionFormReminderTests to ensure reliability of scheduling and cancellation.
Test Coverage Areas
| Test Category | Description |
|---|---|
| Scheduling | Verifies that scheduleReminder is called with correct data when saving new or edited subscriptions. |
| Cancellation | Ensures that changing a reminder to .none triggers cancelReminder for the existing ID. |
| Permission States | Validates that the UI reacts correctly to authorized, denied, and notDetermined statuses. |
| Settings Redirection | Confirms the "Settings" button correctly delegates to the urlOpener to open the app's system notification settings. |
Sources: SubscriptionFormReminderTests.swift:1-331
Summary
The Local Notifications & Reminders system provides a critical utility for tracking recurring expenses. By centralizing logic in NotificationServiceImpl and integrating it tightly with the SubscriptionFormViewModel, the application ensures that alerts remain synchronized with the user's data while maintaining a respect for user privacy and notification preferences.