How to handle UnsafePointerUnmanagedCFArray?
I am a little bit guessing (because I have no experience with the Accessibility
functions), but from the function declaration it should work like this:
let element: AXUIElementRef = ...
var ptr : Unmanaged<CFArray>? = nil
let error = AXUIElementCopyAttributeNames(element, &ptr)
if error == AXError(kAXErrorSuccess) {
let names = ptr!.takeRetainedValue() // gives a CFArray
// ...
}
Update for Swift 3 (untested):
let element: AXUIElement = ...
var cfArray: CFArray?
let error = AXUIElementCopyAttributeNames(element, &cfArray)
if error == .success, let names = cfArray as? [String] {
// names is [String] array ...
}
Cannot convert value of type 'UnsafePointerDouble' to expected argument type 'UnsafePointer_'
So, I was able to solve it, albeit in a roundabout way.
I created a new function convert and used it:
func convertArr<T>(count: Int, data: UnsafePointer<T>) -> [T] {
let buffer = UnsafeBufferPointer(start: data, count: count)
return Array(buffer)
}
...
let doublearrptr = UnsafePointer<Double>(cda)
let arr = convertArr(Int(shobjarrlen), data: doublearrptr)
For some reason this works but not the original syntax...
I'm still open to getting answers from why my original syntax didn't work.
Accessing Objective-C Pointers in Swift
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) {
// ...
}
}
}
Error with fetching value from CFArray
You cannot force cast an UnsafePointer<Void>
to a type. You must first convert that void pointer to UnsafePointer<Type>
then take its memory:
let aPerson = UnsafePointer<ABRecordRef>(CFArrayGetValueAtIndex(allPeople, i)).memory
FYI... ABAddressBook
has been deprecated on iOS 9. For new code targeting that OS, use CNContactStore
instead.
Working with ObjC Functions with UnsafeMutableRawPointer or UnsafeMutablePointer Parameter from Swift?
AXUIElementCopyAttributeNames
does not take a pointer to
an unmanaged CFArray
anymore:
let element: AXUIElement = ...
var cfArray: CFArray?
let error = AXUIElementCopyAttributeNames(element, &cfArray)
if error == .success, let names = cfArray as? [String] {
// names is [String] array ...
}
How to convert CFArray to Swift Array?
Here is how to do this, based on the current state of the Swift compiler and Swift documentation. Hopefully this gets cleaned up in later betas.
UPDATE: Since Beta 5, reinterpretCast has been renamed to unsafeBitCast, and a CTLine object must be sent to it as an input. Way #2 still does not work.
Way #1 - Use the CFArray directly
let line: CTLine = reinterpretCast(CFArrayGetValueAtIndex(lines, 0))
Regarding Gary Makin's comments - The Ref can be dropped from CTLineRef, but this does not change ARC vs non-ARC. According to Using Swift with Cocoa and Objective-C pages 53-54, ref and non-ref are identical to the compiler. Attempting to call CFRelease causes a compiler error.
Way #2 - Convert the CFArray to a Swift array - Does not currently work
Ideally, we want to convert lines to a Swift array of CTLine objects since we know that's what is returned by CTFrameGetLines, giving us type safety after the conversion. Probably due to a compiler bug, the array can be converted to an [AnyObject] array, but not to [CTLine]. According to Apple's documentation, this should work:
let linesNS: NSArray = CTFrameGetLines(frame)
let linesAO: [AnyObject] = linesNS as [AnyObject]
let lines: [CTLine] = linesAO as [CTLine]
This converts CFArray to NSArray, then NSArray to Swift Array [AnyObject], then downcasts that array to the specific type CTLine. This compiles, but when it is run, there is an EXC_BREAKPOINT crash on the last line.
How to use UnsafeMutablePointerOpaquePointer in Swift?
PMPrinter
and PMPaper
are defined in the PrintCore framework
as pointer to an "incomplete type"
typedef struct OpaquePMPrinter* PMPrinter;
typedef struct OpaquePMPaper* PMPaper;
Those are imported into Swift as OpaquePointer
, and are a bit
cumbersome to use.
The second argument to PMSessionGetCurrentPrinter()
is a pointer to
a non-optional PMPrinter
variable, and in Swift it must be
initialized before being passed as an inout argument. One possible way
to initialize a null-pointer is to use unsafeBitCast
.
The easiest way to get the PMPaper
objects from the array seems to
be to use CFArrayGetValueAtIndex()
instead of bridging it to a
Swift array. That returns a UnsafeRawPointer
which can be converted
to an OpaquePointer
.
This worked in my test:
let printInfo = NSPrintInfo.shared()
let printSession = PMPrintSession(printInfo.pmPrintSession())
var currentPrinter = unsafeBitCast(0, to: PMPrinter.self)
PMSessionGetCurrentPrinter(printSession, ¤tPrinter);
var paperListUnmanaged: Unmanaged<CFArray>?
PMPrinterGetPaperList(currentPrinter, &paperListUnmanaged)
guard let paperList = paperListUnmanaged?.takeUnretainedValue() else {
fatalError()
}
for idx in 0..<CFArrayGetCount(paperList) {
let paper = PMPaper(CFArrayGetValueAtIndex(paperList, idx))!
var width = 0.0, height = 0.0
PMPaperGetWidth(paper, &width)
PMPaperGetHeight(paper, &height)
print(width, height)
}
Swift: CFArray : get values as UTF Strings
There are some issues here. First, TISCreateInputSourceList()
has "Create" in its name which means that it returns a (+1) retained
object and you have to take the value with takeRetainedValue()
,
not takeUnretainedValue()
, otherwise the code will leak memory:
let srcs = TISCreateInputSourceList(nil, true).takeRetainedValue()
You could now use the CFArray...
methods to get values from the array,
but it is much easier to convert it to a NSArray
(which is "toll-free bridged"):
let srcs = TISCreateInputSourceList(nil, true).takeRetainedValue() as NSArray
This is not an array of CFStringRef
values but an array ofTISInputSource
objects. You can convert the NSArray
to a Swift array:
let srcs = TISCreateInputSourceList(nil, true).takeRetainedValue()
as NSArray as! [TISInputSource]
The forced cast as!
is acceptable here because the function is
documented to return an array of input sources.
Now you can simply iterate over the elements of the array:
for src in srcs {
// do something with `src` (which is a `TISInputSource`)
}
The properties of an input source are retrieved with the TISGetInputSourceProperty()
function, for example:
let ptr = TISGetInputSourceProperty(src, kTISPropertyInputSourceID)
This returns a "void pointer" (UnsafeMutablePointer<Void>
) which has to be converted to an object
pointer of the appropriate type (which is CFStringRef
for the kTISPropertyInputSourceID
property). Unfortunately, this is a bit
complicated (compare How to cast self to UnsafeMutablePointer<Void> type in swift):
let val = Unmanaged<CFString>.fromOpaque(COpaquePointer(ptr)).takeUnretainedValue()
Again we can take advantage of toll-free bridging, now fromCFStringRef
to NSString
and String
:
let val = Unmanaged<CFString>.fromOpaque(COpaquePointer(ptr)).takeUnretainedValue()
as String
Putting it all together:
let srcs = TISCreateInputSourceList(nil, true).takeRetainedValue()
as NSArray as! [TISInputSource]
for src in srcs {
let ptr = TISGetInputSourceProperty(src, kTISPropertyInputSourceID)
let val = Unmanaged<CFString>.fromOpaque(COpaquePointer(ptr)).takeUnretainedValue()
as String
print(val)
}
Related Topics
Swift: How to Invalidate a Timer If the Timer Starts from a Function
Calling a Child Inside 2 Levels of Nodes
Store Data in Custom Class Array in Core Data
Can the Byte Order of Double Be Safely Reversed
How to Make a Variable from a String
How to Use Querystartingatvalue in My Searchcontroller to Search for Users
Uibutton Causing Unrecognized Selector Sent to Instance
How to Add Nil Value to Swift Dictionary
Why Does Function Has Multiple Return Types in Swift
Disable Email Detection in Swiftui's Text
Nsurl Fail Able Initialiser Initwithstring: Does Not Return Nil on Empty String in Swift
Downloading Firebase Storage Files Device Issue
Hiding Dividers in Nssplitview
Core Data Update in Swift While Selecting Any Row in List Table View Not Working
Failable Initializers with Codable
What Is a Good or Common Way to Structure a Cli Appliction in Swift