summaryrefslogtreecommitdiff
path: root/DomainDig/AppInfo.swift
blob: 28b0826aba825c8c4d3def991d7fe3438f930cf3 (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
import Foundation
import UIKit

/// Runtime app metadata read from the bundle, plus the locally-assembled
/// diagnostics used to prefill an issue report. Nothing here touches the network
/// or collects any identifier beyond the app version, iOS version, and hardware
/// model string.
enum AppInfo {
    /// CFBundleShortVersionString (marketing version), falling back to the
    /// compiled-in `AppVersion.current` if the Info.plist key is missing.
    static var marketingVersion: String {
        Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? AppVersion.current
    }

    /// CFBundleVersion (build number).
    static var buildNumber: String {
        Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "—"
    }

    /// "5.0.0 (build 45)" — version alongside build, as the App Info row shows it.
    static var versionDisplay: String {
        "\(marketingVersion) (build \(buildNumber))"
    }

    static let minimumOS = "17.6"

    static var systemVersion: String {
        UIDevice.current.systemVersion
    }

    /// Hardware model identifier (e.g. "iPhone15,2") — a model string, not a
    /// per-device identifier.
    static var deviceModel: String {
        var systemInfo = utsname()
        uname(&systemInfo)
        let identifier = withUnsafeBytes(of: &systemInfo.machine) { raw -> String in
            let bytes = raw.prefix { $0 != 0 }
            return String(decoding: bytes, as: UTF8.self)
        }
        return identifier.isEmpty ? "Unknown" : identifier
    }

    /// Diagnostics prefilled into an issue report. Shown to the user before any
    /// send — never collected silently.
    static var diagnosticsReport: String {
        """
        DomainDig \(versionDisplay)
        iOS \(systemVersion)
        Device \(deviceModel)
        """
    }
}