A Visual Guide to Creating an Embeddable Framework for Mac OSX

From the Framework Programming Guide

  • Frameworks group related, but separate, resources together. This grouping makes it easier to install, uninstall, and locate those resources.
  • Frameworks can include a wider variety of resource types than libraries. For example, a framework can include any relevant header files and documentation.
  • Multiple versions of a framework can be included in the same bundle. This makes it possible to be backward compatible with older programs.
  • Only one copy of a framework’s read-only resources reside physically in-memory at any given time, regardless of how many processes are using those resources. This sharing of resources reduces the memory footprint of the system and helps improve performance.

Now that we've got a handle on what frameworks are let's set about building one. Open up Xcode and create a new project. We'll use the "Cocoa Framework" template which can be found under the "Mac OSX" group.

Our framework will return a random Cary Grant movie quote so we're going to name it "CaryGrantQuotes". This framework will have only one method which is defined in Quotes.m. Since it's such a simple framework, we can remove all of the frameworks listed under "Linked Frameworks" and "Other Frameworks" save for Foundation.framework.

Next, expand the "Targets" group located in the sidebar and expand "CaryGrantQuotes". Drag Foundation.framework to the group labeled "Link Binary With Libraries".

Header files are important because they tell an application what methods they can access. Headers that will not be directly accessible can be marked as "Project" but any header files that can be imported by an application need to be set to "Public". Since our project has only one header file, we'll need to set it to "Public". Under the group labeled "Copy Headers", right-click on the header files, select "Set role" → "Public".

We'll need to modify our build settings so that the framework can be loaded properly by the enclosing application. Without getting into the gritty details of Xcode-specific environment details, the only ones you need to know about for getting this to work are @rpath and @loader_path. These will expand with the framework being loaded to point to the location on disk where the framework is located relative to the binary. Right-click on the CaryGrantQuotes item under "Targets" and select "Get Info". Adjust your settings to match those shown below.

Project settings

Your CaryGrantQuotes project should now match the image below.

Sidebar visual

Let's quickly build a Mac OSX application to embed our new framework in. We'll call it "MyProject" and all it will do is output a random quote to the debug console.

In the root of the newly-created MyProject directory, create a directory called "Frameworks" and open that directory using Finder. Tab back to the CaryGrantQuotes project in Xcode and build it.

Underneath the "Products" group, there should now be a "CaryGrantQuotes.framework" item listed. Right-click on that item and select "Reveal in Finder". Drag the newly-created framework into "MyProject/Frameworks" as shown in the image below.

Drag frameworks directory

We still have a few more tasks to perform before we can use this awesome new functionality. Under "MyProject", expand the "Frameworks" → "Linked Frameworks" groups. Right-click on "Linked Frameworks", select "Add" → "Existing Framework". This will take a few seconds to load as by default Mac OSX apps recursively search for all frameworks.

Since we are using our own framework, click on "Add Other…" on the bottom of the dialog, browse to the "MyProject/Frameworks" directory and select "CaryGrantQuotes.framework".

Next, expand "Targets" and right-click on "MyProject", select "Add" → "New Build Phase" → "New Copy Files Build Phase". Select "Frameworks" from the dropdown and close the dialog.

Now drag CaryGrantQuotes.framework from the "Linked Frameworks" group into this newly-created group which should be labeled "Copy Files". Then take this same group and drag it to a spot above "Link Binary With Libraries" (which should now contain the CaryGrantQuotes.framework item too).

MyProject sidebar visual

Nearly there! Now we need to let "MyProject" know where it can find this new framework. We do that by modifying the "Runtime search paths" value which will expand into @rpath when the binary is built.

MyProject rpath setting

Now all that remains is to add #import <CaryGrantQuotes/Quotes.h> to the top of MyProjectAppDelegate.m and we can use it how we see fit. To output a random quote to the debug console, stick this statement in applicationDidFinishLaunching:.

NSLog(@"Quote: %@", [Quotes randomQuote]);

Your debug console should now output a random Cary Grant movie quote, quite a useful feature. If, however, you receive the dreaded "Library not loaded; image not found" error, I suggest you get familiar with the command-line utility otool. Run the following command from the root of the "MyProjects" directory.

otool -l Frameworks/CaryGrantQuotes.framework/CaryGrantQuotes

The -l option shows you what libraries the framework is linked to as well as the path settings. The value of name under LC_ID_DYLIB should be @rpath/CaryGrantQuotes.framework/Versions/A/CaryGrantQuotes.

The entirety of the CaryGrantQuotes project can be found on github. Additional links and resources on embedding your own framework are listed below.