Embedded command line tool in Mac App Store app

I’ve started work on a command line tool for Developer Duck, an AI powered developer helper tool. The app can act as a rubber-duck-style chat assistant to help you with your code, and can also automate some of the simple and tedious tasks: adding/removing documentation comments, adding explanation comments, even code completion. I’d like to offer this functionality through the command line for integration into the rest of a developer’s workflow too.

As I soon found out, shipping a command line tool to the Mac App Store is not entirely straightforward. The problem: shipping to the Mac App Store requires a sandboxed app, and all bundled executables must be sandboxed as well. This ends up being slightly more complicated than just adding a Command Line Tool target to my Xcode project.

Thankfully, Apple provides documentation for exactly this task. And for the majority of the work, this documentation was perfect. Unfortunately, after following those instructions my command line program crashed immediately with a Trace/BPT trap: 5 error.

My first real clue was Console showing the following line:

ASI found [libsystem_secinit.dylib] (sensitive) 'Unable to get bundle identifier for container id com.the.product.bundle.identifier: Unable to get bundle identifier because code signature information has no Info.Plist.'

It seems that somehow the system is expecting this command line tool to have an Info.plist, which doesn’t entirely make sense to me. It’s not a real bundle, it’s just a single executable file. I’m must be missing something. Next, I found this helpful git repo which pointed to the related blog post by Alexandre Colucci. The clue was this step in his blog post:

To be properly code signed, the Info.plist needs to be embedded into the binary. This can be done using the CREATE_INFOPLIST_SECTION_IN_BINARY setting

https://blog.timac.org/2021/0516-mac-app-store-embedding-a-command-line-tool-using-paths-as-arguments/

Ah! This must be it – the message in Console was complaining about not finding an Info.plist, and so if we can embed that plist into the executable file, maybe that’ll do the trick? Odd, because there’s still not an Info.plist file for this target, but maybe it’ll make an empty section in the executable? I added this build setting, but unfortunately still no dice. I get the same crash. I even set an INFOPLIST_FILE build setting on the target and created an empty Info.plist, but that didn’t seem to work either.

Then I decided to filter the Build Settings in Xcode for “plist” and found the promising GENERATE_INFOPLIST_FILE setting. I updated my .xcconfig with both of these:

CREATE_INFOPLIST_SECTION_IN_BINARY = YES
GENERATE_INFOPLIST_FILE = YES

Success! Now, Xcode will generate whatever Info.plist it needs and embed it into the binary. This allows it to be found when running the command line executable, and everything works great.

My next adventure will be to read and write files that were passed as arguments to the tool. The same post by Alexandre very helpfully covers this case too!