diff options
| author | Christian Cleberg <[email protected]> | 2026-07-17 00:27:45 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-07-17 00:31:57 -0500 |
| commit | d3a27d06809e6744c092f9bed4bf0537843d0210 (patch) | |
| tree | 1527e935e11561dda4bc8c6cfaabdb92db5be95c /Shared/DomainDigWidgetData.swift | |
| parent | 685f84ad0a8d2dc7b62125d5d9c3a50b75ae2468 (diff) | |
| download | domain-dig-d3a27d06809e6744c092f9bed4bf0537843d0210.tar.gz domain-dig-d3a27d06809e6744c092f9bed4bf0537843d0210.tar.bz2 domain-dig-d3a27d06809e6744c092f9bed4bf0537843d0210.zip | |
Complete v4.5.0: Run Sweep intent, detail deep link, and portfolio widget
Finishes the v4.5.0 "Home Screen & Shortcuts reach" scope that the tag shipped
partially:
- Add RunSweepIntent (opens the app and runs refreshAllTrackedDomains via the
in-process router) and expose it in DomainDigShortcuts.
- Extend the domaindig:// scheme with `sweep` and `domain` (detail) actions;
route .detail to present TrackedDomainDetailView and .sweep to refresh the
watchlist. Move DomainDigDeepLink into Shared/ so the widget can build links.
- Add a WidgetKit extension (DomainDigWidgetExtension) with small/medium/large
Portfolio widgets showing health counts, per-domain status, and certificate
countdowns; tapping a domain deep-links into its detail.
- Share portfolio state via an App Group (group.net.cleberg.DomainDig): the app
writes a DomainDigWidgetData snapshot on launch/foreground and on watchlist
changes and reloads timelines; the widget reads the same store.
Diffstat (limited to 'Shared/DomainDigWidgetData.swift')
| -rw-r--r-- | Shared/DomainDigWidgetData.swift | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/Shared/DomainDigWidgetData.swift b/Shared/DomainDigWidgetData.swift new file mode 100644 index 0000000..5ceae48 --- /dev/null +++ b/Shared/DomainDigWidgetData.swift @@ -0,0 +1,82 @@ +import Foundation + +/// App Group + snapshot shared between the app (writer) and the widget (reader). +/// +/// Deliberately self-contained: it references no app-target types (TrackedDomain, +/// PortfolioSnapshot, DomainHealth, …) so it compiles unchanged into the widget +/// extension. The app maps its richer types into these DTOs. +enum DomainDigWidgetStore { + static let appGroupID = "group.net.cleberg.DomainDig" + private static let key = "widgetData" + + private static var defaults: UserDefaults? { + UserDefaults(suiteName: appGroupID) + } + + static func write(_ data: DomainDigWidgetData) { + guard let defaults, let encoded = try? JSONEncoder().encode(data) else { return } + defaults.set(encoded, forKey: key) + } + + static func read() -> DomainDigWidgetData? { + guard let defaults, + let encoded = defaults.data(forKey: key), + let data = try? JSONDecoder().decode(DomainDigWidgetData.self, from: encoded) + else { return nil } + return data + } +} + +struct DomainDigWidgetData: Codable, Sendable { + var generatedAt: Date + var totalDomains: Int + var healthyCount: Int + var warningCount: Int + var criticalCount: Int + var expiringSoonCount: Int + var unreachableCount: Int + var domains: [DomainDigWidgetDomain] + + static let placeholder = DomainDigWidgetData( + generatedAt: .distantPast, + totalDomains: 3, + healthyCount: 2, + warningCount: 1, + criticalCount: 0, + expiringSoonCount: 1, + unreachableCount: 0, + domains: [ + DomainDigWidgetDomain(domain: "example.com", isPinned: true, status: .healthy, certDaysRemaining: 240, lastChange: nil), + DomainDigWidgetDomain(domain: "cleberg.net", isPinned: false, status: .warning, certDaysRemaining: 21, lastChange: nil), + DomainDigWidgetDomain(domain: "example.org", isPinned: false, status: .healthy, certDaysRemaining: 88, lastChange: nil) + ] + ) + + /// Empty state used before the app has written any data. + static let empty = DomainDigWidgetData( + generatedAt: .distantPast, + totalDomains: 0, + healthyCount: 0, + warningCount: 0, + criticalCount: 0, + expiringSoonCount: 0, + unreachableCount: 0, + domains: [] + ) +} + +struct DomainDigWidgetDomain: Codable, Sendable, Identifiable { + var domain: String + var isPinned: Bool + var status: DomainDigWidgetStatus + var certDaysRemaining: Int? + var lastChange: Date? + + var id: String { domain } +} + +enum DomainDigWidgetStatus: String, Codable, Sendable { + case healthy + case warning + case critical +} |
