Post

Replies

Boosts

Views

Activity

Reply to Programmatically passing files to Final Cut via Apple Events
Thank you so much Eskimo! I have submitted FB13816400 about the documentation page as you suggested. I also confirm using NSWorkspace works with Sandboxed app. Here is super simple stupid code example: let fileURL = [URL(fileURLWithPath: "/Users/user/Movies/test.fcpxml")] let appURL = URL(fileURLWithPath: "/Applications/Final Cut Pro Trial.app") let conf = NSWorkspace.OpenConfiguration() NSWorkspace.shared.open(fileURL, withApplicationAt: appURL, configuration: conf)
Topic: Code Signing SubTopic: Entitlements Tags:
Jun ’24
Reply to Programmatically passing files to Final Cut via Apple Events
[quote='788600022, DTS Engineer, /thread/751810?answerId=788600022#788600022'] I’m not super familiar with these plug-ins. Are they loaded within the Final Cut Pro app process? Or are they built as an app extension? [/quote] You can read more about them here https://developer.apple.com/documentation/professional_video_applications/workflow_extensions However the problem with sending Apple Events and Sandboxing is not directly related to Final Cut extensions. Right now we are just trying out the basic process with a generic macOS app created via Xcode template where we (try) to follow the instructions from Apple and make it work. What would you recommend we do next? Should we open a new DTS ticket about this specific usage of AppleEvents Vs. App Sandboxing?
Topic: Code Signing SubTopic: Entitlements Tags:
May ’24
Reply to Programmatically passing files to Final Cut via Apple Events
If you temporarily disable App Sandbox on your app, does that help? Yes, when I disable app Sandbox completely, there is no privilege validation error Normally when you pass a file reference via an Apple event I would assume so for the native file object. I did not know this was the case for POSIX file as well. I also tried to put the file into ~/Movies and in Targets -> Signing & Capabilities -> App Sandbox -> File Access -> Set Movies folder to Read/Write. This still produces the privilege violation error.
Topic: Code Signing SubTopic: Entitlements Tags:
May ’24
Reply to Programmatically passing files to Final Cut via Apple Events
Hey Eskimo. Good to see you in this thread! :-) Moving the file to /Users/JohnDoe/MyEvents.fcpxml does not help. Same error still occurs. Application is sandboxed (the default configuration when you create a new XCode project). The app in its current test form does not interact with the file at all. It just tries to tell Final Cut Pro to open it. Here is the most simple version of the code we are trying to make function: struct ContentView: View { var body: some View { VStack { Button { callScript() } label: { Text("Open file in Final Cut") } } .padding() } } func callScript(){ let scriptSourceToSend = """ tell application "Final Cut Pro Trial" activate open POSIX file "/Users/user/somefile.fcpxml" end tell """ let script = NSAppleScript(source: scriptSourceToSend) var error: NSDictionary? = nil let result = script?.executeAndReturnError(&error) if let error = error { print("Error executing AppleScript:", error) } else { print("XML file imported successfully.") } if result == nil { } }
Topic: Code Signing SubTopic: Entitlements Tags:
May ’24
Reply to NSXPCConnection from LaunchDaemon to LaunchAgent
I put together proof of concept daemon and agent based on advice from @eskimo. Looks like it's working. It's a bit naive implementation. However feel free to point out shortcomings. Protocols: swift @objc(TestDaemonXPCProtocol) protocol TestDaemonXPCProtocol {   func agentCheckIn(agentEndpoint: NSXPCListenerEndpoint, withReply reply: @escaping (Bool) - Void) } @objc(TestAgentXPCProtocol) protocol TestAgentXPCProtocol {   func doWork(task: String, withReply reply: @escaping (Bool) - Void) } Daemon: swift @objc class AgentXPCConnector: NSObject, TestDaemonXPCProtocol{     let connectionEstablished = DispatchSemaphore(value: 0)     var connection: NSXPCConnection?     func agentCheckIn(agentEndpoint: NSXPCListenerEndpoint, withReply reply: @escaping (Bool) - Void) {         if connection == nil {             logger.log("Agent checking in")             connection = NSXPCConnection(listenerEndpoint: agentEndpoint)             connection!.remoteObjectInterface = NSXPCInterface(with: TestAgentXPCProtocol.self)             connection!.resume()             reply(true)             connectionEstablished.signal()         } else {             logger.error("There is an agent alredy connected")             reply(false)         }     } } class DaemonXPCServer : NSObject, NSXPCListenerDelegate {     let agentResponder = AgentXPCConnector()     func waitForConnection() {         let timeOut = DispatchTime.now() + DispatchTimeInterval.seconds(86400)         switch agentResponder.connectionEstablished.wait(timeout: timeOut) {         case .success:             logger.log("Connection established")         case .timedOut:             logger.error("Timed out while waiting for connection")             exit(1)         }     }     func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) - Bool {         newConnection.exportedInterface = NSXPCInterface(with: TestDaemonXPCProtocol.self)         newConnection.exportedObject = agentResponder         newConnection.resume()         return true     } } let logger = Logger(subsystem: "cz.macadmin.xpcanon", category: "daemon") let server = DaemonXPCServer() let listener = NSXPCListener(machServiceName: "cz.macadmin.xpcanon.xpc") listener.delegate = server; listener.resume() logger.log("Daemon listening for agent connections") server.waitForConnection() logger.log("Creating agent remote object") let agent = server.agentResponder.connection!.synchronousRemoteObjectProxyWithErrorHandler { error in     logger.error("Problem with the connection to the agent \(String(describing: error))") } as? TestAgentXPCProtocol logger.log("Making the agent to do some work!") agent!.doWork(task: "Work Work") { (reply) in     if reply {         logger.log("Work success!")     } else {         logger.log("Work fail!")     } } logger.log("Daemon done") Agent: swift @objc class AgentXPC: NSObject, TestAgentXPCProtocol{     func doWork(task: String, withReply reply: @escaping (Bool) - Void) {         logger.log("Starting work")         sleep(5)         logger.log("Work DONE!")         reply(true)     } } class AgentAnonDelegate : NSObject, NSXPCListenerDelegate {     func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) - Bool {         let exportedObject = AgentXPC()         newConnection.exportedInterface = NSXPCInterface(with: TestAgentXPCProtocol.self)         newConnection.exportedObject = exportedObject         newConnection.resume()         return true     } } let logger = Logger(subsystem: "cz.macadmin.xpcanon", category: "agent") /* Prepare Anonymous listenter endpoint */ let anonDelegate = AgentAnonDelegate() let anonListener = NSXPCListener.anonymous() anonListener.delegate = anonDelegate anonListener.resume() let anonEndpoint = anonListener.endpoint /* Prepare connection to the daemon */ let daemonConnection = NSXPCConnection(machServiceName: "cz.macadmin.xpcanon.xpc", options: NSXPCConnection.Options.privileged) daemonConnection.remoteObjectInterface = NSXPCInterface(with: TestDaemonXPCProtocol.self) daemonConnection.resume() let daemon = daemonConnection.synchronousRemoteObjectProxyWithErrorHandler { error in     logger.log("Unable to connect to daemon") } as? TestDaemonXPCProtocol /* Try to checkin... forever! */ var connectedToDaemon = false while !connectedToDaemon {     daemon!.agentCheckIn(agentEndpoint: anonEndpoint) { (reply) in         logger.log("Passed endpoint to the deamon")         connectedToDaemon = true     }     sleep(1) } /* Nothing more to do here. Only doing work for the daemon */ logger.log("Agent is in the work loop") RunLoop.main.run() LaunchDaemon.plist: 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         keyLabel/key         stringcz.macadmin.xpcdaemon/string         keyProgram/key         string/path/to/xpcdaemon/string         keyRunAtLoad/key         true/         keyMachServices/key         dict             keycz.macadmin.xpcanon.xpc/key             true/         /dict /dict /plist Log output: daemon Daemon listening for agent connections daemon Agent checking in daemon Connection established daemon Creating agent remote object daemon Making the agent to do some work! agent Passed endpoint to the deamon agent Starting work agent Agent is in the work loop agent Work DONE! daemon Work success! daemon Daemon done
Mar ’21
Reply to NSXPCConnection from LaunchDaemon to LaunchAgent
Thank you so much! I am quite new to this to this so pointing me into right direction helps immensely. :-) My use case for XPC. I am working on Swift reimplementation of https://github.com/macadmins/installapplications (Python). It is a bootstrap tool to setup other nonMDM management tools. It work like this: Mac enrolls into MDM (Usually during SetupAssistant via Automated Device Enrollment but enrollment can be also initiated by the user). MDM sends InstallEnterpriseApplicationCommand the device which installs the package containing the bootstrap tool. Bootstrap tool runs as a daemon. It downloads other items (scripts and packages) and executes them sequentially. Packages and most of the scripts needs to be installed/run by the root user so daemon is ideal for this. However we also need to run scrips for the user after the login (with user privileges and inside his gui session). This is where the LaunchAgent comes in. After the list of task is completed bootstrap tool uninstalls itself (removing the daemon and agent as well). I need the daemon to be able to instruct the agent to run user scripts and usually wait until they finish (I use synchronousRemoteObjectProxyWithErrorHandler for this in my current code). Agent is launched either by launchd automatically (during the login) or by the package postinstall script if we install the package when there is already logged in user. If I understand usage of anonymous XPC listener correctly: Daemon will listen for incoming XPC requests (Agent checkin-in). Agent is going to use NSXPCConnection.Options.privileged to be able to connect to the daemon. When Agent checks in it sends the anonymous XPC listener endpoint to the daemon. Daemon can now use the endpoint to issue XPC calls to the Agent. Alternative to this would be Agent polling for work and reporting back the results. Since I would like to keep most of the logic inside the daemon (make agent as dumb as possible) anonymous XPC listener looks like way to go.
Mar ’21