Skip to content

Commit e16b90a

Browse files
committed
Update AXNotificationStream to retry automatically
1 parent ccdf016 commit e16b90a

1 file changed

Lines changed: 47 additions & 14 deletions

File tree

Core/Sources/AXNotificationStream/AXNotificationStream.swift

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import AppKit
22
import ApplicationServices
33
import Foundation
4+
import Logger
45

56
public final class AXNotificationStream: AsyncSequence {
67
public typealias Stream = AsyncStream<Element>
@@ -18,7 +19,7 @@ public final class AXNotificationStream: AsyncSequence {
1819
deinit {
1920
continuation.finish()
2021
}
21-
22+
2223
public convenience init(
2324
app: NSRunningApplication,
2425
element: AXUIElement? = nil,
@@ -72,24 +73,56 @@ public final class AXNotificationStream: AsyncSequence {
7273
.commonModes
7374
)
7475
}
75-
76-
Task {
77-
for name in notificationNames {
78-
var error = AXError.cannotComplete
79-
var retryCount = 0
80-
while error == AXError.cannotComplete, retryCount < 5 {
81-
error = AXObserverAddNotification(observer, observingElement, name as CFString, &continuation)
82-
if error == .cannotComplete {
83-
try await Task.sleep(nanoseconds: 1_000_000_000)
84-
}
85-
retryCount += 1
86-
}
87-
}
76+
77+
Task { [weak self] in
8878
CFRunLoopAddSource(
8979
CFRunLoopGetMain(),
9080
AXObserverGetRunLoopSource(observer),
9181
.commonModes
9282
)
83+
var pendingRegistrationNames = Set(notificationNames)
84+
var retry = 0
85+
while !pendingRegistrationNames.isEmpty, retry < 100 {
86+
guard let self else { return }
87+
retry += 1
88+
for name in notificationNames {
89+
let e = AXObserverAddNotification(
90+
observer,
91+
observingElement,
92+
name as CFString,
93+
&self.continuation
94+
)
95+
switch e {
96+
case .success:
97+
pendingRegistrationNames.remove(name)
98+
case .actionUnsupported:
99+
Logger.service.error("AXObserver: Action unsupported: \(name)")
100+
pendingRegistrationNames.remove(name)
101+
case .apiDisabled:
102+
Logger.service.error("AXObserver: Accessibility API disabled, will try again later")
103+
retry -= 1
104+
case .invalidUIElement:
105+
Logger.service.error("AXObserver: Invalid UI element")
106+
pendingRegistrationNames.remove(name)
107+
case .invalidUIElementObserver:
108+
Logger.service.error("AXObserver: Invalid UI element observer")
109+
pendingRegistrationNames.remove(name)
110+
case .cannotComplete:
111+
Logger.service
112+
.error("AXObserver: Failed to observe \(name), will try again later")
113+
case .notificationUnsupported:
114+
Logger.service.error("AXObserver: Notification unsupported: \(name)")
115+
pendingRegistrationNames.remove(name)
116+
case .notificationAlreadyRegistered:
117+
pendingRegistrationNames.remove(name)
118+
default:
119+
Logger.service
120+
.error("AXObserver: Unrecognized error \(e) when registering \(name), will try again later")
121+
}
122+
}
123+
try await Task.sleep(nanoseconds: 1_500_000_000)
124+
}
93125
}
94126
}
95127
}
128+

0 commit comments

Comments
 (0)