@@ -196,99 +196,42 @@ public final class XcodeInspector: ObservableObject {
196196 group. addTask { [ weak self] in
197197 while true {
198198 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- }
199+ if UserDefaults . shared. value (
200+ for: \. restartXcodeInspectorIfAccessibilityAPIIsMalfunctioningNoTimer
201+ ) {
202+ return
255203 }
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- )
204+
205+ try await Task . sleep ( nanoseconds: 10_000_000_000 )
206+ await MainActor . run {
207+ self . checkForAccessibilityMalfunction ( " Timer " )
271208 }
272209 }
273210 }
274211 }
275-
276-
277212
278213 group. addTask { [ weak self] in // malfunctioning
279214 let sequence = NSWorkspace . shared. notificationCenter
280215 . notifications ( named: . accessibilityAPIMalfunctioning)
281216 for await notification in sequence {
282217 guard let self else { return }
283218 let toast = self . toast
284- toast. toast (
285- content: " Accessibility API malfunction detected: \( notification. object as? String ?? " " ) " ,
286- type: . warning
287- )
219+ if UserDefaults . shared
220+ . value ( for: \. toastForTheReasonWhyXcodeInspectorNeedsToBeRestarted)
221+ {
222+ toast. toast (
223+ content: """
224+ Accessibility API malfunction detected: \
225+ \( notification. object as? String ?? " " ) .
226+ Resetting active Xcode.
227+ """ ,
228+ type: . warning
229+ )
230+ }
288231 if let activeXcode {
289- toast. toast ( content: " Resetting active Xcode " , type: . warning)
290232 await MainActor . run {
291233 self . setActiveXcode ( activeXcode)
234+ activeXcode. observeAXNotifications ( )
292235 }
293236 }
294237 }
@@ -318,12 +261,10 @@ public final class XcodeInspector: ObservableObject {
318261 activeWorkspaceURL = xcode. workspaceURL
319262 focusedWindow = xcode. focusedWindow
320263
321- let setFocusedElement = { [ weak self] in
264+ let setFocusedElement = { @ MainActor [ weak self] in
322265 guard let self else { return }
323266 focusedElement = xcode. appElement. focusedElement
324- Logger . service. debug ( " Update focused element. " )
325267 if let editorElement = focusedElement, editorElement. isSourceEditor {
326- Logger . service. debug ( " Focused on source editor. " )
327268 focusedEditor = . init(
328269 runningApplication: xcode. runningApplication,
329270 element: editorElement
@@ -345,15 +286,35 @@ public final class XcodeInspector: ObservableObject {
345286 setFocusedElement ( )
346287 let focusedElementChanged = Task { @MainActor in
347288 for await notification in xcode. axNotifications {
348- guard notification. kind == . focusedUIElementChanged else { continue }
349- Logger . service. debug ( " Update focused element " )
350- try Task . checkCancellation ( )
351- setFocusedElement ( )
289+ if notification. kind == . focusedUIElementChanged {
290+ Logger . service. debug ( " Update focused element " )
291+ try Task . checkCancellation ( )
292+ setFocusedElement ( )
293+ }
352294 }
353295 }
354296
355297 activeXcodeObservations. insert ( focusedElementChanged)
356298
299+ if UserDefaults . shared
300+ . value ( for: \. restartXcodeInspectorIfAccessibilityAPIIsMalfunctioning)
301+ {
302+ let malfunctionCheck = Task { @MainActor [ weak self] in
303+ if #available( macOS 13 . 0 , * ) {
304+ let notifications = xcode. axNotifications. filter {
305+ $0. kind == . uiElementDestroyed
306+ } . debounce ( for: . milliseconds( 500 ) )
307+ for await _ in notifications {
308+ guard let self else { return }
309+ try Task . checkCancellation ( )
310+ self . checkForAccessibilityMalfunction ( " Element Destroyed " )
311+ }
312+ }
313+ }
314+
315+ activeXcodeObservations. insert ( malfunctionCheck)
316+ }
317+
357318 xcode. $completionPanel. receive ( on: DispatchQueue . main) . sink { [ weak self] element in
358319 self ? . completionPanel = element
359320 } . store ( in: & activeXcodeCancellable)
@@ -374,5 +335,57 @@ public final class XcodeInspector: ObservableObject {
374335 self ? . focusedWindow = window
375336 } . store ( in: & activeXcodeCancellable)
376337 }
338+
339+ @MainActor
340+ private func checkForAccessibilityMalfunction( _ source: String ) {
341+ Logger . service. debug ( """
342+ Check for Accessibility Malfunctioning:
343+ Source Editor: \( {
344+ if let editor = self . focusedEditor {
345+ return editor. element. description
346+ }
347+ return " Not Found "
348+ } ( ) )
349+ Focused Element: \( {
350+ if let element = self . focusedElement {
351+ return " \( element. description) , \( element. identifier) , \( element. role) "
352+ }
353+ return " Not Found "
354+ } ( ) )
355+
356+ Accessibility API Permission: \(
357+ AXIsProcessTrusted ( ) ? " Granted " :
358+ " Not Granted "
359+ )
360+ App: \(
361+ activeApplication? . runningApplication
362+ . bundleIdentifier ?? " "
363+ )
364+ Focused Element: \( {
365+ guard let element = self . activeApplication? . appElement
366+ . focusedElement
367+ else {
368+ return " Not Found "
369+ }
370+ return " \( element. description) , \( element. identifier) , \( element. role) "
371+ } ( ) )
372+ """ )
373+
374+ if let editor = focusedEditor, !editor. element. isSourceEditor {
375+ NSWorkspace . shared. notificationCenter. post (
376+ name: . accessibilityAPIMalfunctioning,
377+ object: " Source Editor Element Corrupted: \( source) "
378+ )
379+ } else if let element = activeXcode? . appElement. focusedElement {
380+ if element. description != focusedElement? . description ||
381+ element. role != focusedElement? . role
382+ {
383+ NSWorkspace . shared. notificationCenter. post (
384+ name: . accessibilityAPIMalfunctioning,
385+ object: " Element Inconsistency: \( source) "
386+ )
387+ }
388+ }
389+ }
377390}
378391
0 commit comments