我正在用 Swift 编写一个 macOS 应用程序,它需要一个特权帮助工具——希望提升不是必需的,但看起来它是.
我发现这个优秀的示例应用程序特别适合这种情况。我已经设法将它的代码移植到我自己的应用程序中,但我被困在需要检查是否安装了帮助工具的地方,如果没有,请使用SMJobBless()
和朋友安装它。
运行示例应用程序时,如果未安装帮助工具,应用程序将停留在以下屏幕:
需要明确的是,通过阅读代码,我认为它应该在某个时候将标签更新为“Helper Installed:No”,但这似乎没有发生。
如果我单击“安装助手”,这就是结果。
从现在开始,除非我手动删除帮助工具,否则重新运行应用程序将显示此屏幕并显示“已安装帮助:是”。
在此示例情况下,此行为可能没问题,用户必须手动单击“安装助手”按钮。但是,在我的应用程序中,如果尚未安装,我希望它自动请求安装帮助工具。如果它已经安装,我不想浪费用户的时间再次请求他们的密码。
我认为这很简单:如果辅助工具不可用,在连接到它的过程中的某个地方,会发生错误,这是我请求安装该工具的触发器。如果没有发生错误,则假定该工具已安装。
这是我为通过 XPC 连接到帮助工具而编写的代码:
var helperConnection: NSXPCConnection?
var xpcErrorHandler: ((Error) -> Void)?
var helper: MyServiceProtocol?
// ...
helperConnection = NSXPCConnection(machServiceName: MyServiceName, options: .privileged)
helperConnection?.remoteObjectInterface = NSXPCInterface(with: MyServiceProtocol.self)
helperConnection?.resume()
helperConnection?.interruptionHandler = {
// Handle interruption
NSLog("interruptionHandler()")
}
helperConnection?.invalidationHandler = {
// Handle invalidation
NSLog("invalidationHandler()")
}
xpcErrorHandler = { error in
NSLog("xpcErrorHandler: \(error.localizedDescription)")
}
guard
let errorHandler = xpcErrorHandler,
let helperService = helperConnection?.remoteObjectProxyWithErrorHandler(errorHandler) as? MyServiceProtocol
else {
return
}
helper = helperService
如果未安装帮助工具,则运行此代码不会产生错误或NSLog()
输出。如果之后我通过 XPC(使用helper?.someFunction(...)
)调用函数,则什么也不会发生——我还不如与/dev/null
.
现在,我只能摸不着头脑,寻找一种技术来检测是否安装了该工具。示例应用程序解决问题的方法是添加一个getVersion()
方法;如果它返回某些内容,“Install Helper”将显示为灰色,并且标签更改为“Helper Installed: Yes”。
我想通过在我的工具中编写一个立即返回的简单函数来扩展这个想法,并在主应用程序中使用超时——如果我在代码超时之前没有得到结果,那么帮助工具可能不会安装。我发现这是一个 hacky 解决方案 - 例如,如果辅助工具(按需启动)启动时间过长,比如说因为计算机很旧并且用户正在运行 CPU 密集型的东西,该怎么办?
我看到了其他替代方法,例如在预期的位置 (/Library/PrivilegedHelperTools
和/Library/LaunchDaemons
) 中查看文件系统,但是这个解决方案再次让我感到不满意。
我的问题:有没有办法明确检测特权 XPC 辅助工具是否在另一端监听?
我的环境:macOS Mojave 10.14.2、Xcode 10.1、Swift 4.2。