Using Frameworks Within NSBundles
Or more specifically: “Using frameworks that use other frameworks within NSBundles”.
For a project that I’ve been quietly working on for some time now, I’ve been using the Connection Kit framework. Initially I had issues getting my NSBundle to load the framework properly.
I’ve used frameworks within NSBundles in previous projects, like Code Complete. As the framework I used there did not itself require a framework, and I had its source, I could simply change the install name to @loader_path/../Frameworks in Xcode.
I wanted to use Connection Kit for my current project, but ran into issues as it requires a few other frameworks to function. Many months ago Stack Overflow helped me work around this by suggesting I copy the internal framework’s source files directly into Connection Kit.
Everything went well until I updated Connection Kit. Suddenly it required more internal frameworks, and copying their source became impractical.
Into the breach
During my previous attempts to make Connection Kit work with my bundle I came across this post: Embedding frameworks in loadable bundles, but wasn’t patient enough to make it work.
This time I obviously had no other choice, so put some time into reading the man pages for both install_name_tool & otool. The former allows one to change dynamic shared library install names, the latter to view them (among other things).
Ultimately, the solution was to change the Connection Kit framework’s name and to change the internal names of each framework it required with:
install_name_tool -id executable install_name_tool -change old_name new_name executable
Just give me the details
The exact commands I used were:
install_name_tool -id \ "@loader_path/../Frameworks/Connection.framework/Versions/A/Connection" \ "$SRCROOT/Frameworks/Connection.framework/Versions/A/Connection"
This change was required to allow my bundle to load the framework correctly.
To ensure Connection Kit could load its internal frameworks, I also needed to perform:
# Update internal framework names install_name_tool -change "@executable_path/../Frameworks/CURLHandle.framework/Versions/A/CURLHandle" \ "@loader_path/../../../CURLHandle.framework/Versions/A/CURLHandle" \ $CONNECTION install_name_tool -change "@executable_path/../Frameworks/DAVKit.framework/Versions/A/DAVKit" \ "@loader_path/../../../DAVKit.framework/Versions/A/DAVKit" \ $CONNECTION otool -L $CONNECTION # Fix DAVKit name install_name_tool -id \ "@loader_path/../Frameworks/DAVKit.framework/Versions/A/DAVKit" \ "$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/DAVKit.framework/Versions/A/DAVKit" otool -D "$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/DAVKit.framework/Versions/A/DAVKit" # Fix CURLHandle name install_name_tool -id \ "@loader_path/../Frameworks/CURLHandle.framework/Versions/A/CURLHandle" \ "$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/CURLHandle.framework/Versions/A/CURLHandle" otool -D "$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/CURLHandle.framework/Versions/A/CURLHandle"
This updated Connection Kit’s internal names for the two frameworks, allowing it to load them successfully. I also updated the internal frameworks’ names here.
While I was working this out, I made heavy use of otool, which allows one to see the name & install paths for a given binary, for example, before performing the above on Connection Kit & internal frameworks (irrelevant lines removed):
otool -D Connection.framework/Versions/A/Connection Connection.framework/Versions/A/Connection: @executable_path/../Frameworks/Connection.framework/Versions/A/Connection otool -L Connection.framework/Versions/A/Connection Connection.framework/Versions/A/Connection: @executable_path/../Frameworks/Connection.framework/Versions/A/Connection (compatibility version 1.0.0, current version 1.0.0) @executable_path/../Frameworks/CURLHandle.framework/Versions/A/CURLHandle (compatibility version 1.0.0, current version 1.0.0) @executable_path/../Frameworks/DAVKit.framework/Versions/A/DAVKit (compatibility version 1.0.0, current version 1.0.0)
And after:
otool -D Connection.framework/Versions/A/Connection Connection.framework/Versions/A/Connection: @loader_path/../../Contents/Frameworks/Connection.framework/Versions/A/Connection otool -L Connection.framework/Versions/A/Connection Connection.framework/Versions/A/Connection: @loader_path/../../Contents/Frameworks/Connection.framework/Versions/A/Connection (compatibility version 1.0.0, current version 1.0.0) @loader_path/../../../CURLHandle.framework/Versions/A/CURLHandle (compatibility version 1.0.0, current version 1.0.0) @loader_path/../../../DAVKit.framework/Versions/A/DAVKit (compatibility version 1.0.0, current version 1.0.0)
I put all of this into a “Run Script” build phase -
After compiling & running the parent application, my bundle loaded perfectly.
I learned a lot about OS X development during this process. Have you had to do anything similar to convince your projects to run?




It’s funny cause it’s true!
Using Frameworks Within NSBundles http://t.co/WzcqWaF2
Exciting stuff.
Great to see you both enjoyed it!