Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get custom value back from Spotlight with CSCustomAttributeKey

I am trying to get some data back from Core Spotlight which I am storing using a custom attribute key. Tested this on macOS and iOS as well, the result is always the same.

My test class:

import CoreSpotlight

class SpotlightSearch {
  let domainId = "com.company.some"
  let originalDataKeyName: String

  init() {
    self.originalDataKeyName = domainId.replacingOccurrences(of: ".", with: "_") + "_originalData"
  }

  func addToIndex(title: String, content: String) {
    guard let originalDataKey = CSCustomAttributeKey(keyName: originalDataKeyName, searchable: false, searchableByDefault: false, unique: false, multiValued: false)

      else { return }

    let uniqueId = "MyUniqueId" + title
    let originalContent = NSString(string: content)

    let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeText as String)
    attributeSet.title = title
    attributeSet.setValue(originalContent, forCustomKey: originalDataKey)
    let item = CSSearchableItem(uniqueIdentifier: uniqueId, domainIdentifier: domainId, attributeSet: attributeSet)
    CSSearchableIndex.default().indexSearchableItems([item]) { error in
      if let error = error {
        print("Indexing error: \(error.localizedDescription)")
      } else {
        print("Item '\(title)' successfully indexed!")
      }
    }
  }

  var query: CSSearchQuery?

  func search(title: String) {
    var allItems = [CSSearchableItem]()
    let queryString = "title == '\(title)'cd"
    let attributes = [ "title", originalDataKeyName ]
    let newQuery = CSSearchQuery(queryString: queryString, attributes: attributes)
    newQuery.foundItemsHandler = { (items: [CSSearchableItem]) -> Void in
      allItems.append(contentsOf: items)
    }

    newQuery.completionHandler = { [weak self] (error: Error?) -> Void in
      guard let originalDataKeyName = self?.originalDataKeyName,
            let originalDataKey = CSCustomAttributeKey(keyName: originalDataKeyName)
       else { return }
      print("Search complete")
      for item in allItems {
        let attributeSet = item.attributeSet
        let customData = attributeSet.value(forCustomKey: originalDataKey)
        // Always nil
        if customData == nil {
          print("\(String(describing: originalDataKeyName)) not found in \(attributeSet.description)")
        } else if let originalData = customData as? NSData {
          let data = Data(referencing: originalData)

          if let originalString = String(data: data, encoding: .utf8) {
            print("Found '\(originalString)'")
          }
        }
      }
    }
    query = newQuery
    newQuery.start()
  }
}

On app init:

let newSpotlightSearch = SpotlightSearch()
newSpotlightSearch.addToIndex(title: "Banana", content: "🍌")

Later:

spotlightSearch.search(title: "Banana")

It will find the title, but will not give me back the custom attribute value. If I put a breakpoint after "// Always nil" and use po attributeSet I will get

(lldb) po attributeSet
{
    "_kMDItemBundleID" = "de.axelspringer.SearchMac";
    "_kMDItemDomainIdentifier" = "com.company.some";
    "_kMDItemExpirationDate" = "2018-08-26 00:00:00 +0000";
    "_kMDItemExternalID" = MyUniqueIdBanana;
    "com_company_some_originalData" = "\Ud83c\Udf4c";
    kMDItemTitle = Banana;
}

So the value is there, but Spotlight will not return it to me. Already tried to use NSData instead of NSString for the custom attribute, but same result.

Also found this orphaned question in the Apple developer forums:

CSCustomAttributeKey valueForCustomKey not working

like image 915
Pegolon Avatar asked Jul 26 '18 15:07

Pegolon


1 Answers

I believe it's iOS issue. While it's not fixed, maybe Apple will allow you to use a private API to do your thing.

So, attributeSet has private Dictionaries attributes and customAttributes. You can try to get those values using Key Value Coding and ObjC:

NSDictionary *attributes = [attributeSet valueForKey:@"attributes"];
id customData = attributes[originalDataKeyName];

OR

NSDictionary *customAttributes = [attributeSet valueForKey:@"customAttributes"];
id customData = customAttributes[originalDataKeyName];

Key type in those dictionaries is either NSString* or CSCustomAttributeKey*, so you can try supplying both originalDataKeyName and originalDataKey.

like image 112
Eugene Dudnyk Avatar answered Nov 10 '22 17:11

Eugene Dudnyk