import AppKit import Dependencies import XcodeInspector public extension NSWorkspace { static func activateThisApp(delay: TimeInterval = 0.3) { Task { @MainActor in try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000)) // NSApp.activate may fail. And since macOS 14, it looks like the app needs other // apps to call `yieldActivationToApplication` to activate itself? let activated = NSRunningApplication.current .activate(options: [.activateIgnoringOtherApps]) if activated { return } // Fallback solution let appleScript = """ tell application "System Events" set frontmost of the first process whose unix id is \ \(ProcessInfo.processInfo.processIdentifier) to true end tell """ try await runAppleScript(appleScript) } } static func activatePreviousActiveApp(delay: TimeInterval = 0.2) { Task { @MainActor in guard let app = await XcodeInspector.shared.safe.previousActiveApplication else { return } try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000)) _ = app.activate() } } static func activatePreviousActiveXcode(delay: TimeInterval = 0.2) { Task { @MainActor in guard let app = await XcodeInspector.shared.safe.latestActiveXcode else { return } try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000)) _ = app.activate() } } } struct ActivateThisAppDependencyKey: DependencyKey { static var liveValue: () -> Void = { NSWorkspace.activateThisApp() } } struct ActivatePreviousActiveAppDependencyKey: DependencyKey { static var liveValue: () -> Void = { NSWorkspace.activatePreviousActiveApp() } } struct ActivatePreviousActiveXcodeDependencyKey: DependencyKey { static var liveValue: () -> Void = { NSWorkspace.activatePreviousActiveXcode() } } public extension DependencyValues { var activateThisApp: () -> Void { get { self[ActivateThisAppDependencyKey.self] } set { self[ActivateThisAppDependencyKey.self] = newValue } } var activatePreviousActiveApp: () -> Void { get { self[ActivatePreviousActiveAppDependencyKey.self] } set { self[ActivatePreviousActiveAppDependencyKey.self] = newValue } } var activatePreviousActiveXcode: () -> Void { get { self[ActivatePreviousActiveXcodeDependencyKey.self] } set { self[ActivatePreviousActiveXcodeDependencyKey.self] = newValue } } } @discardableResult func runAppleScript(_ appleScript: String) async throws -> String { let task = Process() task.launchPath = "/usr/bin/osascript" task.arguments = ["-e", appleScript] let outpipe = Pipe() task.standardOutput = outpipe task.standardError = Pipe() return try await withUnsafeThrowingContinuation { continuation in do { task.terminationHandler = { _ in do { if let data = try outpipe.fileHandleForReading.readToEnd(), let content = String(data: data, encoding: .utf8) { continuation.resume(returning: content) return } continuation.resume(returning: "") } catch { continuation.resume(throwing: error) } } try task.run() } catch { continuation.resume(throwing: error) } } }