aboutsummaryrefslogtreecommitdiff
path: root/Shared/DomainDigDeepLink.swift
blob: ba1d64b86c682e03191be2920ee22bcce6618a32 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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
        }
    }
}