Ever been confused about how to effectively compile and link third-party libraries that you may have cloned from github, bitbucket, et al? This post post describes the process of setting up your Xcode4 environment to clone, compile and link third-party libraries.
Third-party libraries that are hosted on github, bitbucket, etc typically offer varying degrees of documentation as to how they should be linked in but, in general, they all follow the same basic sequence of steps.
This post describes the approach that I have been using to link third-party libraries that I have cloned from git.
Overview
I tend to make good use of the new workspace functionality of Xcode4. For me, it allows me to group my related projects while still retaining the logical separation of each of the projects.
This post describes creating a new workspace and adding an out-of-the-box project from one of the Xcode4 templates. It then clones an arbitrary project from github and links it into our application.
Sounds fairly easy… let’s get into it.
Setting Up the Workspace
Create a new workspace:
And add a project from the list of Xcode4-supplied iPhone templates:
Now, you should be able to compile and run this project. Nothing very exciting yet.
Cloning Third-Party Project
For this example, I chose to clone a JSON library (for no other reason than it was the first one to appear in google).
Open the Git Repositories view by using the File/Source Control/Repositories… menu item and add a new cloned repository:
and enter the URL for your remote library:
and the local name for it (I usually try to use the same name as the remote repository):
and clone:
Note that cloning does not add the project to your workspace. You have to manually add it. After cloning you will be presented with the following dialog – choose Show in Finder and we will drag the project into our workspace.
Drag the SBJson.xcodeproj
file into your Xcode4 workspace. Be careful here… you want to drag the project so that it is a peer of your TestApp
project, not a subordinate.
Your workspace should now look like the following:
If it looks like the following, delete it and drag it a little further left before dropping:
Verifying Cloned Library
Now that we have successfully cloned the third-party library into our workspace, we should firstly make sure it compiles by itself.
Switch schemes to the newly added project and confirm that the library compiles OK (note that very occasionally Xcode4 doesn’t add the new schemes into the list – I have no idea why, but usually just closing Xcode4 and starting again seems to fix it.)
Adding Third-Party Library as a Dependency
We have the third-party library in our workspace, but the TestApp
project still doesn’t know about it yet. Fortunately, Xcode4 supports the notion of dependent projects, which is what we are going to use next.
Drag the blue heading bar of the third-party library onto the blue heading bar of the TestApp
application:
and create a group for the added project:
Your workspace should now look like:
You’ve added a project dependency within the workspace, but we still haven’t told your target that it is dependent on the third-party library. Click on your app’s target, select the Build Phases tab, and add a Target Dependency:
While you’re on this tab, add an entry to the Link Binary With Libraries section to make sure the library is linked in at build-time:
The nice thing about this dependency being set up is that if you make any changes to the sub-project, Xcode4 is smart enough to recompile the library and re-link it into your app.
Using the Library
Switch back to the TestApp
scheme and perform a build. You should find that everything compiles OK at this point. Now it is time to add some code that uses the third-party library.
In this case, we are just going to add a single line of code (with an import). Open up the AppDelegate.m
file and add the following statement:
#import <SBJson/SBJson.h>
Now, add a line at the start of the application:didFinishLaunchingWithOptions:
method:
NSLog(@"JSON Test: %@", [[NSDictionary dictionary] JSONRepresentation]);
If everything has been set up correctly, you should find that Xcode4′s content assist should be able to offer assistance for the library’s methods.
Compile and run.
Uh-oh… What will likely happen is that it will start up, but bomb out with an error message like:
2012-02-27 15:31:19.108 TestApp[95762:f803] -[__NSCFDictionary JSONRepresentation]: unrecognized selector sent to instance 0x6841c70 2012-02-27 15:31:19.132 TestApp[95762:f803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFDictionary JSONRepresentation]: unrecognized selector sent to instance 0x6841c70'
The reason for this is that we need to include one more linker setting to tell the compiler to pick up the newly added objective-c methods. Apple has written an article that describes the reasoning behind this. To resolve, open up the Build Settings for the TestApp
target, search for Other Linker Flags and add the following values:
Compile and run again, and you should see output like:
2012-02-27 15:37:47.226 TestApp[96063:f803] JSON Test: {}
And that is it! We’re done.
Footnote: Header File Management
You may be wondering how Xcode4 found the SBJson.h
file that we imported. I don’t claim to be an expert here, but my research (and some trial/error) indicates that there are several factors at play here:
- When using an Xcode4 workspace, the build artefacts all get built into a ‘Derived Data’ directory. This physical location for this directory is defined in the Location panel of the Xcode4 settings dialog. On my machine, this is
~/Library/Developer/Xcode/DerivedData
. Underneath this directory, each workspace gets its own unique directory. In my case, it isLinkingThirdPartiesExample-bvzijybaagkayohgfmjlxcaezgck
- Within this directory, there is a Build directory that contains the build-time artefacts for the various platforms (iPhone simulator, iPhone device, Mac, et al).
- Xcode4 is pre-defined to look in the
include
directory for header files. If they are there, they will automatically be picked up. - Any
.h
in your workspace that are marked as Public are published underneath theBuild
directory. Well-structured libraries (such as
SBJson
) mark their headers as public and use the Public Headers Folder Path build setting in such a way so that consumers of their library don’t have to modify their include paths.- Not-so-well-structured libraries may not mark their headers as public, or have non-standard entries for their Public Headers Folder Path, which will force you to modify your app’s build path to include those files explicitly.