How to create a helper app for macOS

Because of a missing replacement of deprecated temporary exceptions for the scripting bridge, we need a technical solution for the professional requirements, that is accepted for publication in the app store. See also this thread: https://forums.developer.apple.com/thread/111287


A suggested solution in the linked thread is to split up the app. We could provide core features in our app for the store and in addition a helper app, that the users can download form outside the store. The idea is, that the store app would be scriptable. Instead of the store app a helper helper app would respond to a shortcut in a service provider, capture the user's context with scripting bridge and forward the result to the scriptable main store app.


I could find a good tutorial with an example project in Github to make an app scriptable. I'm also familiar with service provider implementation, but not familar with helper apps. It should be an app, that has no user interface and would be startet by the store app after the helper app has been downloaded an installed. I think the correct approach is an XPCServer. Does someone know a good tutorial or example project?


Edit: I just reviewed the code template of Xcode. A scriptable app seems not necessary with XPCService and the process flow would be different. The main app would be triggered by a shortcut like today. This main app would ask the XPCService to capture the user's context with the scripting bridge and would receive the artifact to proceed with the data analysis process for retrieving contextual content.


I couldn't find instruction how to install a helper tool and start the XPC service from the main app.

Replies

That is not quite the intent of an XPC process. You can definitely use an XPC process if you want, but an XPC alone isn't going to help you escape the sandbox. It is possible and explicitly allowed for Mac App Store apps to have plug-ins that can escape the sandbox. But I am unaware of any Apple framework that is explicitly designed to help you do that. Also, for the Mac App Store, you will have to design your app carefully so that it has an acceptable level of functionality without the plug in.


But if you can handle all that, then you can design your plug-in however you want. There are numerous ways to have the plug-in and your sandbox app work together. Personally, I wouldn't recommend scripting because that is hard to do and fairly limited. Once you leave Apple's approved and intended path, Apple doesn't help you anymore, but then you're free to do whatever you want. Some suggestions would be a command line tool that accepts arguments, or a command line tool that can communicate over an open pipe with the sandboxed app. There will, of course, be limitations on sandboxed side.

Thanks for your answer. After reading some documentation and implementing an XPC Service sample for my playground project, it seems, that XPC Services are embedded in apps. I havent found any documentation about external XPC Services, that run independently from a parent app. I found somethin about launch deamons: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/DesigningDaemons.html


This seems to be something different. Then I come back to the idea to make a scriptable main app and write a terminal app, that calls the main app with an object.


An other approach could be to deploy an optional embedded XPC Service inside the app folder after the main app was downloaded and installed from the store. But I assume, that this would damage the integrity of the main app.


I think, we must find a solution with a helper app, that is downloadod outside the store. We wouldn't be the only app in the store, that requires an additional download. Cookie 5 is an example. They use another approach. The Cookie 5 app calls additional scripts.

I have seen a post recently about external XPC processes, but I couldn't find it. I don't think the question even concerned the sandbox. XPC is very useful for further restricting entitlements and for doing IPC with an easy-to-use RPC-style invocation. But I don't know if it would work to escape the sandbox. I'm not sure if you could even make it work with an external XPC, even if the sandbox was not an issue. Don't get me wrong, it may be possible, but it would be tricky and high risk.


Instead, I suggest looking at other approaches that are more straightfoward and virtually guaranteed to work. For example, a web server is really nothing more than IPC. You can always expect HTTP requests to succeed from the sandbox as long as you do the request correctly. You can test the server side very easily. Then it is just a question of making the connection from you app. A sandboxed app can also execute via NSTask virtually any command line tool that it can access. You could do everything on the command line.

Thanks for your recommandation. I will try two approaches:


  1. A helper app implemented as an scriptable agent app with a scurity group, that uses the Scrupting bridge itself to capture the context.
  2. A terminal app that is called with an NSTask and the result captured by NSPipe.


I'm not sure, whether it is possible to use an intermediate app for apple events. I have to test it. Further more I didn't find a way to call a scriptabel app with a parameter for a response. An app has to send a parameter and has to call the scriptable app a second time to get a response value, correct?


Edit


I have implemented a scritable helper app. The main app first has to send a parameter and then request an object from the helper app. Unfortunately I get a build error, if I use the header file of the helper app's scripting bridge:


Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_MyHelperApplication", referenced from:
      objc-class-ref in AnyClass.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)


I don't think, that the reason is a wrong sdef file, because I can reproduce the problem, if I try to import a header file of TextEdit in a sample project, that was crearted with the following command:


sdef /Applications/TextEdit.app | sdp -fh --basename TextEdit


This is strange, because the header files for other sriptable apps, that I have created some time ago, are working. Does someone has an idea to solve the problem?


Edit


I have used a wrong constructor for the scriptable app. The correct on eis:


TextEditApplication *textEdit = [SBApplication applicationWithBundleIdentifier:@"com.apple.TextEdit"];