Skip to content

Commit 28ead25

Browse files
committed
Update
1 parent 0c56c85 commit 28ead25

1 file changed

Lines changed: 47 additions & 32 deletions

File tree

Tool/Sources/XcodeInspector/Apps/XcodeAppInstanceInspector.swift

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import Combine
55
import Foundation
66

77
public final class XcodeAppInstanceInspector: AppInstanceInspector {
8-
@Published public var focusedWindow: XcodeWindowInspector?
9-
@Published public var documentURL: URL? = nil
10-
@Published public var workspaceURL: URL? = nil
11-
@Published public var projectRootURL: URL? = nil
12-
@Published public var workspaces = [WorkspaceIdentifier: Workspace]()
8+
@Published public fileprivate(set) var focusedWindow: XcodeWindowInspector?
9+
@Published public fileprivate(set) var documentURL: URL? = nil
10+
@Published public fileprivate(set) var workspaceURL: URL? = nil
11+
@Published public fileprivate(set) var projectRootURL: URL? = nil
12+
@Published public fileprivate(set) var workspaces = [WorkspaceIdentifier: Workspace]()
1313
public var realtimeWorkspaces: [WorkspaceIdentifier: WorkspaceInfo] {
1414
updateWorkspaceInfo()
1515
return workspaces.mapValues(\.info)
@@ -72,10 +72,10 @@ public final class XcodeAppInstanceInspector: AppInstanceInspector {
7272
override init(runningApplication: NSRunningApplication) {
7373
super.init(runningApplication: runningApplication)
7474

75-
observeFocusedWindow()
76-
observeAXNotifications()
77-
78-
Task {
75+
Task { @MainActor in
76+
observeFocusedWindow()
77+
observeAXNotifications()
78+
7979
try await Task.sleep(nanoseconds: 3_000_000_000)
8080
// Sometimes the focused window may not be ready on app launch.
8181
if !(focusedWindow is WorkspaceXcodeWindowInspector) {
@@ -84,6 +84,7 @@ public final class XcodeAppInstanceInspector: AppInstanceInspector {
8484
}
8585
}
8686

87+
@MainActor
8788
func observeFocusedWindow() {
8889
if let window = appElement.focusedWindow {
8990
if window.identifier == "Xcode.WorkspaceWindow" {
@@ -104,16 +105,19 @@ public final class XcodeAppInstanceInspector: AppInstanceInspector {
104105

105106
window.$documentURL
106107
.filter { $0 != .init(fileURLWithPath: "/") }
108+
.receive(on: DispatchQueue.main)
107109
.sink { [weak self] url in
108110
self?.documentURL = url
109111
}.store(in: &focusedWindowObservations)
110112
window.$workspaceURL
111113
.filter { $0 != .init(fileURLWithPath: "/") }
114+
.receive(on: DispatchQueue.main)
112115
.sink { [weak self] url in
113116
self?.workspaceURL = url
114117
}.store(in: &focusedWindowObservations)
115118
window.$projectRootURL
116119
.filter { $0 != .init(fileURLWithPath: "/") }
120+
.receive(on: DispatchQueue.main)
117121
.sink { [weak self] url in
118122
self?.projectRootURL = url
119123
}.store(in: &focusedWindowObservations)
@@ -127,6 +131,7 @@ public final class XcodeAppInstanceInspector: AppInstanceInspector {
127131
}
128132
}
129133

134+
@MainActor
130135
func refresh() {
131136
if let focusedWindow = focusedWindow as? WorkspaceXcodeWindowInspector {
132137
focusedWindow.refresh()
@@ -135,16 +140,19 @@ public final class XcodeAppInstanceInspector: AppInstanceInspector {
135140
}
136141
}
137142

143+
@MainActor
138144
func observeAXNotifications() {
139145
longRunningTasks.forEach { $0.cancel() }
140146
longRunningTasks = []
141147

142-
let focusedWindowChanged = Task {
143-
let notification = AXNotificationStream(
144-
app: runningApplication,
145-
notificationNames: kAXFocusedWindowChangedNotification
146-
)
147-
for await _ in notification {
148+
let windowChangeNotification = AXNotificationStream(
149+
app: runningApplication,
150+
notificationNames: kAXFocusedWindowChangedNotification
151+
)
152+
153+
let focusedWindowChanged = Task { @MainActor [weak self] in
154+
for await _ in windowChangeNotification {
155+
guard let self else { return }
148156
try Task.checkCancellation()
149157
observeFocusedWindow()
150158
}
@@ -153,19 +161,23 @@ public final class XcodeAppInstanceInspector: AppInstanceInspector {
153161
longRunningTasks.insert(focusedWindowChanged)
154162

155163
updateWorkspaceInfo()
156-
let updateTabsTask = Task { @MainActor in
157-
let notification = AXNotificationStream(
158-
app: runningApplication,
159-
notificationNames: kAXFocusedUIElementChangedNotification,
160-
kAXApplicationDeactivatedNotification
161-
)
164+
165+
let elementChangeNotification = AXNotificationStream(
166+
app: runningApplication,
167+
notificationNames: kAXFocusedUIElementChangedNotification,
168+
kAXApplicationDeactivatedNotification
169+
)
170+
171+
let updateTabsTask = Task { @MainActor [weak self] in
162172
if #available(macOS 13.0, *) {
163-
for await _ in notification.debounce(for: .seconds(2)) {
173+
for await _ in elementChangeNotification.debounce(for: .seconds(2)) {
174+
guard let self else { return }
164175
try Task.checkCancellation()
165176
updateWorkspaceInfo()
166177
}
167178
} else {
168-
for await _ in notification {
179+
for await _ in elementChangeNotification {
180+
guard let self else { return }
169181
try Task.checkCancellation()
170182
updateWorkspaceInfo()
171183
}
@@ -174,19 +186,22 @@ public final class XcodeAppInstanceInspector: AppInstanceInspector {
174186

175187
longRunningTasks.insert(updateTabsTask)
176188

177-
let completionPanelTask = Task {
178-
let stream = AXNotificationStream(
179-
app: runningApplication,
180-
notificationNames: kAXCreatedNotification, kAXUIElementDestroyedNotification
181-
)
189+
let completionPanelNotification = AXNotificationStream(
190+
app: runningApplication,
191+
notificationNames: kAXCreatedNotification, kAXUIElementDestroyedNotification
192+
)
193+
194+
let completionPanelTask = Task { @MainActor [weak self] in
195+
for await event in completionPanelNotification {
196+
guard let self else { return }
182197

183-
for await event in stream {
184198
// We can only observe the creation and closing of the parent
185199
// of the completion panel.
186200
let isCompletionPanel = {
187-
event.element.firstChild { element in
188-
element.identifier == "_XC_COMPLETION_TABLE_"
189-
} != nil
201+
event.element.identifier == "_XC_COMPLETION_TABLE_"
202+
|| event.element.firstChild { element in
203+
element.identifier == "_XC_COMPLETION_TABLE_"
204+
} != nil
190205
}
191206
switch event.name {
192207
case kAXCreatedNotification:

0 commit comments

Comments
 (0)