如何在macOS应用程序中从App注册服务?

米兰诺萨

我想实现,将在服务时选择了文本呈现的上下文菜单项。因此,例如,如果我在TextEdit中选择了一个单词,我希望在上下文菜单中显示一个菜单项“ Do my stuff”,它将选择的单词馈送到我的应用程序代码中以进行进一步处理。

通过仔细研究,我得出结论,我需要实施和注册服务。我尝试遵循提供服务的文档,但这似乎至少有些过时(更不用说在某些地方含糊)。

据我了解,我能够做到以下几点:

我实现了服务对象:

import Foundation
import AppKit

class ContextualMenuServiceProvider {

    func importString(_ pasteBoard: Pasteboard, userData: String, error: String) {
         print(">>>> in import string from service consumer")
    }
}

我在Info.plist中为服务创建了一个条目:

    <key>NSServices</key>
    <array>
        <dict>
            <key>NSMenuItem</key>
            <dict>
                <key>default</key>
                <string>Import to $(PRODUCT_NAME)</string>
            </dict>
            <key>NSMessage</key>
            <string>importString</string>
            <key>NSPortName</key>
            <string>MyApp</string>
            <key>NSSendTypes</key>
            <array>
                <string>public.plain-text</string>
            </array>
        </dict>
    </array>

最后,我尝试在AppDelegate中注册该服务。现在文档说要使用:

NSApp.setServicesProvider(encryptor)
// where encryptor is an object of my ContextualMenuServiceProvider

但是,似乎NSApp没有setServicesProvider方法。我尝试使用:

 NSApp.servicesProvider = ContextualMenuServiceProvider()

属性,但是,这似乎也不起作用。

我通过从Xcode运行应用程序来对其进行测试,然后在应用程序运行时,我尝试在TextEdit中选择一些文本(不应该局限于TextEdit,我仅以它为例),然后右键单击我正在寻找我的菜单项。这没用。

我能够找到某种类似的SO问题(例如,在Delphi XE2中创建os x服务如何向Mac OS Finder中添加菜单项),但是我无法从中检测到我做错了什么(不是提及其中大多数是较旧的(使用setServiceProvider),或使用C#或Delphi之类的语言)。

知道我的代码/方法有什么问题吗?

米兰诺萨

好的,看来我犯了几个小错误,使我相信它不起作用。但是,最终我能够使它起作用。因此,如果您面临相同的任务,这是Swift 3中的解决方案:

(1)您必须实现一种服务方法:

import Cocoa

class ServiceProvider: NSObject {

    let errorMessage = NSString(string: "Could not find the text for parsing.")

    @objc 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子类)。Services 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>

这是Info.plist的XML源-尽管Apple建议使用其编辑器,但在Xcode 8.2中,我无法NSRequiredContext通过编辑器添加密钥-我不得不将文件作为源代码打开并手动添加。我建议直接编辑XML源。

您可以在“服务属性”中找到有关每个键的含义的文档,但我想提及一些关键点。首先,您需要NSRequiredContext密钥-他们在文档中提到了它,但是编辑器不支持它-直接编辑XML并将其添加为空(就像我一样)。其次,如果您正在使用NSSendTypesNSReturnTypes并且想要使用字符串,则NSStringPboardType即使其文档说它已被弃用,也应使用,而应使用代替NSPasteboardTypeString-后者将不起作用。最后,NSMessage带有service值的是服务方法的名称。因此,我的服务提供者对象声明以下方法:

@objc func service(_ pasteboard: NSPasteboard, userData: String?, error: AutoreleasingUnsafeMutablePointer<NSString>)

我正在使用中的serviceNSMessage如果要更改它(例如,您想要几种服务方法),只是不要忘记这两个必须匹配(Info.plist配置中的值和服务方法的名称)。

(4)在这种状态下,它应该可以工作。我想提到的一件事可能会帮助您进行调试。通过运行以下命令,使用文档最后提到的方法对其进行测试

 /Applications/TextEdit.app/Contents/MacOS/TextEdit -NSDebugServices com.mycompany.myapp

从终端运行此命令应报告是否已注册使用提供的捆绑软件的任何服务,以及是否将其呈现-例如,在我的情况下,问题是我未提供必需的NSRequiredContext使用这种方法对其进行测试之后,我能够识别出该服务已安装,并且问题在于服务API假定应将其过滤掉。经过一些实验和谷歌搜索,我通过添加空值解决了它NSRequiredContext

每次更改服务后,我建议退出TextEdit并再次运行它,似乎它保留了对旧服务提供者对象的引用(或类似的东西,如果我不重新启动它,则TextEdit不会注册更改)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何在macOS Catalina中删除系统应用程序?

如何在SwiftUI macOS应用程序中模糊背景?

如何在应用程序服务容器中安装SSH

如果未注册为服务,如何在启动时自动启动应用程序

如何在服务应用程序中包含表单应用程序?

如何在VB中检查应用程序是否为服务应用程序

如何在应用程序中添加App Indexing概念?

如何在Linux的用户应用程序中注册“ uevent”?

如何在KDE中注册应用程序?

如何在Rails应用程序上注册serviceWorker?

如何在应用程序中注册翻译功能

如何在Spring Boot应用程序中注册所有子包中的bean?

如何在Vue和TypeScript应用程序中全局正确注册axios

如何在JavaScript应用程序中获取不同的FCM注册令牌?

如何在 Web 应用程序部署期间在注册表中添加新的时区?

如何在混合应用程序中的角度控制器内部注册cordova事件

如何在Microsoft Store中免费注册到公共应用程序?

如何在我的 React 应用程序中强制使用 Firebase 身份验证进行注册?

安装VB.net应用程序时如何在注册表中写入值?

如何在现有应用程序中通过邀请进行注册?

如何在 azure 应用程序注册中显示客户端机密的隐藏值

如何在应用程序包 [macOS] 中的 C 中查找当前目录

如何在Xcode之外运行Xcode应用程序?MacOS Standalone应用程序与Xcode Simulator中的应用程序运行不同

如何在macOS(不是iOS)上的SwiftUI中以编程方式从后台打开应用程序窗口?

如何在基于文档的 macOS 应用程序中处理不同的文档类型?

如何在 macOS 上的 Swift 中在 PATH 上启动终端应用程序?

如何在 macOS 应用程序中从文件系统读取图像

如何在macOS下的非基于文档的应用程序中添加选项卡?

如何在.NET Core控制台应用程序中获取MacOS用户名?