Reputation: 2541
I’m learning Swift. How do I fix the following code to list the window names?
import CoreGraphics
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
if let window = CFArrayGetValueAtIndex(windows, i) {
print(CFDictionaryGetValue(window, kCGWindowName))
}
}
The error:
main.swift:6:32: error: cannot convert value of type 'UnsafeRawPointer' to expected argument type 'CFDictionary?'
print(CFDictionaryGetValue(window, kCGWindowName))
^~~~~~
as! CFDictionary
Upvotes: 0
Views: 1916
Reputation: 32904
You can use unsafeBitCast(_:to:)
to convert the opaque raw pointer to a CFDictionary
. Note that you'll also need to convert the second parameter, to a raw pointer:
CFDictionaryGetValue(unsafeBitCast(window, to: CFDictionary.self), unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self))
unsafeBitCast(_:to:) tells the compiler to treat that variable as another type, however it's not very safe (thus the unsafe
prefix), recommending to read the documentation for more details, especially the following note:
Warning
Calling this function breaks the guarantees of the Swift type system; use with extreme care.
In your particular case there should not be any problems using the function, since you're working with the appropriate types, as declared in the documentation of the Foundation
functions you're calling.
Complete, workable code could look something like this:
import CoreGraphics
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
let windowDict = unsafeBitCast(CFArrayGetValueAtIndex(windows, i), to: CFDictionary.self)
let rawWindowNameKey = unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self)
let rawWindowName = CFDictionaryGetValue(windowDict, rawWindowNameKey)
let windowName = unsafeBitCast(rawWindowName, to: CFString?.self) as String?
print(windowName ?? "")
}
Update You can bring the CoreFoundation array sooner to the Swift world by casting right from the start:
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID) as? [[AnyHashable: Any]]
windows?.forEach { window in
print(window[kCGWindowName])
}
The code is much readable, however it might pose performance problems, as the cast to [[AnyHashable: Any]]` can be expensive for large array consisting of large dictionaries.
Upvotes: 1
Reputation: 540075
It becomes easier if you avoid using the Core Foundation types and methods, and bridge the values to native Swift types as early as possible.
Here, CGWindowListCopyWindowInfo()
returns an optional CFArray
of CFDictionaries
, and that can be bridged to the corresponding Swift type [[String : Any]]
. Then you can access its values with the usual Swift methods (array enumeration and dictionary subscripting):
if let windowInfo = CGWindowListCopyWindowInfo(.optionAll, kCGNullWindowID) as? [[ String : Any]] {
for windowDict in windowInfo {
if let windowName = windowDict[kCGWindowName as String] as? String {
print(windowName)
}
}
}
Upvotes: 5