Search code examples
swiftmacosspotlightcorespotlight

How to Add CoreSpotlight to macOS App in Swift - CoreSpotlight Indexing


How do you get CoreSpotlight to index a Mac app?

I cannot figure out how to get my macOS App to index with CoreSpotlight.

  1. Is there some configuration, Info.plist setting, or capability that I need to enable to show up in Spotlight search results on Mac?
  2. Am I doing something wrong? None of the iOS examples that I converted from iOS to macOS work for me.
  3. I'm expecting to see my "TestSpotlight" app in the search results, but I see nothing in Spotlight

'10 min timer' in spotlight does not appear with Spotlight

import SwiftUI
import CoreSpotlight

@main
struct TestSpotlightApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var delegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class AppDelegate: NSObject, NSApplicationDelegate {
    
    var searchableItems = [CSSearchableItem]()
    
    func addSearchableTerms() {
        let itemSet = CSSearchableItemAttributeSet(contentType: .content)
        let timerTitle = "10 min timer"
        let identifier = "com.myapp.timers.10min"
        itemSet.title = timerTitle
        itemSet.contentDescription = timerTitle
        itemSet.keywords = ["timer", "minute", "10", "ten"]
        itemSet.identifier = identifier
        itemSet.duration = NSNumber(integerLiteral: 10 * 60)
        
        let searchableItem = CSSearchableItem(uniqueIdentifier:identifier, domainIdentifier: "timers", attributeSet: itemSet)
        searchableItems.append(searchableItem)
        
        // TODO: Add more timers programmatically
        
        CSSearchableIndex.default().indexSearchableItems(searchableItems) { (error) in
            if let error = error {
                // handle failure
                print(error)
            } else {
                print("Indexed: \(self.searchableItems)")
            }
        }
    }
    
    func applicationDidFinishLaunching(_ notification: Notification) {
        addSearchableTerms()
    }
}

References:

  1. Indexing content using Core Spotlight
  2. CoreSpotlight indexing not working

Solution

  • I figured out how to get it work by setting the CSSearchableItemAttributeSet(contentType:) to something else. Both .application or .text seem to work. Thanks @ATV

    import Cocoa
    import CoreSpotlight
    
    @main
    class AppDelegate: NSObject, NSApplicationDelegate {
        var searchableItems: [String : CSSearchableItem] = [:]
        
        func addSearchTerm(identifier: String, title: String, duration: Int, keywords: [String]) {
            let itemSet = CSSearchableItemAttributeSet(contentType: .application) // or use .text
            itemSet.title = title
            itemSet.contentDescription = title
            itemSet.keywords = keywords
            itemSet.identifier = identifier
            //        itemSet.textContent = title
            itemSet.duration = NSNumber(integerLiteral: duration)
            
            let searchableItem = CSSearchableItem(uniqueIdentifier:identifier, domainIdentifier: "timers", attributeSet: itemSet)
            searchableItems[identifier] = searchableItem
        }
        
        func addSearchableTerms() {
            addSearchTerm(identifier: "com.paulsolt.SpotlightTimer.10min", title: "10 minute timer", duration: 10 * 60, keywords: ["min"])
            addSearchTerm(identifier: "com.paulsolt.SpotlightTimer.3min", title: "3 minute timer", duration: 3 * 60, keywords: ["min"])
            addSearchTerm(identifier: "com.paulsolt.SpotlightTimer.4min", title: "4 minute timer", duration: 4 * 60, keywords: ["min"])
            // TODO: Add more timers programmatically
            
            CSSearchableIndex.default().indexSearchableItems(Array(searchableItems.values)) { (error) in
                if let error = error {
                    // handle failure
                    print(error)
                } else {
                    print("Indexed: \(self.searchableItems)")
                }
            }
        }
        
        func applicationDidFinishLaunching(_ aNotification: Notification) {
            addSearchableTerms()
        }
        
        func application(_ application: NSApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([any NSUserActivityRestoring]) -> Void) -> Bool {
            if userActivity.activityType == CSSearchableItemActionType,
               let identifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
                handleSearchableItem(withIdentifier: identifier)
                return true
            }
            return false
        }
        
        func handleSearchableItem(withIdentifier identifier: String) {
            if let item = searchableItems[identifier],
               let duration = item.attributeSet.duration?.intValue {
                self.startTimer(duration: duration)
            } else {
                print("Duration not found in attributes")
            }
        }
        
        func startTimer(duration: Int) {
            // Your code to start the timer with the specified duration
            print("Starting timer for \(duration) seconds")
        }
    }
    

    The item will be lower in the list under Other or Documents, and only by interacting does it seem to get promoted.

    Spotlight Custom 10 Minute Timer

    I'm wondering if it makes more sense to use a NSUserActivity instead, but I haven't been able to get that to work.