import Foundation import UniformTypeIdentifiers /// A helper class for creating Spotlight importer plug-ins. /// /// Any `CFPlugIn`, and that includes a Spotlight plug-in, has a single entry /// point with the C prototype: /// /// ``` /// extern void * myPlugInEntryPoint(CFAllocatorRef allocator, CFUUIDRef typeUUID) /// ``` /// /// To create a Spotlight plug-in: /// /// 1. Run the `uuidgen` command-line tool to generate a factory UUID for your /// plug-in. This UUID identifies your specific plug-in, so you need a new /// one each time you start a new Spotlight plug-in project. /// /// 2. Subclass this class. /// /// 3. Implement the `importFrom(_:type:updating:)` method, declared below. /// /// 4. Write an entry point function with the prototype above that calls through /// to the `class makeInstance(factoryID:)` method, passing in the factory /// UUID from step 1. /// /// 5. Configure your `Info.plist` with the factory UUID from step 1 and the /// name of your plug-in entry point from step 4. open class QSpotlightPlugIn: QCFPlugIn { /// Plug-in entry point. /// /// Call this method from your plug-in’s entry point, passing in the factory /// UUID that identifiers your specific plug-in. /// /// - important: This factory UUID needs to match that in your `Info.plist` /// (it actually shows up twice, once in `CFPlugInFactories` and again in /// `CFPlugInTypes). /// /// - Parameter factoryID: The factory UUID for this plug-in. final class func makeInstance(factoryID: UUID) -> UnsafeMutableRawPointer? { // `kMDImporterTypeID` is defined by a complicated macro that Swift is // unable to import, so I’ve just copied the value here. This should // be safe because the value can’t reasonably change because it’s are // baked into a bazillion shipping Spotlight importer plug-ins. let kMDImporterTypeID = UUID(uuid: (0x8B, 0x08, 0xC4, 0xBF, 0x41, 0x5B, 0x11, 0xD8, 0xB3, 0xF9, 0x00, 0x03, 0x93, 0x67, 0x26, 0xFC)) return self.makeInstance(factoryID: factoryID, typeID: kMDImporterTypeID, typeVTableSize: MemoryLayout.size) } final public override func setupVTable(_ vtable: UnsafeMutableRawPointer, for interfaceID: UUID) -> Bool { // I’ve hard-coded `kMDImporterURLInterfaceID` much like I did for // `kMDImporterTypeID`. let kMDImporterURLInterfaceID = UUID(uuid: (0x13, 0xF6, 0x0F, 0x02, 0x36, 0x22, 0x4F, 0x35, 0x98, 0x91, 0xEC, 0x10, 0xE6, 0xCD, 0x08, 0xF8)) guard interfaceID == kMDImporterURLInterfaceID else { return false } let vtable = vtable.assumingMemoryBound(to: MDImporterURLInterfaceStruct.self) vtable.pointee.ImporterImportURLData = { interface, attributes, contentTypeUTI, urlForFile in let obj = QSpotlightPlugIn.object(forInstance: interface!) do { try obj.importFrom(urlForFile! as URL, type: UTType(contentTypeUTI! as String)!, updating: attributes!) return true } catch { return false } } return true } /// Import data entry point. /// /// Implement this method to support Spotlight import. /// /// This method corresponds to the [`ImporterImportData`][refImporterImportData] /// member of the `MDImporterInterfaceStruct`. The `url`, `contentTypeUTI`, /// `attributes` parameters all have the same meaning as their equivalent /// parameters in that API. /// /// [refImporterImportData]: /// /// - Parameters: /// - url: Equivalent to the `pathToFile` parameter of `ImporterImportData` /// - contextTypeUTI: See `ImporterImportData`. /// - attributes: See `ImporterImportData`. open func importFrom(_ url: URL, type contextTypeUTI: UTType, updating attributes: NSMutableDictionary) throws { fatalError("Implement this in your factory-specific subclass.") } }