Files
Kordant/iOS/KordantWidgets/KordantWidgets.swift
Michael Freno e33ddf3002 feat: complete Tasks 21-28 — backend integration, security hardening, UI tests & CI
- Add Apple Sign-In backend (JWKS verification, account linking, session management)
- Implement push notification deep linking with NotificationDeepLinkRouter
- Add jailbreak detection, runtime integrity monitoring, secure enclave service
- Implement OAuth social login, token refresh, and secure logout flows
- Add image caching (memory/disk), optimizer, upload queue, async semaphore
- Implement notification analytics, type preferences, and category setup
- Expand UI test suite with UITestBase, accessibility, auth flow, performance tests
- Add CI pipeline for iOS UI tests (3 device sizes) and performance benchmarks
- Restructure Xcode project to manual groups with KordantWidgets target
- Add SwiftLint, Swift Collections/Algorithms/GoogleSignIn dependencies
- Update project.yml for XcodeGen with new targets and configurations
2026-06-02 15:01:38 -04:00

164 lines
5.0 KiB
Swift

import SwiftUI
import WidgetKit
// MARK: - Widget Bundle
@main
struct KordantWidgets: WidgetBundle {
var body: some Widget {
ThreatScoreWidget() // systemSmall
AlertSummaryWidget() // systemMedium
FullDashboardWidget() // systemLarge
}
}
// MARK: - Timeline Provider
struct KordantWidgetProvider: IntentTimelineProvider {
typealias Entry = WidgetEntry
typealias Intent = KordantWidgetConfigurationIntent
func placeholder(in context: Context) -> WidgetEntry {
WidgetEntry(
date: Date(),
widgetData: .placeholder,
severityFilter: .all,
isPlaceholder: true
)
}
func getSnapshot(
for configuration: KordantWidgetConfigurationIntent,
in context: Context,
completion: @escaping (WidgetEntry) -> Void
) {
Task {
let data = WidgetDataManager.shared.load() ?? .placeholder
let entry = WidgetEntry(
date: Date(),
widgetData: data,
severityFilter: configuration.severityFilter,
isPlaceholder: context.isPreview
)
completion(entry)
}
}
func getTimeline(
for configuration: KordantWidgetConfigurationIntent,
in context: Context,
completion: @escaping (Timeline<WidgetEntry>) -> Void
) {
Task {
let data = WidgetDataManager.shared.load() ?? .unavailable
let entry = WidgetEntry(
date: Date(),
widgetData: data,
severityFilter: configuration.severityFilter,
isPlaceholder: false
)
// Widgets must refresh at most every 15 minutes per system policy.
let nextRefresh = Calendar.current.date(
byAdding: .minute,
value: 15,
to: Date()
) ?? Date().addingTimeInterval(900)
// If data is unavailable, retry sooner (5 minutes) to pick up
// initial data after the user opens the app.
let refreshDate = data == .unavailable
? Date().addingTimeInterval(300)
: nextRefresh
let timeline = Timeline(entries: [entry], policy: .after(refreshDate))
completion(timeline)
}
}
}
// MARK: - System Small: Threat Score Gauge
struct ThreatScoreWidget: Widget {
let kind: String = "com.frenocorp.kordant.widget.threatScore"
var body: some WidgetConfiguration {
IntentConfiguration(
kind: kind,
intent: KordantWidgetConfigurationIntent.self,
provider: KordantWidgetProvider()
) { entry in
KordantWidgetsEntryView(entry: entry)
.containerBackground(.widgetBackground, for: .widget)
}
.configurationDisplayName("Threat Score")
.description("Your current Kordant threat score at a glance.")
.supportedFamilies([.systemSmall])
.contentMarginsDisabled()
}
}
// MARK: - System Medium: Threat Score + Recent Alerts
struct AlertSummaryWidget: Widget {
let kind: String = "com.frenocorp.kordant.widget.alertSummary"
var body: some WidgetConfiguration {
IntentConfiguration(
kind: kind,
intent: KordantWidgetConfigurationIntent.self,
provider: KordantWidgetProvider()
) { entry in
KordantWidgetsEntryView(entry: entry)
.containerBackground(.widgetBackground, for: .widget)
}
.configurationDisplayName("Alert Summary")
.description("Your threat score with up to 2 recent alerts.")
.supportedFamilies([.systemMedium])
.contentMarginsDisabled()
}
}
// MARK: - System Large: Full Dashboard
struct FullDashboardWidget: Widget {
let kind: String = "com.frenocorp.kordant.widget.fullDashboard"
var body: some WidgetConfiguration {
IntentConfiguration(
kind: kind,
intent: KordantWidgetConfigurationIntent.self,
provider: KordantWidgetProvider()
) { entry in
KordantWidgetsEntryView(entry: entry)
.containerBackground(.widgetBackground, for: .widget)
}
.configurationDisplayName("Security Dashboard")
.description("Full dashboard with threat score, alerts, stats, and quick actions.")
.supportedFamilies([.systemLarge])
.contentMarginsDisabled()
}
}
// MARK: - Entry View Router
struct KordantWidgetsEntryView: View {
let entry: WidgetEntry
@Environment(\.widgetFamily) var family
var body: some View {
Group {
switch family {
case .systemSmall:
SmallWidgetView(entry: entry)
case .systemMedium:
MediumWidgetView(entry: entry)
case .systemLarge:
LargeWidgetView(entry: entry)
@unknown default:
SmallWidgetView(entry: entry)
}
}
}
}