XCode+Swift+XPC: как запустить и развернуть цель Swift XPC на MacOS
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я относительно новичок в MacOS/XCode
Я хочу построить простой XPC Launch Agent в Swift (то есть: в ~/Library/LaunchAgents
) но я не смог найти много документации.
Я начал с шаблона XCode XPC, но я не знаю, была ли это хорошая идея для моего быстрого проекта.
Я заметил, что я должен был ~/Library/LaunchAgents/com.demo.myservice.plist
Версии:
- MaOS: 10.13.2
- Xcode: 9,2
Инструкция по созданию проекта XCode XPC:
- Файл> Новый проект
- Я выбрал шаблон MacOS: XPC
- Я создаю пакет 'com.demo.myservice'
- Это создает мне проект Objective-C. Поэтому я удаляю все файлы (т.е.
myserviceProtocol.h
,myservice.h
,myservice.m
,main.m
а такжеInfo.plist
- Создайте файлы:
myserviceProtocol.swift
import Foundation
@objc(myserviceProtocol) protocol myserviceProtocol {
func ping()
}
myservice.swift
import Foundation
class myservice : NSObject, myserviceProtocol {
func ping() {
print("ping")
}
}
main.swift
import Foundation
class ServiceDelegate : NSObject, NSXPCListenerDelegate {
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
newConnection.exportedInterface = NSXPCInterface(with:myserviceProtocol.self)
let exportedObject = myservice()
newConnection.exportedObject = exportedObject
newConnection.resume()
return true
}
}
// Create the listener and resume it:
//
let delegate = ServiceDelegate()
let listener = NSXPCListener.service()
listener.delegate = delegate;
listener.resume()
Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>com.demo.myservice</string>
<key>ProgramArguments</key>
<array>
<string>myservice</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
Я скопировал
Info.plist
в ~/Library/LaunchAgents/:cp ~/Documents/myservice/myservice/Info.plist ~/Library/LaunchAgents/com.demo.myservice.plist
Я получаю свой идентификатор пользователя сid -u
И затем я пытаюсь выполнить его из командной строки (поскольку он, похоже, ничего не делает из Xcode):
sudo launchctl debug user/501/com.demo.myservice /Users/olivier/Library/Developer/Xcode/DerivedData/myservice-hbwefcgibyqbajguvblgcmxsnrmd/Build/Products/Debug/myservice.xpc
Configuration failed: 113: Could not find specified service
Could not find service "com.demo.myservice" in domain for uid: 501
Я не совсем уверен в том, что я делаю. Был ли я права использовать XPC
Шаблон для создания моей быстрой XPC.
1 ответ
Если вы хотите, чтобы ваш агент предоставлял службу XPC, вам необходимо предоставить ее как службу Mach.
Вы инициализируете свой Listener для службы XPC (обратите внимание на заглавную букву S), служба XPC - это пакет, который является частью пакета вашего приложения, расположенного внутри Contents/XPCServices/
каталог.
Таким образом, вам необходимо:
1.) Создайте агента, который предоставляет службу XPC через службу mach. Ваш слушатель будет выглядеть так:
let listener = NSXPCListener(machServiceName: "com.rderik.exampleXPC" )
2.) Чтобы использовать службу вашего агента, вам необходимо установить соединение с этой службой.
let connection = NSXPCConnection(machServiceName: "com.rderik.exampleXPC")
Надеюсь, это поможет.
Если вы хотите узнать больше, я написал руководство о том, как это сделать здесь:
https://rderik.com/blog/creating-a-launch-agent-that-provides-an-xpc-service-on-macos/
Я не уверен, какова ваша цель. Если вам нужен демон, которым управляет launchd, вам не нужен XPC. Просто создайте демон (возможно, с помощью шаблона проекта Command Line Tool) и создайте файл конфигурации launchd plist (см. man launchd.plist
).
XPC предназначен для связи с хост-приложением. Двоичный файл службы должен быть встроен в ваш пакет приложений, и launchd запустит его, когда ваше приложение попытается подключиться. Вам не нужно изменять ~/Library/LaunchAgents для этого.
Ваше приложение должно настроить NSXPCConnection для подключения к XPCListener, который вы создали.
Что-то вроде:
let connection = NSXPCConnection(serviceName: "com.demo.myservice")
let interface = NSXPCInterface(with: myserviceProtocol.self)
connection.remoteObjectInterface = interface
connection.resume()
let proxy = connection.remoteObjectProxyWithErrorHandler {(error) in
os_log("Connection Error: %{public}@", error.localizedDescription)
} as! myserviceProtocol
// message proxy here to communicate with service
Оба сценария описаны более подробно в Руководстве по программированию демонов и служб.