Files
uskey/Sources/AppDelegate.swift
loveuer 72fd1ef2d6 Fix: Auto-initialize app after accessibility permission grant
Problem:
- When user first launches the app without accessibility permissions,
  the system shows a permission dialog
- After granting permissions, the app was already running but not
  fully initialized
- User couldn't reopen the app (already running) and functionality
  wasn't available

Solution:
- Implemented permission monitoring with Timer (checks every 1 second)
- Deferred app initialization until permissions are granted
- Split initialization into initializeApp() method
- Automatically initialize once permissions are detected

User Experience:
- User grants permission in System Preferences
- App automatically detects permission change
- App initializes and starts working without restart
- Updated alert message to inform user about auto-start

Technical Changes:
- Added permissionCheckTimer for monitoring
- Added initializeApp() for deferred initialization
- Store config and keyMapper as instance variables
- Use Task { @MainActor } for timer callback
- Changed alert style from warning to informational

🤖 Generated with [Qoder](https://qoder.com)
2025-12-02 18:10:53 +08:00

131 lines
4.9 KiB
Swift

@preconcurrency import Cocoa
@preconcurrency import ApplicationServices
@MainActor
class AppDelegate: NSObject, NSApplicationDelegate {
private var statusBarController: StatusBarController?
private var eventTapManager: EventTapManager?
private var config: Config?
private var keyMapper: KeyMapper?
private var permissionCheckTimer: Timer?
func applicationDidFinishLaunching(_ notification: Notification) {
Logger.setup()
Logger.info("uskey - macOS Keyboard Remapper")
Logger.info("================================")
Logger.info("App bundle path: \(Bundle.main.bundlePath)")
config = loadConfig()
Logger.logLevel = config!.log.level
Logger.info("Checking accessibility permissions...")
if !checkAccessibilityPermissions() {
Logger.warning("Accessibility permissions not granted, waiting for user to grant permissions...")
showAccessibilityAlert()
startPermissionMonitoring()
return
}
Logger.info("Accessibility permissions granted")
initializeApp()
}
func applicationWillTerminate(_ notification: Notification) {
permissionCheckTimer?.invalidate()
eventTapManager?.stop()
Logger.info("Application terminated")
Logger.cleanup()
}
private func initializeApp() {
guard let config = config else {
Logger.error("Configuration not loaded")
return
}
let keyMapper = KeyMapper(fromConfig: config)
self.keyMapper = keyMapper
keyMapper.printMappings()
eventTapManager = EventTapManager(keyMapper: keyMapper)
statusBarController = StatusBarController(eventTapManager: eventTapManager!, config: config)
statusBarController?.setupStatusBar()
Logger.info("Attempting to start event monitoring...")
if eventTapManager!.start() {
Logger.info("Event monitoring started successfully")
} else {
Logger.error("Failed to start event monitoring - check if app has accessibility permissions")
}
Logger.info("Application ready")
}
private func startPermissionMonitoring() {
Logger.info("Starting permission monitoring...")
permissionCheckTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
Task { @MainActor in
guard let self = self else { return }
let trusted = AXIsProcessTrusted()
if trusted {
Logger.info("Accessibility permissions granted! Initializing app...")
self.permissionCheckTimer?.invalidate()
self.permissionCheckTimer = nil
self.initializeApp()
}
}
}
}
private func loadConfig() -> Config {
let configPath = Config.getConfigPath()
Logger.info("Loading config from: \(configPath)")
do {
let config = try Config.load(from: configPath)
Logger.info("Configuration loaded successfully")
return config
} catch {
Logger.warning("Failed to load config: \(error.localizedDescription)")
Logger.info("Creating default configuration...")
do {
try Config.createDefault(at: configPath)
Logger.info("Default configuration created at: \(configPath)")
return try Config.load(from: configPath)
} catch {
Logger.error("Failed to create default config: \(error.localizedDescription)")
Logger.warning("Using fallback configuration")
return Config()
}
}
}
private func checkAccessibilityPermissions() -> Bool {
let trusted = AXIsProcessTrusted()
Logger.debug("AXIsProcessTrusted result: \(trusted)")
if !trusted {
Logger.info("Requesting accessibility permissions...")
let options = [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary
AXIsProcessTrustedWithOptions(options)
}
return trusted
}
private func showAccessibilityAlert() {
let alert = NSAlert()
alert.messageText = "Accessibility Permissions Required"
alert.informativeText = "uskey needs accessibility permissions to remap keyboard keys.\n\nPlease:\n1. Click 'Open System Preferences' in the dialog that appeared\n2. Enable uskey in the Accessibility list\n\nThe app will automatically start once permissions are granted."
alert.alertStyle = .informational
alert.addButton(withTitle: "OK")
DispatchQueue.main.async {
alert.runModal()
}
}
}