krz/domain-dig
an ios app for DNS & SSL analysis
clone: git clone https://gitbay.org/krz/domain-dig.git
v5.0.1: Shared/DomainDigDeepLink.swift · raw
1import Foundation
2
3/// Shared builder/parser for the `domaindig://` URL scheme, used by the intents
4/// and app (to open/route) and by the widget (to deep-link into a domain).
5///
6/// Lives in `Shared/` so it compiles into both the app and the widget target.
7/// It relies only on Foundation and no actor isolation, so it is safe in the
8/// widget extension (`APPLICATION_EXTENSION_API_ONLY`).
9enum DomainDigDeepLink {
10 static let scheme = "domaindig"
11
12 enum Action: Equatable {
13 case inspect(String)
14 case watch(String)
15 case detail(String)
16 case sweep
17
18 var host: String {
19 switch self {
20 case .inspect: return "inspect"
21 case .watch: return "watch"
22 case .detail: return "domain"
23 case .sweep: return "sweep"
24 }
25 }
26
27 /// The domain the action targets, if any. `.sweep` has no domain.
28 var domain: String? {
29 switch self {
30 case let .inspect(domain), let .watch(domain), let .detail(domain):
31 return domain
32 case .sweep:
33 return nil
34 }
35 }
36 }
37
38 static func url(for action: Action) -> URL {
39 var components = URLComponents()
40 components.scheme = scheme
41 components.host = action.host
42 if let domain = action.domain {
43 components.queryItems = [URLQueryItem(name: "domain", value: domain)]
44 }
45 // The scheme and host are fixed and any domain is percent-encoded by
46 // URLComponents, so this is always a valid URL.
47 return components.url!
48 }
49
50 static func action(from url: URL) -> Action? {
51 guard url.scheme == scheme else { return nil }
52
53 if url.host() == "sweep" {
54 return .sweep
55 }
56
57 let domain = URLComponents(url: url, resolvingAgainstBaseURL: false)?
58 .queryItems?
59 .first { $0.name == "domain" }?
60 .value?
61 .trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
62 guard !domain.isEmpty else { return nil }
63
64 switch url.host() {
65 case "inspect": return .inspect(domain)
66 case "watch": return .watch(domain)
67 case "domain": return .detail(domain)
68 default: return nil
69 }
70 }
71}