Help me implement SMAppServices

I have followed these steps as mentioned in this link :(https://developer.apple.com/forums/thread/721737)

My projects app bundle structure is like this :

TWGUI.app
TWGUI.app/Contents
TWGUI.app/Contents/_CodeSignature
TWGUI.app/Contents/_CodeSignature/CodeResources
TWGUI.app/Contents/MacOS
TWGUI.app/Contents/MacOS/TWAgent
TWGUI.app/Contents/MacOS/TWGUI
TWGUI.app/Contents/Resources
TWGUI.app/Contents/Library
TWGUI.app/Contents/Library/LaunchAgents
TWGUI.app/Contents/Library/LaunchAgents/com.example.TWGUI.agent.plist
TWGUI.app/Contents/Info.plist
TWGUI.app/Contents/PkgInfo

TWGUI is my main GUI App , i which i want to embed TWAgent (a command line tool target) and register it using SMAppServices so that launchd can launch it.

In TWGUI, code for registering to launchd using SMAppServices is structure as follow :

import SwiftUI
import ServiceManagement
struct ContentView: View {
let agent = SMAppService.agent(plistName: "com.example.TWGUI.agent.plist")
var body: some View {
VStack {
Button("Register Agent") {
RegisterAgent ()
}
.padding()
Button("Unregister Agent") {
UnregisterAgent ()
}
.padding()
}
}
func RegisterAgent() {
DispatchQueue.global(qos: .background).async {
do {
print("Registering Agent. Status: \(agent.status.rawValue)")
try agent.register()
print("Agent registered")
} catch {
print("Failed to register agent: \(error)")
}
}
}
func UnregisterAgent() {
DispatchQueue.global(qos: .background).async {
do {
print("Unregistering Agent. Status: \(agent.status.rawValue)")
try agent.unregister()
print("Agent unregistered")
} catch {
print("Failed to unregister agent: \(error)")
}
}
}
}

com.example.TWGUI.agent.plist :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs$
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.TWGUI.agent</string>
<key>ProgramArguments</key>
<array>
<string>Contents/MacOS/TWAgent</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>

I have used ProgramArguements instead of using Program in above plist because i was getting this error when i was using Program earlier :

Registering Agent. Status: 3
Failed to register agent: Error Domain=SMAppServiceErrorDomain Code=111 "Invalid or missing Program/ProgramArguments" UserInfo={NSLocalizedFailureReason=Invalid or missing Program/ProgramArguments}

TWGUI apps Info.plist is :

<?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>BuildMachineOSBuild</key>
<string>23C71</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>TWGUI</string>
<key>CFBundleIdentifier</key>
<string>com.example.TWAgent</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>TWGUI</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string></string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>14.2</string>
<key>DTSDKBuild</key>
<string>23C53</string>
<key>DTSDKName</key>
<string>macosx14.2</string>
<key>DTXcode</key>
<string>1510</string>
<key>DTXcodeBuild</key>
<string>15C65</string>
<key>LSMinimumSystemVersion</key>
<string>14.2</string>
</dict>
</plist>

TWAgent target has main.swift file which does this :

import Foundation
let startTime = CFAbsoluteTimeGetCurrent()
func logTimeSinceStart() {
let elapsedTime = CFAbsoluteTimeGetCurrent() - startTime
NSLog("Time since program started: \(elapsedTime) seconds")
}
func startLoggingTime() {
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
logTimeSinceStart()
}
}
// Start logging time
startLoggingTime()
// Keep the run loop running
CFRunLoopRun()

I followed these exact same steps in another project earlier and my agent was getting registered, although i lost that project due to some reasons.

But now i am getting this error when i am registering or unregistering agent using SMAppServices from the code above :

Registering Agent. Status: 3
Failed to register agent: Error Domain=SMAppServiceErrorDomain Code=1 "Operation not permitted" UserInfo={NSLocalizedFailureReason=Operation not permitted}

I tried diffrent fixes for like this :

  • Moved app bundle to /applications folder
  • Gave permission for full disc access to this app .
  • Code sign again (both agent and TWGUI

...

But nothing seems to work , getting same error.

I tried to launch agent using :

Launchctl load com.example.TWGUI.agent.plist

and it worked , so there is no issue with my plist implementation.

Can someone help me understand how can i solve this issue ? or if i am following right steps ? Can give steps need to follow to implement this and steps so that i can register and start my agent using SMAppServices?

And i also tried the project give in apples official documentation : [https://developer.apple.com/documentation/servicemanagement/updating-your-app-package-installer-to-use-the-new-service-management-api)

but got same error in this project as well .

After a while when i ran the xcode project again without making any changes , application worked fine , agent was getting registered/running , i was getting logs for agent in console.app. Tried it multiple time and it was working whole day.

But today again ran application from xcode , i started getting same error .

Registering Agent. Status: 3
Failed to register agent: Error Domain=SMAppServiceErrorDomain Code=1 "Operation not permitted" UserInfo={NSLocalizedFailureReason=Operation not permitted}

What possibly could be the reason for such behavior?

Just FYI, I recommend against using the Dispatch global concurrent queues. See Avoid Dispatch Global Concurrent Queues.

Also, the .background QoS is something to be wary of. On iOS, for example, work scheduled this won’t run at all if Low Power Mode is enabled.

But, anyway, back to your real problem…


I think your main issue is the way you’ve specified the agent launch path. You need to use BundleProgram, with a relative path from the root of your app’s bundle, that is, Contents/MacOS/TWAgent.

The other potential gotcha is sandboxing. If your main app is sandboxed, we should talk about how to handle that in your agent.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Help me implement SMAppServices
 
 
Q