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/DomainDigDeepLink.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/DomainDigDeepLink.swift')
| -rw-r--r-- | Shared/DomainDigDeepLink.swift | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/Shared/DomainDigDeepLink.swift b/Shared/DomainDigDeepLink.swift new file mode 100644 index 0000000..ba1d64b --- /dev/null +++ b/Shared/DomainDigDeepLink.swift @@ -0,0 +1,71 @@ +import Foundation + +/// Shared builder/parser for the `domaindig://` URL scheme, used by the intents +/// and app (to open/route) and by the widget (to deep-link into a domain). +/// +/// Lives in `Shared/` so it compiles into both the app and the widget target. +/// It relies only on Foundation and no actor isolation, so it is safe in the +/// widget extension (`APPLICATION_EXTENSION_API_ONLY`). +enum DomainDigDeepLink { + static let scheme = "domaindig" + + enum Action: Equatable { + case inspect(String) + case watch(String) + case detail(String) + case sweep + + var host: String { + switch self { + case .inspect: return "inspect" + case .watch: return "watch" + case .detail: return "domain" + case .sweep: return "sweep" + } + } + + /// The domain the action targets, if any. `.sweep` has no domain. + var domain: String? { + switch self { + case let .inspect(domain), let .watch(domain), let .detail(domain): + return domain + case .sweep: + return nil + } + } + } + + static func url(for action: Action) -> URL { + var components = URLComponents() + components.scheme = scheme + components.host = action.host + if let domain = action.domain { + components.queryItems = [URLQueryItem(name: "domain", value: domain)] + } + // The scheme and host are fixed and any domain is percent-encoded by + // URLComponents, so this is always a valid URL. + return components.url! + } + + static func action(from url: URL) -> Action? { + guard url.scheme == scheme else { return nil } + + if url.host() == "sweep" { + return .sweep + } + + let domain = URLComponents(url: url, resolvingAgainstBaseURL: false)? + .queryItems? + .first { $0.name == "domain" }? + .value? + .trimmingCharacters(in: .whitespacesAndNewlines) ?? "" + guard !domain.isEmpty else { return nil } + + switch url.host() { + case "inspect": return .inspect(domain) + case "watch": return .watch(domain) + case "domain": return .detail(domain) + default: return nil + } + } +} |
