AutostartManager.swift

2.3 KB · raw

1import Foundation2import ServiceManagement34@MainActor5protocol AutostartManaging: AnyObject {6  var isEnabled: Bool { get set }7}89class AutostartManager: AutostartManaging {10  static let shared = AutostartManager()1112  private let launchAgentURL: URL13  private let bundleIdentifier = "com.june.Haven"14  private let executablePath: () -> String?15  private let writePlist: (Data, URL) throws -> Void16  private let removePlist: (URL) throws -> Void1718  init(19    libraryDirectory: URL? = nil,20    executablePath: @escaping () -> String? = { Bundle.main.executablePath },21    writePlist: @escaping (Data, URL) throws -> Void = { data, url in try data.write(to: url) },22    removePlist: @escaping (URL) throws -> Void = { try FileManager.default.removeItem(at: $0) }23  ) {24    let libraryDir =25      libraryDirectory26      ?? FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!27    let launchAgentsDir = libraryDir.appendingPathComponent("LaunchAgents")2829    // Ensure directory exists30    try? FileManager.default.createDirectory(at: launchAgentsDir, withIntermediateDirectories: true)3132    self.launchAgentURL = launchAgentsDir.appendingPathComponent("\(bundleIdentifier).plist")33    self.executablePath = executablePath34    self.writePlist = writePlist35    self.removePlist = removePlist36  }3738  var isEnabled: Bool {39    get {40      return FileManager.default.fileExists(atPath: launchAgentURL.path)41    }42    set {43      if newValue {44        enable()45      } else {46        disable()47      }48    }49  }5051  private func enable() {52    guard !isEnabled else { return }5354    // Point to the actual executable inside the bundle55    guard let appPath = executablePath() else { return }5657    let plistContent: [String: Any] = [58      "Label": bundleIdentifier,59      "ProgramArguments": [appPath],60      "RunAtLoad": true,61      "ProcessType": "Interactive",62    ]6364    do {65      let data = try PropertyListSerialization.data(66        fromPropertyList: plistContent, format: .xml, options: 0)67      try writePlist(data, launchAgentURL)68    } catch {69      print("Failed to enable autostart: \(error)")70    }71  }7273  private func disable() {74    guard isEnabled else { return }7576    do {77      try removePlist(launchAgentURL)78    } catch {79      print("Failed to disable autostart: \(error)")80    }81  }82}