MDQueryGetResultAtIndex and UnsafeRawPointer in Swift 3

I am having trouble finding Spotlight search results in Swift 3 using MDQuery. I expect to MDQueryGetResultAtIndex

give MDItem, and in C / Objective-C this assumption works and I can call it MDItemCopyAttribute

to study the item. Here, for example, I am successfully getting the path to the found element:

MDItemRef item = (MDItemRef)MDQueryGetResultAtIndex(q,i);
CFStringRef path = MDItemCopyAttribute(item,kMDItemPath);

      

But in Swift 3, it MDQueryGetResultAtIndex

returns UnsafeRawPointer!

(that's a pointer to void in C). To get past this I tried, for example:

if let item = MDQueryGetResultAtIndex(q, 0) {
    let ptr = item.bindMemory(to: MDItem.self, capacity: 1)
    let path = MDItemCopyAttribute(ptr.pointee, kMDItemPath)
}

      

but it crashes and the registration shows that it ptr.pointee

is NSAtom. It's pretty obvious that my personal UnsafeRawPointer mojo isn't working (and to be honest, I've always found it confusing).

How do I convert this UnsafeRawPointer to something I can call successfully MDItemCopyAttribute

?

Alternatives

  • I can overcome this hump by putting my Objective-C code into an Objective-C helper object and calling it from Swift; but I would like to write a pure Swift solution.

  • Likewise, I could rewrite my code to use a higher level NSMetadataQuery, and I can do that; but my original Objective-C code using a lower level MDQueryRef works fine, so now I'm curious how to turn it directly into Swift.


Complete code for those who would like to try this at home:

let s = "kMDItemDisplayName == \"test\"" // you probably have one somewhere
let q = MDQueryCreate(nil, s as CFString, nil, nil)
MDQueryExecute(q, CFOptionFlags(kMDQuerySynchronous.rawValue))
let ct = MDQueryGetResultCount(q)
if ct > 0 {
    if let item = MDQueryGetResultAtIndex(q, 0) {
        // ... 
    }
}

      

+3


source to share


1 answer


The problem is in the code

if let item = MDQueryGetResultAtIndex(q, 0) {
    let ptr = item.bindMemory(to: MDItem.self, capacity: 1)
    let path = MDItemCopyAttribute(ptr.pointee, kMDItemPath)
}

      

is that it is UnsafeRawPointer

interpreted as a pointer to MDItem

and then dereferenced to ptr.pointee

, but the raw pointer is a reference MDItem

, so it is dereferenced too often.

The shortest method to convert a raw pointer to a link MDItem

unsafeBitCast

:

let item = unsafeBitCast(rawPtr, to: MDItem.self)

      



which is a direct analogue of the (Objective-) C cast. You can also use the methods to Unmanaged

convert a raw pointer to an unmanaged link and from there to a managed link (compare How to include self in UnsafeMutablePointer <Void> type in swift ):

let item = Unmanaged<MDItem>.fromOpaque(rawPtr).takeUnretainedValue()

      

This looks a little more complicated, but it probably expresses the intent more clearly. The latter approach also works with (+1) stored links (using takeRetainedValue()

).

Standalone example:

import CoreServices

let queryString = "kMDItemContentType = com.apple.application-bundle"
if let query = MDQueryCreate(kCFAllocatorDefault, queryString as CFString, nil, nil) {
    MDQueryExecute(query, CFOptionFlags(kMDQuerySynchronous.rawValue))

    for i in 0..<MDQueryGetResultCount(query) {
        if let rawPtr = MDQueryGetResultAtIndex(query, i) {
            let item = Unmanaged<MDItem>.fromOpaque(rawPtr).takeUnretainedValue()
            if let path = MDItemCopyAttribute(item, kMDItemPath) as? String {
                print(path)
            }
        }
    }
}

      

+3


source







All Articles