Although I’m not new to Mac development I never needed to embed a dynamic library (.dylib file) into an app until just recently. Needless to say I struggled with this for a while.
I also wasn’t able to locate any single, up to date and reputable tutorial on the topic so I wanted to collect the bits and pieces I found into this single, easy to understand article so I could help others.
The strategy that worked for me, and which I’ll cover below, was to create a post-build script.
Understanding Dynamic Libraries
Dynamic libraries have encoded within them a “name” which actually serves as a path by which they are located. In order for your already built app to reference a dynamic library it needs to know where to find it and during the build process this encoded name becomes that location. When your app starts up it attempts to load all its dependencies at their named paths and unless said dynamic library is installed on the user’s Mac the app will crash.
Of course you can never be 100% certain a user will have the dynamic library beforehand and installed at just the right location. It therefore makes more sense to bundle the library within your app.
Determining the Encoded Name
In your app’s main target create a post-build script and enter the commands below, replacing MyProject with the name of your project and the “ThirdParty/Abc/” with the path of the .dylib file relative to your project’s base path.
echo OTOOL BEGIN\n
otool -L MyProject/ThirdParty/Abc/Abc.dylib\n
echo OTOOL END
Now build your project and inspect the build log (Right click on any build issue and select the “Reveal in Log” menu item). The output from otool’s execution should appear below “OTOOL BEGIN” and above “OTOOL END”.
Did it work? Okay, now remove these 3 lines from your build script but keep the dynamic library’s name handy for the last step.
Overriding the Dynamic Library’s Encoded Name
The below command overrides the dynamic library’s name to one that will match its path within your app’s folder (remember an OS X app is really an .app folder, not a file). Typically libraries appear under the “Frameworks” folder. NOTE: Make sure you have a “Copy Files” build phase which will copy the file from its source to this “Frameworks” folder by specifying it as the Destination.
install_name_tool -id @executable_path/../Frameworks/Abc.dylib MyProject/ThirdParty/Abc/Abc.dylib
NOTE: The executable path variable is the folder in which the true binary executable is located. Typically in the PRODUCT_NAME/Contents/MacOS folder. So the path @executable_path/../Frameworks/ is really PRODUCT_NAME.app/Contents/Frameworks.
In the above command replace Abc.dylib with your dynamic library’s filename.
Also replace MyProject with the name of your project.
In this example the source folder for the Abc.dylib is under “ThirdParty/Abc” and you’ll need to provide an appropriate path to your dylib file, following your project’s conventions.
Now that the dynamic library’s new encoded name matches the new location in the “Frameworks” folder the next step is to tell your app to expect it there.
Remember this is a post build step. Your app was originally linked against the not yet renamed dynamic library and thinks it needs to look for it there still. This is where we use the encoded name otool reported to us…
Modifying the App’s Reference to the Dynamic Library
The below command changes the referred location of the dynamic library from its original path to its new one within the “Frameworks” folder. Add it to the bottom of your script.
install_name_tool -change usr/lib/Abc.dylib @executable_path/../Frameworks/Abc.dylib MyProject/ThirdParty/Abc/Abc.dylib
As with the previous step, modify the paths to reflect those of your project and take special careful to type all paths accurately. The above “usr/lib” path may differ from that of your dylib and this is why you used otool. Take the path you discovered with that tool and if different from “usr/lib” replace it.
Run the build once more. If no errors occur and the dylib file is indeed bundled in the “Frameworks” folder (you can check by stripping the .app extension from the folder) there is one last thing to test. Running the app!
Archive the project (Xcode menu Product → Archive). This will open the Organizer window. From here you can press the Distribute button which will prompt you for the method of distribution.
Choose “Export as Application” and export it to your desktop. The app is now waiting for you there and you only need to double click it. If the app starts up without complaint you’ve successfully bundled the dynamic library!
If the app doesn’t execute, check the error report OS X offers in the crash dialog to see what file path it expected to use. You can also reuse the otool command shown in the first step at the bottom of your build script to double check that the dynamic library was indeed renamed. Most errors end up being mistyped paths and otool can prove so.