What is a .app?

In macOS, I browse to my /Applications directory to see lots of files. Keynote.app, Spotify.app, Vidrio.app, etc. What are these things? These “applications” can’t be launched in the normal fashion:

$ /Applications/Spotify.app/
-bash: /Applications/Spotify.app/: is a directory

They’re not files, they’re directories!

$ cd /Applications/Spotify.app
$ tree -L 2
└── Contents
    ├── Frameworks
    ├── Info.plist
    ├── MacOS
    ├── Resources
    └── _CodeSignature

Despite being directories, we can open them to launch the application. What program is used to run this?

$ pstree -p 38699
-+= 00001 root /sbin/launchd
 \-+= 38699 jim /Applications/Spotify.app/Contents/MacOS/Spotify
   |--- 38705 jim /Applications/Spotify.app/Contents/Frameworks/Spotify Helper.app/Contents/MacOS/Spotify Helper --type=gpu-process --lang=en-US --log-file=/
   \--- 38710 jim /Applications/Spotify.app/Contents/Frameworks/Spotify Helper.app/Contents/MacOS/Spotify Helper --type=renderer --disable-pinch --primordial

The process 1, /sbin/launchd, started /Applications/Spotify.app/Contents/MacOS/Spotify.

The real program binary is at Contents/MacOS/Spotify within the .app directory. We can run this program ourselves, but it doesn’t run correctly for some reason:

$ /Applications/Spotify.app/Contents/MacOS/Spotify
2017-05-07 12:41:40.858 Spotify[39249:1666301] NSWindow warning: adding an unknown subview: <_NSThemeCloseWidget: 0x7f8ca4482130>. Break on NSLog to debug.
2017-05-07 12:41:40.859 Spotify[39249:1666301] Call stack:
	0   AppKit                              0x00007fffbfa56ebd -[NSThemeFrame addSubview:] + 109
	1   Spotify                             0x0000000108524c02 Spotify + 1461250
	2   Spotify                             0x0000000108524958 Spotify + 1460568

We can make an “application bundle” from scratch. Make any binary, like so:

#include <unistd.h>
#include <stdio.h>
char log_line[] = "log line\n";
int main() {
	FILE * f = fopen("/tmp/log", "w");
	for (;;) {
		size_t num_written = fwrite(log_line, sizeof(log_line), 1, f);
		printf("num_written: %zu\n", num_written);
		int flush_result = fflush(f);
	return 0;

Compile this and construct the application bundle directories:

$ mkdir -p Foo.app/Contents/MacOS
$ clang Foo.c -o Foo.app/Contents/MacOS/Foo
$ open Foo.app/ &
$ cat /tmp/log
log line
log line
log line
log line

It seems the expected path for the binary in <Name>.app is /Contents/MacOS/<Name>.

Tagged .

Similar posts

More by Jim

Want to build a fantastic product using LLMs? I work at Granola where we're building the future IDE for knowledge work. Come and work with us! Read more or get in touch!

This page copyright James Fisher 2017. Content is not associated with my employer. Found an error? Edit this page.