EasySpotlight makes it easy to index contento to CoreSpotlight and handle app launches from Spotlight.
CoreSpotlight
is only available in iOS 9.0 or later
EasySpotlight is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod "EasySpotlight"
Or through Carthage adding this to your Cartfile:
github "felix-dumit/EasySpotlight"
To use simply create a struct or class that implements the SpotlightConvertable
protocol.
/**
* Protocol that enables types to be saved to spotlight search
*/
public protocol SpotlightConvertable {
/// title displayed in search
var title: String? {get}
/// content description displayed in search
var contentDescription: String? {get}
/// unique idenfitier
var identifier: String {get}
//optional
var itemType: String {get}
var thumbnailImage: UIImage? {get}
func configure(searchableItem item: CSSearchableItem)
}
Here is a simple example of a struct that implements the protocol and becomes indexable.
struct SimpleStruct: SpotlightConvertable {
var title:String? = nil
var identifier:String
}
You can now use all of the following methods:
let x = SimpleStruct(title:"Bob", identifier:"identifier")
EasySpotlight.index(x) { error in
handleError(error)
}
EasySpotlight.deIndex(x)
EasySpotlight.deIndexAll(of: SimpleStruct.self)
EasySpotlight.index([x])
If you want to further configure the CSSearchableItem
and CSSearchbleAttributeItemSet
properties you can implement the configureSeachableItem
method in the protocol.
In order to easily handle when the app is opened via a spotlight search, you must implement the SpotlightRetrievable
protocol.
There is also a helper SpotlightSyncRetrievable
protocol in case your retrieval code is thread safe and you do not need the retrieving code to be executed on the same queue as the fetched item will be consumed on or not.
Here is the simple example:
extension SimpleStruct: SpotlightSyncRetrievable {
static func retrieveItem(with identifier: String) throws -> SimpleStruct? {
return SimpleStruct(title: "item_\(identifier)", contentDescription: "cool", identifier: identifier)
}
}
The method will be called in a background queue and then your registered handler will be called in the registered queue.
But if you are using something like Realm to save your objects (not thread safe), you need something like:
extension SimpleRealmClass: SpotlightRetrievable {
static func retrieveItem(with identifier: String) throws -> (() throws -> SimpleRealmClass?) {
let obj = try Realm().object(ofType: self, forPrimaryKey: identifier)
let safe = obj.map { ThreadSafeReference(to: $0) }
return {
guard let safe = safe else { return nil }
return try Realm().resolve(safe)
}
}
}
The method is still called in a background queue, but the returned closure will be executed in the same queue as the registered handler.
Now you have to register a handler for that type in application:didFinishLaunchingWithOptions:
:
EasySpotlight.registerIndexableHandler(SimpleStruct.self, queue: .main) { item in
// handle when item is opened from spotlight
assert(item is SimpleStruct)
print("started with: \(item)")
}
The queue parameter is optional and defaults to .main
Now all that is left is to implement the function called when the app opens from a spotlight search:
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
return EasySpotlight.handle(activity: userActivity) || handleOtherUserActivities(userActivity)
}
See the included example application for a sample that indexes and retrieves objects from Realm
.
New feature ideas, suggestions, or any general feedback is greatly appreciated.
Felix Dumit, felix.dumit@gmail.com
EasySpotlight is available under the MIT license. See the LICENSE file for more info.