Хорошо, похоже, я сделал несколько небольших ошибок, которые заставляют меня думать, что он не работает. Однако, в конце концов, я смог заставить его работать. Так что, если вы столкнулись с теми же задачами, здесь есть решение в Swift 3:
(1) Вы должны реализовать метод обслуживания:
import Cocoa
class ServiceProvider: NSObject {
let errorMessage = NSString(string: "Could not find the text for parsing.")
func service(_ pasteboard: NSPasteboard, userData: String?, error: AutoreleasingUnsafeMutablePointer<NSString>) {
guard let str = pasteboard.string(forType: NSStringPboardType) else {
error.pointee = errorMessage
return
}
let alert = NSAlert()
alert.messageText = "Hello \(str)"
alert.informativeText = "Welcome in the service"
alert.addButton(withTitle: "OK")
alert.runModal()
}
}
Здесь важно то, что я не знал раньше было то, что объект, предоставляющий услугу, должен подклассифицировать NSObject (он также может быть ViewController или любым другим подклассом NSObject). API-интерфейс служб использует селектор для его вызова, а технология выбора работает только с NSObject.
Кроме того, service
метод должен иметь такое заявление: он принимает 3 аргумента, первый является NSPasteboard
(вы можете использовать Pasteboard
, но это не дает string
метод), не маркированы, второй является факультативным String
маркированы userData
, третий AutoreleasingUnsafeMutablePointer<NSString>
с пометкой error
. Следуйте за этим, чтобы получить наилучшую безопасность по типу, все еще делая работу. В противном случае API-интерфейс служб не сможет найти его и вызвать.Вы можете использовать UnsafeRawPointers
для всех своих аргументов, но вы ничего не получите от этого.
(2) В приложение делегата (или документация говорит, что вы можете сделать это где угодно, но я делаю это здесь) регистрации поставщика услуг:
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
NSApplication.shared().servicesProvider = ServiceProvider()
NSUpdateDynamicServices()
}
}
Это, кажется, работает также без NSUpdateDynamicServices
звоните, но я думаю, что лучше быть в безопасности, чем извините - он должен обновить службы в системе, чтобы вам не приходилось выходить из системы и вводить пользователя в систему, чтобы получить текущую версию сервиса.
(3) И, наконец, вы должны настроить info.plist рекламировать свой метод сервиса:
<key>NSServices</key>
<array>
<dict>
<key>NSMessage</key>
<string>service</string>
<key>NSPortName</key>
<string>serviceTest</string>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Test hello world</string>
</dict>
<key>NSRestricted</key>
<false/>
<key>NSRequiredContext</key>
<dict/>
<key>NSSendTypes</key>
<array>
<string>NSStringPboardType</string>
</array>
</dict>
</array>
Это является источником XML из Info.plist - хотя компания Apple рекомендует использовать их редактор, в Xcode 8.2 Мне не удалось добавить ключ NSRequiredContext
через редактор - мне пришлось открыть файл как исходный код и добавить его вручную. Я бы рекомендовал редактировать напрямую источник XML.
Вы можете найти документацию о значении каждого ключа в Services Properties, но я хотел бы упомянуть некоторые важные моменты. Во-первых, вы НЕОБХОДИМО использовать ключ NSRequiredContext
- они упоминают его в документации, но редактор его не поддерживает - прямое редактирование XML и добавление его даже пустым (как и я). Во-вторых, если вы используете NSSendTypes
или и хотите работать со строками, используйте NSStringPboardType
, хотя его documentation говорит, что он устарел и должен быть заменен NSPasteboardTypeString
- последний не будет работать. Наконец, NSMessage
ключ с service
Значение - это имя метода услуги. Так что мой объект поставщик услуг объявляет следующий метод:
func service(_ pasteboard: NSPasteboard, userData: String?, error: AutoreleasingUnsafeMutablePointer<NSString>)
Я использую значение service
в NSMessage
. Если вы хотите изменить его (например, вы хотите несколько методов обслуживания), просто не забывайте, что эти два должны соответствовать (значение в конфигурации Info.plist и имя метода службы).
(4) В этом состоянии он должен работать. Есть одна вещь, которую я хотел бы упомянуть, которая может помочь вам в отладке. Проверьте это с помощью метода, упомянутого в documentation в конце команды:
/Applications/TextEdit.app/Contents/MacOS/TextEdit -NSDebugServices com.mycompany.myapp
Запуск этого из терминала должен сообщить, зарегистрирована ли какие-либо услуги, используя при условии сверток, а также будет ли он представлен - например, в моем случае проблема заключалась в том, что я не предоставил обязательный NSRequiredContext
. После тестирования с использованием этого подхода я смог узнать, что служба была установлена, и проблема заключалась в том, что API-интерфейс служб предполагал, что он должен быть отфильтрован. После некоторых экспериментов и googling я решил это, добавив пустой NSRequiredContext
.
После каждого изменения службы я рекомендую закрыть TextEdit и запустить ее снова, кажется, что она содержит ссылку на старый объект поставщика услуг (или что-то в этом роде, изменения не были зарегистрированы TextEdit, если я не перезапустил Это).