Skip to content

Instantly share code, notes, and snippets.

@adamdroberts
Last active June 29, 2025 23:37
Show Gist options
  • Save adamdroberts/1a0161a418fc503d80d330dd419edf43 to your computer and use it in GitHub Desktop.
Save adamdroberts/1a0161a418fc503d80d330dd419edf43 to your computer and use it in GitHub Desktop.
MacOS swift code to detect NSProgressIndicator on screen in any application
import Cocoa
import ApplicationServices
/// Traverses the view hierarchy of all windows associated with the current application
/// to find if any view is an NSProgressIndicator.
///
/// - Returns: `true` if at least one NSProgressIndicator is found, `false` otherwise.
@MainActor func findNativeProgressBarsInAllWindows() -> Bool {
// Get the shared application instance
let app = NSApplication.shared
// Iterate through all windows managed by the application
for window in app.windows {
// Check the window's content view and its subviews
if let contentView = window.contentView {
if traverseViewHierarchy(startingFrom: contentView) {
// Found a progress bar in this window's hierarchy
return true
}
}
}
// No progress bar found in any window's view hierarchy
print("No NSProgressIndicator found in any window.")
return false
}
/// Recursively traverses a view hierarchy starting from a given view.
///
/// - Parameter view: The root view to start the traversal from.
/// - Returns: `true` if an NSProgressIndicator is found within the hierarchy, `false` otherwise.
@MainActor private func traverseViewHierarchy(startingFrom view: NSView) -> Bool {
// Check if the current view itself is an NSProgressIndicator
if view is NSProgressIndicator {
print("Found NSProgressIndicator: \(view) in window: \(view.window?.title ?? "N/A")")
return true
}
// Recursively check each subview
for subview in view.subviews {
if traverseViewHierarchy(startingFrom: subview) {
// Found in a subview, propagate the result up
return true
}
}
// NSProgressIndicator not found in this view or its direct/indirect subviews
return false
}
/// Recursively traverses an AXUIElement hierarchy starting from a given element.
///
/// - Parameter element: The root AXUIElement to start the traversal from.
/// - Returns: `true` if an element with the role of progress indicator is found, `false` otherwise.
func traverseAXHierarchy(startingFrom element: AXUIElement) -> Bool {
var role: CFTypeRef?
if AXUIElementCopyAttributeValue(element, kAXRoleAttribute as CFString, &role) == .success {
if let role = role as? String, role == (kAXProgressIndicatorRole as String) {
print("Found progress bar: \(element)")
return true
}
}
var children: CFTypeRef?
if AXUIElementCopyAttributeValue(element, kAXChildrenAttribute as CFString, &children) == .success,
let childrenArray = children as? [AXUIElement] {
for child in childrenArray {
if traverseAXHierarchy(startingFrom: child) {
return true
}
}
}
return false
}
/// Searches for native progress bars at the screen level using accessibility APIs.
///
/// - Returns: `true` if at least one progress bar is found system-wide, `false` otherwise.
func findNativeProgressBarsOnScreen() -> Bool {
// Get information for all on-screen windows (excluding desktop elements)
let options: CGWindowListOption = [.optionOnScreenOnly, .excludeDesktopElements]
guard let windowInfoList = CGWindowListCopyWindowInfo(options, kCGNullWindowID) as? [[String: Any]] else {
print("Failed to retrieve window information.")
return false
}
// Iterate through each window from the system list
for windowInfo in windowInfoList {
// Retrieve the process identifier (PID) for the window owner
if let pid = windowInfo[kCGWindowOwnerPID as String] as? pid_t {
let appElement = AXUIElementCreateApplication(pid)
var axWindows: CFTypeRef?
if AXUIElementCopyAttributeValue(appElement, kAXWindowsAttribute as CFString, &axWindows) == .success,
let axWindowsArray = axWindows as? [AXUIElement] {
for axWindow in axWindowsArray {
if traverseAXHierarchy(startingFrom: axWindow) {
return true
}
}
}
}
}
print("No system progress bar found.")
return false
}
// Example of how to call the new function:
DispatchQueue.main.async { // Ensure UI access is on the main thread
if findNativeProgressBarsOnScreen() {
print("Success: Found at least one native progress bar on screen.")
} else {
print("Result: No native progress bars were found on screen.")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment