When should we use "embedded binaries" rather than "Linked Frameworks" in Xcode?

In short,

  • system libraries, link them;
  • 3rd party libraries, embed them.

why?

  • if you try to embed system libraries, you won't find them in the popup list;
  • if you link 3rd party libraries, you'll probably get a crash.

Xcode pre-v11. Embedded Binaries vs Linked Frameworks and Libraries

History

Embedded Binaries vs Linked Frameworks and Libraries -> Frameworks, Libraries, and Embedded Content

[Xcode v11. Frameworks, Libraries, and Embedded Content] replaced it from Xcode v11 section in General tab

embedded binaries and Linked Frameworks are a part of Dependency management [About]

[Xcode v11]

Link Binary

General -> Linked Frameworks and Libraries is a mirror of Build Phases -> Link Binary With Libraries.

Static Library and Framework

If you add a Static Library or Static Framework to this section it will appear at Frameworks group[About](Project Navigator -> <workspace/project> -> Frameworks) and there will be a reference added to your project for it. Then it will be used by Static Linker. Static Linker at compile time will include/copy all code from the library into the executable object file. Static linker works in pair with Build Settings -> <Library/Framework> Search Paths

Static Library

  • Build Settings -> Library Search Paths[library not found] If you do not add a static library to this section you will get a linker error[ld: symbol(s) not found]

Static Framework

  • Build Settings -> Framework Search Paths. If you do not add a static framework to this section you will get a compile error[No such module]

Embed binary

Static Library and Static Framework

Embedding wouldn’t make any sense for a Static Library and Static Framework because the symbols from them are compiled into the executable binary. Xcode won’t let you drop a static library under the Embed section.

Dynamic Framework

General -> Embedded Binaries is a mirror of Build Phases -> Embed Frameworks.

Embedding actually adds a copy of the framework into your application bundle(not merging framework's and application's code into single executable binary)

By default the bundle's folder is Frameworks but you can change it using Destination field. Moreover you can specify a Subpath.

Dynamic linker :dyld at load or run time will try to find the embedded framework using @rpath[About] If it is not found the error will occur [dyld: Library not loaded]

Result:

  • Static Library - Link
  • Static Framework - Link
  • Dynamic Framework - Embed

[Static vs Dynamic linker]
[When use Link and Embed]
[Vocabulary]


The question you linked references the "Link Binary With Libraries" functionality, which is somewhat different than an embedded binary.

"Link Binary With Libraries" means what you'd expect it to with respect to linkage: Regardless of whether the binary is a static library, dynamic library, or framework it will be linked to your object code at link time after compilation.

When you think of linkage with a static library, what happens is pretty clear: the linker copies the code from the library (e.g. libFoo.a) into your output binary. Your output file grows in size but doesn't need to resolve any external dependencies at runtime. Everything your program needs to run (with respect to the static library) is present after it is built.

With a dynamic library (.dylib, or system-supplied framework), the expectation is that the library you are linking against will be present somewhere in the system's dynamic-library loader path when you run your program. This way you don't have the overhead of copying all the third party external libraries into your binary, and all the different programs on a computer that also link to that library will be able to find it, which saves minimally disk space, but also potentially memory space, depending on how and where the system caches libraries.

A framework is much like a dynamic library, but can contain resources in its directory structure (images, audio, other frameworks, etc.). In this case a simple static-library or .dylib file won't cut it so you might have to link to a framework just so it can find what it needs to run properly.

When you link to a third-party framework (say something you downloaded from github and built yourself), it might not be present on the system you intend to run on. In this case, you'd not only link to the framework, but embed it inside your application bundle as well using the "Copy Frameworks" phase. When your program runs, the runtime-linker (aka the resolver) will look inside your bundle in addition to the system loader path, find the embedded framework, and link it so your app will have the code it needs in order to run.

Finally, what is properly an "embedded binary" is an executable you both embed in your application bundle via a Copy-Files Phase, and that you execute yourself, perhaps with a call to popen() or similar. The embedded binary may be called by your program, but it isn't linked with it. It is a fully external entity (like programs in the /bin directory).

In practice, for system-supplied libraries and frameworks you will link against them and that's all you need to do.

If you need to link a library you built that doesn't need any embedded resources (i.e. doesn't require a framework to exist), then you can just link against a static library. If you find you have multiple modules in your program that want to use the same library code, then converting it to a framework or dynamic library and linking against that can save space and may be convenient (particularly if memory usage is a concern).

Finally, frameworks can include not only resources, but header and/or license files. Using a framework to convey these files is actually a convenient distribution mechanism so often you may want to incorporate a framework just so these things can tag along with your binary (i.e. license requirements may make this mandatory).

--- EDIT ---

Adam Johns posted the following question as a comment:

This is a great answer. There is something I'm still a little confused on, however. What does it mean to execute the binary yourself? Do you mean simply using the embedded framework's code? I know you mentioned popen(), but you're saying my app is calling popen()? I don't really know what that means.

I'm saying an embedded binary is just another resource file in your bundle, like an audio file or image, although the file is instead an executable command-line tool. The popen() function (man popen from your terminal to read more about it) lets you execute arbitrary programs from another running program. The system() function is another way. There are others, and I'll give a historical example here that may make understanding use of an embedded binary a bit more clear:

As you're probably aware, when you launch an app on Mac OS X it is launched with a user id of the current user. Under most common installations that's the default user-at-the-Desktop admin user, who is given user id 501.

On Unix-based operating systems only the root user (user id 0) has full access to the entire filesystem. Sometimes it happens that an installer program launched by the Desktop user needs to install files in a privileged directory (drivers for example). In this case, the application program needs to escalate its privileges to the root user so it can write in these restricted directories.

To facilitate this in operating systems through OS X 10.7, Apple provided in its Authorization Services API the function AuthorizationExecuteWithPrivileges() (this is now deprecated, but is still a useful example).

AuthorizationExecuteWithPrivileges() took as an argument a path to a command-line tool to execute as root. The command line tool was an executable shell script or compiled binary that you wrote to run your install logic. This tool was installed inside your application bundle just like any other resource file.

When called, the OS put up an authorization dialog asking for the user's password (you've seen this before!) and when entered would execute the program as root on your app's behalf. This process is similar to just executing a program with popen() yourself, although popen() alone doesn't give you the benefit of privilege escalation.