# Lendair iOS Notifications ## Overview SwiftUI implementation of the notifications feature for the Lendair iOS app. ## Architecture ### MVVM Pattern - **View**: `Views/` - SwiftUI views for notification display - **ViewModel**: `ViewModels/` - State management and business logic - **Service**: `Services/` - Data layer with API communication - **Model**: `Models/` - Data structures and type definitions ### File Structure ``` Lendair/ ├── Models/ │ └── Notification.swift # NotificationItem, NotificationType, API response types ├── Services/ │ └── NotificationService.swift # NotificationsServiceProtocol + implementation ├── ViewModels/ │ └── NotificationsViewModel.swift # State management, mark-as-read actions ├── Views/ │ ├── NotificationsView.swift # Main notifications list screen │ └── NotificationRowView.swift # Individual notification row └── README.md ``` ## Components ### NotificationsView (`Views/NotificationsView.swift`) - Main navigation container for the notifications screen - Pull-to-refresh via `.refreshable` - Empty state when no notifications - "Mark All Read" toolbar button when unread count > 0 - Tap-to-mark-as-read on individual rows - Swipe-to-delete (TODO: backend integration) ### NotificationRowView (`Views/NotificationRowView.swift`) - Individual notification list item - Type-specific SF Symbol icon with color coding - Read/unread indicator (blue dot) - Relative timestamp display ### NotificationsViewModel (`ViewModels/NotificationsViewModel.swift`) - `@Published notifications` — sorted by createdAt descending - `@Published isLoading` — loading state for UI feedback - `@Published error` — typed error state (NotificationError) - `fetchNotifications()` — loads from service - `markAsRead(id:)` — marks single notification, updates local state - `markAllAsRead()` — marks all unread, updates local state - `unreadCount` — computed property for badge display ### NotificationsService (`Services/NotificationService.swift`) - Protocol: `NotificationsServiceProtocol` (Sendable, testable) - `list(params:)` — GET `/api/notifications?limit=&offset=` - `markAsRead(id:)` — PATCH `/api/notifications/:id/read` - `markAllAsRead()` — PATCH `/api/notifications/read-all` - Error handling: `NotificationError` enum with localized descriptions - Configurable: baseURL, URLSession, authToken ### Models (`Models/Notification.swift`) - `NotificationItem` — Identifiable, Equatable, Codable - `NotificationType` — 6 cases with icon/color mappings - `NotificationListParams` — pagination parameters - `NotificationListResponse`, `NotificationMarkAsReadResponse`, `NotificationMarkAllReadResponse` — API response types ## Notification Types | Type | Icon | Color | |------|------|-------| | `LOAN_APPROVED` | checkmark.circle.fill | Green | | `LOAN_REJECTED` | xmark.circle.fill | Red | | `PAYMENT_RECEIVED` | arrow.down.circle.fill | Green | | `PAYMENT_DUE` | exclamationmark.circle.fill | Orange | | `NEW_LENDER` | person.circle.fill | Blue | | `SYSTEM_UPDATE` | info.circle.fill | Gray | ## API Endpoints | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/api/notifications?limit=&offset=` | List notifications | | PATCH | `/api/notifications/:id/read` | Mark single as read | | PATCH | `/api/notifications/read-all` | Mark all as read | ## Testing Tests are in `LendairTests/NotificationServiceTests.swift`: - 12 ViewModel tests (fetch, mark-as-read, mark-all-read, unread count, refresh, error handling) - 6 Model tests (icons, colors, equality, raw values, params) - Uses `MockNotificationsService` conforming to `NotificationsServiceProtocol` ## Usage ```swift // In your MainTabView or navigation stack NavigationStack { NotificationsView() } ``` ## Future Enhancements 1. **Push Notifications**: Integrate with UNUserNotificationCenter 2. **Notification Preferences**: Allow users to customize notification types 3. **Deep Linking**: Navigate to relevant screens when tapping notifications 4. **Offline Support**: Cache notifications locally with Core Data 5. **Analytics**: Track notification engagement metrics