# 32. iOS App — Dashboard and Service Screens (DarkWatch, VoicePrint, SpamShield, etc.) meta: id: kordant-unified-restructure-32 feature: kordant-unified-restructure priority: P1 depends_on: [kordant-unified-restructure-28, kordant-unified-restructure-29, kordant-unified-restructure-30, kordant-unified-restructure-31] tags: [ios, swiftui, dashboard, services, mobile] objective: - Build the main dashboard and all service-specific screens for the iOS app. These should mirror the web app's functionality while using native iOS UI patterns (lists, forms, sheets, charts). deliverables: - `iOS/Kordant/Views/Dashboard/DashboardView.swift` — Main dashboard: - Scrollable vertical stack of widgets - Threat score gauge (circular progress ring) - Recent alerts list (top 5) - Service summary cards (DarkWatch, VoicePrint, SpamShield, HomeTitle, RemoveBrokers) - Quick action buttons - Pull-to-refresh - `iOS/Kordant/Views/Dashboard/AlertDetailView.swift` — Alert detail: - Full alert information - Correlated alerts section - "Mark as resolved" and "False positive" actions - `iOS/Kordant/Views/Services/DarkWatchView.swift` — DarkWatch screen: - Watchlist items list with add/remove - Exposures list with severity badges - Exposure detail with source info and recommendations - "Run scan" button - `iOS/Kordant/Views/Services/VoicePrintView.swift` — VoicePrint screen: - Voice enrollments list with play/delete - "Enroll voice" button → opens recording sheet - Analysis history list - Analysis detail with confidence and verdict - `iOS/Kordant/Views/Services/SpamShieldView.swift` — SpamShield screen: - Blocked calls/SMS statistics - Custom rules list with add/edit/delete - Number check input with result - "Report spam" button - `iOS/Kordant/Views/Services/HomeTitleView.swift` — HomeTitle screen: - Watched properties list with add/remove - Property detail with snapshots and changes - Map view showing property location (MapKit) - "Run scan" button - `iOS/Kordant/Views/Services/RemoveBrokersView.swift` — RemoveBrokers screen: - Broker registry list with search/filter - Removal requests list with status - Request detail with progress timeline - "Start removal" button - `iOS/Kordant/Views/Settings/SettingsView.swift` — Settings: - Account info, subscription details - Notification preferences toggle - Theme selection (light/dark/system) - Biometric auth toggle - Family group management - Logout button steps: 1. Create view directories: `Dashboard/`, `Services/`, `Settings/`. 2. **DashboardView**: - `ScrollView` with `VStack` - Threat score: `Canvas` or custom `View` with circular progress ring - Alerts: `ForEach` over `alerts` array, each as `ShieldCard` row - Service cards: horizontal scroll or grid of compact cards - Quick actions: `HStack` of icon buttons - `.refreshable` modifier for pull-to-refresh 3. **AlertDetailView**: - `ScrollView` with sections - Severity badge at top - Description and metadata - Correlated alerts in a nested list - Action buttons at bottom 4. **DarkWatchView**: - `List` with sections: Watchlist, Exposures - Watchlist items: swipe-to-delete, tap to edit - Add button → sheet with form - Exposures: tap to open detail 5. **VoicePrintView**: - Enrollments section with audio playback (AVFoundation) - "Enroll" button → `RecordingView` sheet with waveform visualization - Analysis list with verdict color coding 6. **SpamShieldView**: - Stats cards at top - Rules list with toggle for active/inactive - Number check: `ShieldTextField` + check button → result card 7. **HomeTitleView**: - Properties list with address and status - Add property: address search with geocoding - Property detail: `Map` view, snapshot history, changes list 8. **RemoveBrokersView**: - Broker list with category filters - Request list with status badges and progress bars - Start removal: sheet with broker selection and personal info form 9. **SettingsView**: - `Form` with sections: Account, Preferences, Family, Danger Zone - Use native `Toggle`, `Picker`, `NavigationLink` - Subscription info with upgrade button 10. Wire all views to API client (task 31): - Use `@StateObject` view models that call TRPCBridge - Show `ShieldSkeleton` while loading - Show `ShieldEmptyState` when lists are empty - Handle errors with `ShieldToast` or inline messages steps: - Unit: Each view model fetches data correctly from mocked API - Unit: DashboardView displays correct widget count - Visual: All screens use theme tokens and adapt to dark mode - E2E: Navigate through all service screens and verify data loads - E2E: Perform CRUD operations (add watchlist item, delete enrollment, create rule) acceptance_criteria: - [ ] Dashboard displays threat score, alerts, service summaries, and quick actions - [ ] All 5 service screens (DarkWatch, VoicePrint, SpamShield, HomeTitle, RemoveBrokers) load and display data - [ ] Each service screen supports core CRUD operations - [ ] Alert detail shows full information and correlation group - [ ] Settings screen allows managing account, preferences, and family - [ ] Pull-to-refresh updates dashboard data - [ ] All screens show loading skeletons and empty states appropriately - [ ] Navigation between screens works smoothly with native iOS transitions validation: - Launch app, login, and verify dashboard renders with real data - Tap each service tab and verify screen loads - Add a watchlist item in DarkWatch and verify it appears in list - Delete a voice enrollment and verify it disappears - Create a spam rule and verify it applies - Toggle settings and verify preferences persist - Run unit tests via Xcode Cmd+U notes: - Use native iOS patterns: `List` for scrollable data, `Form` for settings, `Sheet` for modals, `.contextMenu` for actions. - For the threat score gauge, consider using a SwiftUI charting library like `SwiftUI Charts` (iOS 16+) or a custom `Canvas` drawing. - The map in HomeTitle should use `Map` (iOS 17+) or `MKMapView` wrapped in `UIViewRepresentable` for older versions. - Voice recording requires microphone permission. Add `NSMicrophoneUsageDescription` to Info.plist. - Keep view models separate from views for testability. View models should own the `@Published` state and API calls. - Consider using `Task` and `.task` modifier for async data loading instead of `onAppear` to handle cancellation correctly.