@@ -6,10 +6,17 @@ import Foundation
66import Logger
77import Preferences
88import SuggestionModel
9+ import Toast
10+
11+ public extension Notification . Name {
12+ static let accessibilityAPIMalfunctioning = Notification . Name ( " accessibilityAPIMalfunctioning " )
13+ }
914
1015public final class XcodeInspector : ObservableObject {
1116 public static let shared = XcodeInspector ( )
1217
18+ private var toast : ToastController { ToastControllerDependencyKey . liveValue }
19+
1320 private var cancellable = Set < AnyCancellable > ( )
1421 private var activeXcodeObservations = Set < Task < Void , Error > > ( )
1522 private var appChangeObservations = Set < Task < Void , Never > > ( )
@@ -182,6 +189,110 @@ public final class XcodeInspector: ObservableObject {
182189 }
183190 }
184191 }
192+
193+ if UserDefaults . shared
194+ . value ( for: \. restartXcodeInspectorIfAccessibilityAPIIsMalfunctioning)
195+ {
196+ group. addTask { [ weak self] in
197+ while true {
198+ guard let self else { return }
199+ try await Task . sleep ( nanoseconds: 10_000_000_000 )
200+ Logger . service. debug ( """
201+ Check for Accessibility Malfunctioning:
202+ Source Editor: \( {
203+ if let editor = self . focusedEditor {
204+ return editor. element. description
205+ }
206+ return " Not Found "
207+ } ( ) )
208+ Focused Element: \( {
209+ if let element = self . focusedElement {
210+ return " \( element. description) , \( element. identifier) , \( element. role) "
211+ }
212+ return " Not Found "
213+ } ( ) )
214+
215+ Accessibility API Permission: \(
216+ AXIsProcessTrusted ( ) ? " Granted " :
217+ " Not Granted "
218+ )
219+ App: \(
220+ self . activeApplication? . runningApplication
221+ . bundleIdentifier ?? " "
222+ )
223+ Focused Element: \( {
224+ guard let element = self . activeApplication? . appElement
225+ . focusedElement
226+ else {
227+ return " Not Found "
228+ }
229+ return " \( element. description) , \( element. identifier) , \( element. role) "
230+ } ( ) )
231+ First Source Editor: \( {
232+ guard let element = self . activeApplication? . appElement
233+ . firstChild ( where: \. isSourceEditor)
234+ else {
235+ return " Not Found "
236+ }
237+ return " \( element. description) , \( element. identifier) , \( element. role) "
238+ } ( ) )
239+ """ )
240+
241+ if let editor = self . focusedEditor, !editor. element. isSourceEditor {
242+ NSWorkspace . shared. notificationCenter. post (
243+ name: . accessibilityAPIMalfunctioning,
244+ object: " Source Editor Element Corrupted "
245+ )
246+ } else if let element = self . activeXcode? . appElement. focusedElement {
247+ if element. description != self . focusedElement? . description ||
248+ element. identifier != self . focusedElement? . role
249+ {
250+ NSWorkspace . shared. notificationCenter. post (
251+ name: . accessibilityAPIMalfunctioning,
252+ object: " Element Inconsistency "
253+ )
254+ }
255+ }
256+ }
257+ }
258+
259+ group. addTask {
260+ let sequence = DistributedNotificationCenter . default ( )
261+ . notifications ( named: . init( " com.apple.accessibility.api " ) )
262+ for await notification in sequence {
263+ if AXIsProcessTrusted ( ) {
264+ Logger . service. debug ( " Accessibility API Permission Granted " )
265+ } else {
266+ Logger . service. debug ( " Accessibility API Permission Not Granted " )
267+ NSWorkspace . shared. notificationCenter. post (
268+ name: . accessibilityAPIMalfunctioning,
269+ object: " Accessibility API Permission Check "
270+ )
271+ }
272+ }
273+ }
274+ }
275+
276+
277+
278+ group. addTask { [ weak self] in // malfunctioning
279+ let sequence = NSWorkspace . shared. notificationCenter
280+ . notifications ( named: . accessibilityAPIMalfunctioning)
281+ for await notification in sequence {
282+ guard let self else { return }
283+ let toast = self . toast
284+ toast. toast (
285+ content: " Accessibility API malfunction detected: \( notification. object as? String ?? " " ) " ,
286+ type: . warning
287+ )
288+ if let activeXcode {
289+ toast. toast ( content: " Resetting active Xcode " , type: . warning)
290+ await MainActor . run {
291+ self . setActiveXcode ( activeXcode)
292+ }
293+ }
294+ }
295+ }
185296 }
186297 }
187298
@@ -210,19 +321,23 @@ public final class XcodeInspector: ObservableObject {
210321 let setFocusedElement = { [ weak self] in
211322 guard let self else { return }
212323 focusedElement = xcode. appElement. focusedElement
324+ Logger . service. debug ( " Update focused element. " )
213325 if let editorElement = focusedElement, editorElement. isSourceEditor {
326+ Logger . service. debug ( " Focused on source editor. " )
214327 focusedEditor = . init(
215328 runningApplication: xcode. runningApplication,
216329 element: editorElement
217330 )
218331 } else if let element = focusedElement,
219332 let editorElement = element. firstParent ( where: \. isSourceEditor)
220333 {
334+ Logger . service. debug ( " Focused on child of source editor. " )
221335 focusedEditor = . init(
222336 runningApplication: xcode. runningApplication,
223337 element: editorElement
224338 )
225339 } else {
340+ Logger . service. debug ( " No source editor found. " )
226341 focusedEditor = nil
227342 }
228343 }
0 commit comments