83 lines
2.8 KiB
Swift
83 lines
2.8 KiB
Swift
import Foundation
|
|
import ServiceManagement
|
|
import AppKit
|
|
|
|
final class AppPreferences: ObservableObject {
|
|
|
|
@Published var port: Int {
|
|
didSet { UserDefaults.standard.set(port, forKey: "daemonPort") }
|
|
}
|
|
@Published var showInDock: Bool {
|
|
didSet {
|
|
UserDefaults.standard.set(showInDock, forKey: "showInDock")
|
|
applyDockPolicy()
|
|
}
|
|
}
|
|
@Published var developmentTeam: String {
|
|
didSet { UserDefaults.standard.set(developmentTeam, forKey: "developmentTeam") }
|
|
}
|
|
@Published var allowProvisioningUpdates: Bool {
|
|
didSet { UserDefaults.standard.set(allowProvisioningUpdates, forKey: "allowProvisioningUpdates") }
|
|
}
|
|
@Published var buildConfiguration: String {
|
|
didSet { UserDefaults.standard.set(buildConfiguration, forKey: "buildConfiguration") }
|
|
}
|
|
|
|
var launchAtLogin: Bool {
|
|
get { SMAppService.mainApp.status == .enabled }
|
|
set {
|
|
do {
|
|
if newValue { try SMAppService.mainApp.register() }
|
|
else { try SMAppService.mainApp.unregister() }
|
|
objectWillChange.send()
|
|
} catch { print("SMAppService error: \(error)") }
|
|
}
|
|
}
|
|
|
|
var tailscaleIP: String {
|
|
TailscaleDetector.currentIP() ?? "Not detected"
|
|
}
|
|
|
|
init() {
|
|
let d = UserDefaults.standard
|
|
self.port = d.integer(forKey: "daemonPort").nonZero ?? 8080
|
|
self.showInDock = d.bool(forKey: "showInDock")
|
|
self.developmentTeam = d.string(forKey: "developmentTeam") ?? ""
|
|
self.allowProvisioningUpdates = d.object(forKey: "allowProvisioningUpdates") as? Bool ?? true
|
|
self.buildConfiguration = d.string(forKey: "buildConfiguration") ?? "Debug"
|
|
applyDockPolicy()
|
|
}
|
|
|
|
private func applyDockPolicy() {
|
|
DispatchQueue.main.async {
|
|
NSApp.setActivationPolicy(self.showInDock ? .regular : .accessory)
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension Int {
|
|
var nonZero: Int? { self == 0 ? nil : self }
|
|
}
|
|
|
|
enum TailscaleDetector {
|
|
static func currentIP() -> String? {
|
|
let paths = [
|
|
"/Applications/Tailscale.app/Contents/MacOS/Tailscale",
|
|
"/usr/local/bin/tailscale",
|
|
"/opt/homebrew/bin/tailscale"
|
|
]
|
|
guard let exe = paths.first(where: { FileManager.default.fileExists(atPath: $0) }) else {
|
|
return nil
|
|
}
|
|
let p = Process()
|
|
p.executableURL = URL(fileURLWithPath: exe)
|
|
p.arguments = ["ip", "--4"]
|
|
let pipe = Pipe()
|
|
p.standardOutput = pipe
|
|
p.standardError = Pipe()
|
|
try? p.run(); p.waitUntilExit()
|
|
return String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)?
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
.components(separatedBy: "\n").first
|
|
}
|
|
}
|