2

I have this Objective-C Code fragment, which I want to express in Swift

CFArrayRef windowList;
AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute, (CFTypeRef *)&windowList);

if ((!windowList) || CFArrayGetCount(windowList)<1)
        continue;

AXUIElementRef windowRef = (AXUIElementRef) CFArrayGetValueAtIndex( windowList, 0);
CFTypeRef role;
AXUIElementCopyAttributeValue(windowRef, kAXRoleAttribute, (CFTypeRef *)&role);         

The first thing I´m not sure about: Who allocates the memory behind the windowListPointer. I tried with this fragment:

var windowListPointer : UnsafeMutablePointer<Optional<AnyObject>>
AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute as CFString, windowListPointer );

But that does not even compile: It complains, the windowListPointer is not initialised. What Object I could create, to let the WindowListPointer point to?

2
  • 1
    Does this help stackoverflow.com/questions/25124002/… ? Commented Nov 25, 2017 at 14:53
  • @MartinR TNT, As in the example var windowList : CFArray? AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute as CFString, &windowList ) does not compile too: Cannot pass immutable value of type 'CFTypeRef?' (aka 'Optional<AnyObject>') as inout Commented Nov 25, 2017 at 15:10

2 Answers 2

3

If you pass an UnsafeMutablePointer<Optional<AnyObject>> as the last argument to AXUIElementCopyAttributeValue() then you must initialize it by allocating (and ultimately releasing) memory:

var resultPtr: UnsafeMutablePointer<Optional<AnyObject>> = UnsafeMutablePointer.allocate(capacity: 1)
resultPtr.initialize(to: nil)

let result = AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute as CFString, resultPtr)
// ...

resultPtr.deinitialize()
resultPtr.deallocate(capacity: 1)

It is easier to pass the address of an Optional<AnyObject> variable with &. Then conditionally cast the received object to the expected type, in this case an array of AXUIElement:

var value: AnyObject?
let result = AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute as CFString, &value)
if result == .success, let windowList = value as? [AXUIElement] {
    // use `windowList`
}

and similarly:

if let window = windowList.first {
    var value: AnyObject?
    let result = AXUIElementCopyAttributeValue(window, kAXRoleAttribute as CFString, &value)
    if result == .success, let role = value as? String {
        // use `role` ...
    }
}

One could define a generic utility function which encapsulates all the casting:

func axUICopyAttributeValue<T>(of element: AXUIElement, attribute: String, as type: T.Type) -> T? {
    var value: AnyObject?
    let result = AXUIElementCopyAttributeValue(element, attribute as CFString, &value)
    if result == .success, let typedValue = value as? T {
        return typedValue
    }
    return nil
}

Example usage:

if let windowList = axUICopyAttributeValue(of: appRef, attribute: kAXWindowsAttribute, as:[AXUIElement].self) {

    for window in windowList {
        if let role = axUICopyAttributeValue(of: window, attribute: kAXRoleAttribute, as: String.self) {

            // ...
        }
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks! Two Questions, 1) where can I find, that I have to cast to [AXUIElement] 2) var value: AnyObject? Does that declaration allocate any memory for an Object or just a reference to AnyObject (or nil)
1) developer.apple.com/documentation/applicationservices/… states that the values is "an array of accessibility objects". – 2) Optional variables are implicitly initialized to nil. No memory is allocated there.
Compiles fine now, but returns AXError . Got the other parameters as in this post: stackoverflow.com/questions/47480873/…
The row value of the AXError is kAXErrorCannotComplete = -25204, Doku says: A fundamental error has occurred, such as a failure to allocate memory during processing. Could it be possible, that I have to allocate anything for var value: AnyObject??
Sorry, I have no idea. I tested it with a reference to the current application let appRef = AXUIElementCreateApplication(getpid()), and that worked.
|
1

CFArray is the Foundation C version of NSArray (since C doesn't understand Objective C NSObjects). Swift papers over both NSArray and CFArray for you so you don't need to use a pointer; you should just be able to cast it to a Swift array of the appropriate type with as?

1 Comment

What Swift Type would you use for windowList in `AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute as CFString, windowList )'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.