Я пишу приложение для MacOS на Swift, которому нужен привилегированный вспомогательный инструмент - хотелось бы, чтобы повышение не было необходимости, но похоже, что оно .
Я нашел это отличный пример приложения, специально созданный для этого сценария. Мне удалось перенести его код в мое собственное приложение, но я застрял в том месте, где мне нужно проверить, установлен ли вспомогательный инструмент, а если нет, использовать SMJobBless()
и друзей для его установки.
При запуске примера приложения, если вспомогательный инструмент не установлен, приложение застревает на следующем экране:
![enter image description here](https://i.stack.imgur.com/ryGyw.png)
Чтобы было ясно, после прочтения кода я подумал, что в какой-то момент я должен был обновить ярлык до "Установлен помощник: нет", но, похоже, этого не происходит.
Если я нажму «Установить помощник», это будет результат.
![enter image description here](https://i.stack.imgur.com/kuXu8.png)
С этого момента, если я не удалю вспомогательный инструмент вручную, при повторном запуске приложения на этом экране будет отображаться «Установлено вспомогательное средство: Да».
Это может быть нормально в этом примере ситуации, когда пользователь должен вручную нажать кнопку «Установить помощник». Однако в моем приложении мне бы хотелось, чтобы оно автоматически запрашивало установку вспомогательного инструмента, если он еще не установлен. Если он уже установлен, я не хочу тратить время пользователя на повторный запрос пароля.
Я подумал, что это будет достаточно просто: если вспомогательный инструмент недоступен, где-то в процессе подключения к нему произойдет ошибка, которая послужит мне триггером для запроса установки инструмента. Если ошибки не возникают, предполагается, что инструмент уже установлен.
Вот взломанный код, который я написал для подключения к вспомогательному инструменту через 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».
Я подумал о том, чтобы немного расширить эту идею, написав простую функцию в моем инструменте, которая мгновенно возвращает результат, и использовать тайм-аут в основном приложении - если я не получу результат до истечения времени ожидания кода, вспомогательный инструмент скорее всего не установлен. Я считаю это хакерским решением - что, если, например, вспомогательный инструмент (запускаемый по требованию) запускается слишком долго, скажем, из-за того, что компьютер устарел, а пользователь использует что-то интенсивное использование процессора?
Я вижу другие альтернативы, такие как просмотр файловой системы в ожидаемых местах (/Library/PrivilegedHelperTools
и /Library/LaunchDaemons
), но опять-таки это решение кажется мне неудовлетворительным.
Мой вопрос: Есть ли способ однозначно определить, прослушивает ли привилегированный вспомогательный инструмент XPC на другом конце?
Моя среда: macOS Mojave 10.14.2, Xcode 10.1, Swift 4.2.