11import AppKit
2+ import FileChangeChecker
3+ import os. log
24import Service
35import ServiceManagement
46import SwiftUI
@@ -11,9 +13,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
1113 private var statusBarItem : NSStatusItem !
1214
1315 func applicationDidFinishLaunching( _: Notification ) {
16+ // setup real-time suggestion controller
17+ _ = RealtimeSuggestionController . shared
18+ setupRestartOnUpdate ( )
19+ setupQuitOnUserTerminated ( )
20+
1421 NSApp . setActivationPolicy ( . accessory)
1522 buildStatusBarMenu ( )
16- AXIsProcessTrustedWithOptions ( nil )
1723 }
1824
1925 @objc private func buildStatusBarMenu( ) {
@@ -105,6 +111,70 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
105111 }
106112 UserDefaults . shared. set ( isOn, forKey: SettingsKey . realtimeSuggestionToggle)
107113 }
114+
115+ func setupRestartOnUpdate( ) {
116+ Task {
117+ guard let url = Bundle . main. executableURL else { return }
118+ let checker = await FileChangeChecker ( fileURL: url)
119+
120+ // If Xcode or Copilot for Xcode is made active, check if the executable of this program
121+ // is changed. If changed, restart the launch agent.
122+
123+ let sequence = NSWorkspace . shared. notificationCenter
124+ . notifications ( named: NSWorkspace . didActivateApplicationNotification)
125+ for await notification in sequence {
126+ try Task . checkCancellation ( )
127+ guard let app = notification
128+ . userInfo ? [ NSWorkspace . applicationUserInfoKey] as? NSRunningApplication ,
129+ app. isUserOfService
130+ else { continue }
131+ guard await checker. checkIfChanged ( ) else {
132+ os_log ( . info, " XPC Service is not updated, no need to restart. " )
133+ continue
134+ }
135+ os_log ( . info, " XPC Service will be restarted. " )
136+ #if DEBUG
137+ #else
138+ let manager = LaunchAgentManager (
139+ serviceIdentifier: serviceIdentifier,
140+ executablePath: Bundle . main. executablePath ?? " "
141+ )
142+ do {
143+ try await manager. restartLaunchAgent ( )
144+ } catch {
145+ os_log (
146+ . error,
147+ " XPC Service failed to restart. %{public}s " ,
148+ error. localizedDescription
149+ )
150+ }
151+ #endif
152+ }
153+ }
154+ }
155+
156+ func setupQuitOnUserTerminated( ) {
157+ Task {
158+ // Whenever Xcode or the host application quits, check if any of the two is running.
159+ // If none, quit the XPC service.
160+
161+ let sequence = NSWorkspace . shared. notificationCenter
162+ . notifications ( named: NSWorkspace . didTerminateApplicationNotification)
163+ for await notification in sequence {
164+ try Task . checkCancellation ( )
165+ guard UserDefaults . shared. bool ( forKey: SettingsKey . quitXPCServiceOnXcodeAndAppQuit)
166+ else { continue }
167+ guard let app = notification
168+ . userInfo ? [ NSWorkspace . applicationUserInfoKey] as? NSRunningApplication ,
169+ app. isUserOfService
170+ else { continue }
171+ if NSWorkspace . shared. runningApplications. contains ( where: \. isUserOfService) {
172+ continue
173+ }
174+ exit ( 0 )
175+ }
176+ }
177+ }
108178}
109179
110180private class UserDefaultsObserver : NSObject {
@@ -119,3 +189,12 @@ private class UserDefaultsObserver: NSObject {
119189 onChange ? ( keyPath)
120190 }
121191}
192+
193+ extension NSRunningApplication {
194+ var isUserOfService : Bool {
195+ [
196+ " com.apple.dt.Xcode " ,
197+ bundleIdentifierBase,
198+ ] . contains ( bundleIdentifier)
199+ }
200+ }
0 commit comments