Easy, Modular Code Sharing Across iPhone Apps: Static Libraries and Cross-Project References

March 30, 2009

Finding an elegant way to reuse and share code (i.e., libraries) across separate iPhone applications can be a bit tricky at first, especially considering Apple’s restrictions on dynamic library linking and custom Frameworks. Most people agree that the best solution is to use static libraries. This tutorial builds on that solution, showing how your Xcode project can reference a second Xcode project–one which is used to build a static library. This allows you to automatically build that static library with the rest of your app, using your current build configuration (e.g., debug, release, etc.) and avoid pre-building several versions of the library separately (where each version was built for a specific environment/configuration).

Problem: What’s the best way to share code across iPhone projects?

If you want to reuse/share code across different iPhone applications, you only have two options (that I’m aware of):

  1. Copy all of the source code from the “shared” library into your own project
  2. Keep the shared library code in a separate Xcode project and use it to build static libraries (e.g., libSomeLibrary.a, also referred to as “archive files”) that can be referenced by your project and used via static linking.

The first option, copying the files, should be avoided when possible since it’s inherently redundant and contrary to the goal of keeping “common code” modular and atomic. It’s a much better idea to put the code in a static library since, as mentioned in the introduction, dynamic linking to custom libraries/frameworks isn’t allowed by Apple when it comes to iPhone apps. If you’re not sure how to put your code in a static library, I’d suggest taking a look at this tutorial on the Stormy Productions blog.

So we’ve established that the second option is preferable, but there’s a catch: you’ll need to build and distribute multiple versions of the static library–one for each runtime environment and build configuration. For example, you would need to build both “release” and “debug” versions of the library for the Simulator, as well as other pairs for the iPhone or iPod device itself. How can we avoid manually pre-building and managing separate .a files?

Solution: Static libraries that are built on-demand via Xcode cross-project references

The trick to avoid pre-building static libraries for each environment is to use an Xcode “cross-project reference” so that those libraries are built dynamically (i.e., when you build your own app) using your app’s current build configuration. This allows you to both reuse shared source code and avoid the headache of managing multiple versions of the library. Here’s how it works at a high level:

  1. The shared code lives in its own Xcode project that, when built, results in one or more static libraries.
  2. You create an Xcode environment variable with a path to the directory that contains the static library’s *.xcodeproj file.
  3. All iPhone apps that need the static library will use the aforementioned environment variable to reference the library’s Xcode project, including any static library in that project and the related header files.
  4. Each time you build your project for a specific configuration/runtime environment, the shared project library will also be built for that config/environment–if it hasn’t already–and linked with your executable.

In addition to solving the main problem (reusing code and avoiding management of multiple library versions), there are a couple of nice benefits to this strategy. First, if you make changes to the shared library source code, those changes will immediately be included the next time you build your own project (via the cross-project reference). Second, you can modify the Xcode environment variable to point to different versions of any project. For example, you might have separate directories for “somelibrary-1.0″ and “somelibrary-2.0″; as you’ll see in the detailed solution instructions, it’s easy to modify the environment variable and switch your project to a different version of “somelibrary.”

Other Solutions

zerg-xcode

Victor Costan has developed a slick command-line tool called “zerg-xcode” which helps you copy the source code from one Xcode project (i.e., a static library project) into another Xcode project. In addition to physically copying the files, it inserts the targets from the “library” project into your “app” project. If the library project changes, you simply run zerg-xcode again with the approriate commands to sync the files and targets. Some people may find this tool very useful; my personal preference, however, is to avoid making any copies of the source code files and stick to Xcode’s built-in “cross-project reference” mechanism.

“Fat” Universal Binary

Another approach is to “bundle” two versions of a static library into a single file, referred to as a “fat” universal binary (see this post on the Latenitesoft blog for an example). More specifically, one version of the library would be for the i386 architecture (i.e., the Simulator) and the second for the ARM architecture (i.e., the phone). This may be a perfectly fine solution for you if you really only need two versions, or if the source code for the library is kept private. That said, you’re still left with the task of maintaining pre-built versions of the libraries (plus the extra work of bundling them into the single file). In addition, I’m not sure that you can bundle more than two versions of the library into the binary (e.g., iPhone “release” and Simulator “release”, but not iPhone “debug” and Simulator “debug”).

How to Implement the Cross-Project Reference Solution

The instructions for setting up cross-project references to shared static libraries can be split into two parts:

  • Part 1: Global Xcode Settings
  • Part 2: Project-Specific Settings

Also, I’ll be using an example in the instructions to help illustrate things. A suitable example would be an application that needs to use a shared static library from a separate project. In this case, I’ll use a sample iPhone app called “Game Skeleton” (by Matt Sephton) that depends on a static library called libcocos2d.a (which is part of an open source project called Cocos2d-iphone).

Note: If it wasn’t already clear, cross-project referencing is a standard Xcode feature and is actually suggested by Apple in the official “Xcode Project Management Guide” documentation. You can certainly get some great bits of info from Apple’s guide, but as you’d expect, it’s a high-level document (hence my thinking that this tutorial could be helpful for others).

Part 1: Global Xcode Settings

The first step in getting your Xcode project to use cross-project referencing is to configure a couple of things that aren’t specific to any one project (i.e., global settings).

Set up a shared build output directory that will be shared by all Xcode projects.

Screenshot showing how the Xcode preference dialog and how to configure Xcode to use a global build output directory.

Screenshot showing how the Xcode preference dialog and how to configure Xcode to use a global build output directory.

  1. With Xcode open, select “Xcode > Preferences” from the menubar.
  2. Select the “Building” tab.
  3. Set “Place Build Projects in” to “Customized location” and specify the path to the common build directory you created.
  4. Set “Place Intermediate Build Files in” to “With build products.”

Why is this necessary?

A brief explanation of why this is necessary might be helpful for some people. When you build your app (i.e., Xcode project) Xcode generates one or more “products” (object files, libraries, etc.) in the project’s own build output directory, by default; it will then “look” inside this directory when it comes time to link everything together and make an executable, for example.

Once you start using cross-project references, you’ll essentially be building more than one project. However, Xcode will still only look in the immediate project’s build output directory for libraries. Apple therefore recommends using a shared build output directory for cross-project references (see the last paragraph in the “Referencing Other Projects” section of “Xcode Project Management Guide: Files in Projects”). This ensures that Xcode will always be able to find products from other projects builds.

Will a shared build output directory cause problems?

I’ve had some questions from folks about whether or not using a shared build output directory causes problems. While I’m certainly not an authority on building with Xcode, I can say that in four months of using this technique (with several projects and a few different shared libraries) I’ve not had any problems (such as a “debug” build resulting in a “release” version of your library being overwritten, etc.).

Apple’s Xcode documentations clearly states that “Within the build directory, Xcode maintains separate subdirectories for each build configuration defined by the project” (see the “Build Locations section of “Xcode Project Management Guide: Building Products”). For example, I have a custom logging library that is used by multiple iPhone and OS X apps. The OS X versions of the *.a file show up in “Release” and “Debug” sub-directories within the common build output folder, the simulator versions in “Release-iphonesimulator” and “Debug-iphonesimulator”, and finally the device versions in “Release-iphoneos” and “Debug-iphoneos.” In other words, none of the builds seem to be overwriting each other.

Add a “Source Tree” variable that Xcode can use to dynamically find the static library project.

“Source Tree settings” are basically Xcode environment variables that hold paths to directories on the file system; this allows us to make the cross-project references flexible and avoid hard-coded paths.

Screenshot showing the "Source Tree" settings tab within Xcode preferences.

Screenshot showing the "Source Tree" settings tab within Xcode preferences.

  1. Again, open the Xcode preferences.
  2. Select the “Source Trees” tab.
  3. Create a new Source Tree variable by clicking on the “+” button and filling in the columns. The screenshot above shows that we’re using “COCOS2D_SRC” for the cocos2d-iphone variable name and that it points to “/Users/clint/dev/cocos2d-iphone.googlecode.com/release-0.5.3″.Tip: avoid using special characters in the actual file path (i.e., stick to alphanumeric characters, underscores, and hyphens); this path will be used as a “Search Path” and Xcode seems to have problems with search paths that use characters like an ampersand (&).

Part 2: Project-Specific Settings

Once you’ve got Xcode configured to use a global build output directory and have a “Source Tree” variable pointing at your shared project, you’re ready to set up the cross-project reference, dependencies, etc.

Set Up the Cross-Project Reference, Header File Search Paths, and Static Library Linking

  1. Open your project in Xcode.
  2. In the “Groups & Files” pane of Xcode, select your project root and hit Option+Cmd+A (add to project).
  3. Find the Xcode project package for the project that contains the shared library. Using our example, we’ll select the Cocos2d-iphone Xcode project (cocos2d-port.xcodeproj):

    Screenshot showing a second Xcode project file being selected so that we can add a reference to it.

    Screenshot showing a second Xcode project file being selected so that we can add a reference to it.

  4. When the “Add to Project” dialog is displayed, use the same settings displayed in the screenshot below and click the “Add” button.
    Important: do NOT check the “Copy items” box.

    Screenshot showing which "add to project" options are selected when adding a cross-project reference in Xcode.

    Screenshot showing which "add to project" options are selected when adding a cross-project reference in Xcode.

  5. After you click the “Add” button the project will appear as a “sub-project.” In our Cocos2d-iphone example, it looks like this:

    Screenshot showing how the Cocos2d-iphone Xcode project appears as a "sub project" once it has been added to the main "Skeleton" project.

    Screenshot showing how the Cocos2d-iphone Xcode project appears as a "sub project" once it has been added to the main "Skeleton" project.

    Remember that you have not imported a physical copy of the second project–it’s a reference.

  6. When the cross-project reference appears select it and hit Cmd+i. Then change “Path Type” to be relative to the environment variable you set up in Part 1. In the example below, we’re using the COCOS2D_SRC variable:

    Screenshot showing the Xcode "Project Info" dialog for a project added via cross-project reference.

    Screenshot showing the Xcode "Project Info" dialog for a project added via cross-project reference.

Configure the Library Dependencies, Linking, and Header Files

  1. In the “Groups & Files” pane of Xcode, under “Targets”, select your main app target and hit Cmd+i. Then select general tab and add the static library(ies) your app needs from the shared project by clicking the “+” button under “Direct Dependencies”. In our example, we’ve added the “Chipmunk” and “cocos2d” libraries which are both built from the Cocos2d-iphone project:

    Screenshot showing an Xcode executable target being configured to depend on static libraries that are built from a cross-project reference.

    Screenshot showing an Xcode executable target being configured to depend on static libraries that are built from a cross-project reference.

  2. Click on the build tab and scroll down to the “search paths” section
  3. Important: If a hard-coded path to your shared project appears in the “Library Search Paths” field, delete it. This can be done by double-clicking the field and using the “-” button.
  4. Double-click on blank area next to “User header search paths”. Then click on the “+” button, check the recursive checkbox, and type in the Xcode environment variable that points to your shared project directory, surrounded by $(). The example screenshot below shows $(COCOS2D_SRC) being used:

    Screenshot showing how to configure the Xcode "User Header Search Paths" for a library that is being included via cross-project reference.

    Screenshot showing how to configure the Xcode "User Header Search Paths" for a library that is being included via cross-project reference.

  5. When you click OK and go back to the Build tab, the “user header search paths” text field should show an absolute path to your shared project directory. In our example, $(COCOS2D_SRC) expanded to the actual path and ends with “**” to show that the search will be recursive:

    Screenshot showing how an Xcode environment variable will be dynamically expanded to the actual, absolute path once entered using the $() notation.

    Screenshot showing how an Xcode environment variable will be dynamically expanded to the actual, absolute path once entered using the $() notation.

  6. Finally, click and drag the static libraries from underneath the cross-project reference to “Targets > {your target} > Link Binary with Libraries.” This ensures that that the .a files will be passed to the linker when you do the build. Here’s a sample screenshot from our example app:

    Screenshot showing how to link your executable to the static libraries via cross-project reference in Xcode.

    Screenshot showing how to link your executable to the static libraries via cross-project reference in Xcode.

Summary

To recap, if you need to share code across different iPhone projects I suggest 1) putting the shared code in its own “static library” Xcode project and 2) using a cross-project reference so that you can build the library with your own app as needed. The verboseness of this tutorial might give you the impression that setting this up is a lot of work; it’s not, really, especially if you do it more than once (which is likely, considering that the goal here is to share code across multiple projects). I’ve been using this approach for about four months with several projects and it’s definitely saved me a lot of time. Finally, there might be a better strategy that I’m not aware of; feedback and suggestions for alternate solutions are certainly welcome.

Clint Harris is an independent software consultant living in Brooklyn, New York. He can be contacted directly at ten.sirrahtnilc@tnilc.

{ 19 trackbacks }

warpedvisions.org :: Link: XCode Static Libraries and Cross-Project References
05.14.09 at 9:16 pm
Let’s Talk iPhone » Tic Tac Toe in Cocos2D
05.19.09 at 3:06 pm
LB Technology Services Blog » Blog Archive » Sharing code in iPhone applications
07.16.09 at 8:22 am
Cocos2D Game Engine
07.24.09 at 9:10 am
11 Cocos2D iPhone Game Engine Resources | iPhone Development Tutorials and Tips
07.24.09 at 12:48 pm
Confluence: AKQA Mobile
07.28.09 at 4:29 am
Confluence: AKQA Mobile
07.28.09 at 5:40 am
Running the Cocos2D examples
07.31.09 at 5:15 pm
Confluence: Brad Brooks
08.01.09 at 7:51 am
Let's Talk iPhone » Tic Tac Toe in Cocos2D
09.01.09 at 3:50 pm
Getting Started with the Cocos2D Game Engine
09.16.09 at 12:22 pm
Confluence: Petter Tiilikainen
10.23.09 at 4:00 am
cocos2d Part1 « myCodeStudio
12.06.09 at 9:40 pm
Samuel's Project Nash » Blog Archive » ????? iPhone ?? ??? 1 – Cocos2D for iPhone ??
01.07.10 at 10:03 pm
iPhone Development and Code Reuse « mattsoftware
01.09.10 at 5:56 am
links for 2010-02-02 | Visualrinse | Design and Development by Chad Udell
02.02.10 at 6:05 pm
In A Day Development » Blog Archive » Using another XCode Project as a Static Library
03.13.10 at 2:22 pm
Static libraries in Xcode « I am Tyler
04.12.10 at 1:45 am
How to setup Xcode to share code across iPhone projects « Kimptoc
06.17.10 at 1:59 am

{ 65 comments… read them below or add one }

Victor Costan 03.31.09 at 10:20 pm

Dear Chris,

Thank you so much for your post!

I wasn’t completely happy myself with copying files between projects, and I was researching using hardlinks or symlinks instead of copies.

That aside, I’m wondering how you would advise to apply your method in an environment where source code is under version control, and developers can check out the code at arbitrary locations on their filesystem.

Thanks!
Victor

Clint 03.31.09 at 10:59 pm

@Victor Costan: Thanks for the feedback! Good question regarding using this method in a team environment with source control. In a nutshell, it seems to work just fine–I’ve personally used this strategy on projects with more than one developer, where each developer checks out the code to an arbitrary filesystem location. Neither the “shared build output directory” nor “environment variable” settings (as described in “Part 1. Global Xcode Settings”) are stored in the *.xcodeproj package that usually goes into source control. Each developer will configure these settings for his/her own instance of Xcode.

In the example I use in the tutorial, the project.pbxproj file (i.e, the main Xcode project meta-data file inside the *.xcodeproj package) only contains references to the COCOS2D_SRC variable; the actual path to the directory where I’m keeping the cocos2d project doesn’t show up in the file. Similarly, my shared build output directory doesn’t appear in the project.pbxproj file, either. Those global settings are only stored on my machine and don’t go into source control.

Steve Johnson 04.02.09 at 3:37 pm

Hi Clint - thanks for the article. I also have a question relating to multiple developers with different checkout structures.

In our case, our common library root folder will always be a sibling of our parent folder. So:

Developer/svnroot/trunk/common/mylib is where the library project will be.
Developer/svnroot/trunk/apps/myapp is where the app project will be.

Couldn’t I specify “relative to project” for all of my path settings and not need to use the Source Paths or Environental Setting? This way, once a project is set up, the other developer only needs to check out at “trunk” and all is well??

I haven’t tried any of this, just trying to sort it out ahead of time.

Thanks,
Steve

Clint 04.02.09 at 4:02 pm

@Steve Johnson: I totally agree with you. In a scenario where the relative path between the “project A” and “project B” is constant on everyone’s machine (e.g., both projects are part of the same version control checkout) you should be fine with using the “relative to project” setting for the project reference (i.e., as opposed to “relative to SOMELIB_SRC”).

Thanks for pointing this out–could be really helpful to some folks.

Sean 04.06.09 at 8:59 pm

Perfect, Worked like a charm. I don’t have to keep flipping back and forth between builds it automatically builds the right library for my target. You are going to save me a lot of time.

Paul Solt 04.30.09 at 10:17 pm

Thank you, I’ve mostly worked with Visual Studio and was looking to do something like this in Xcode for the iPhone. Your guide is a good reference.

I’m using the setup with multiple targets that use different versions of a library and it’s nice to have everything build and link properly.

Scott D. Yelich 05.04.09 at 2:08 pm

Working my way through this… I’d also like to get to the point where just adding the libX.a to a project is as simple as shown in the facebook connect demo video. I understand here that we’re building the lib each time… so this is good for when that is needed.

Step 5:
When you click OK and go back to the general tab, the “use …
perhaps:
When you click OK and go back to the *build* tab, the “use …

ok, yup… build > clean all targets on the static shared lib xcode project and build clean on test project … build and go on test project built the static library (although I had to update the static lib xcode before the libX.a went from red to black) — I just switched from device to simulator — it seems both get built (even when just building the test app for the simulator).

Thanks!

Clint 05.04.09 at 2:34 pm

@Scott D. Yelich: Thanks for the feedback! I’ve updated the post with your proposed edit for Step #5 (good catch!).

Santthosh 05.06.09 at 12:06 am

Thank you very much, I was also not happy replicating code and this approach came in very handy. I actually used Keremk’s iPhone Static Library project template to build the static library, this reduced the trouble in setting up the common library.

ramzez 05.12.09 at 7:42 pm

Hi, is there a way to share Macros across the project as well?

Clint 05.15.09 at 10:03 am

@ramzez: Yes–you can share macros the same way you would in any other situation (e.g., by putting something like “#import file_with_macros.h” in your code). For example, I have an Xcode project for a custom logging tool I made; the project is built into a static library and there are also a few macros defined in some of the header files. All of my apps which reference the logging project (using the steps in the tutorial) can use those macros as long as I #import the correct header file. In my case, I use the logging macros everywhere so I #import the logging header in each project’s .pch file (i.e., every file in the project has access to the logging header and the macros it defines).

In other words, if you just want to share C macros there’s really no need to go use the approach described in the tutorial.

ramzez 05.15.09 at 10:11 am

@Clint, hmm, we thought about it however our code base is about 20 years old and modifying thousands of files to just include a header file with macros is not really a solution for us.

Max MacLeod 05.15.09 at 10:50 am

Thank you so much Clint. This has saved me days of frustration and pain without a doubt. You are the man!

Kyle Davis 05.17.09 at 3:08 pm

Wow, this is unbelievably useful. Thanks for this!

frankmail007 05.18.09 at 11:22 pm

Great article. But I have problems. Here is my description:

My project structure:

Projects—CompanyName—externalLib
—lib
—project 1
—project 2

First of all, I didn’t set source tree because I’m using relative path and I didn’t set the shared output path because I’ll set the build path to different companyName’s lib. I followed every other things in your article.

My problem is I still get the link error to link to my externalLib. The dependency check works and external lib is built inside the lib directory. But the funny thing is the library for example libexternal.a is always shown in red color. It looks like it has never been built. I think that’s why I got the linkage errors. But I don’t know what happen? Do you have any clue.

Thanks very much,

Frank

ramzez 05.20.09 at 2:33 pm

My biggest problem with Xcode is freezing and slowness. what i have is a project which has 39 static libraries defined as targets from on big source tree. I build for iphone. Each time i start Xcode or change between Simulator or Device it takes ages on Checking Dependencies stage and then starts to recompile all the libraries, although they are all built already.

If anyone knows a solution to this, i would be really grateful.

ramzez 05.20.09 at 2:35 pm

@frankmail007

try hitting Cmd+I and see what the path it things the red coloured library should be…

Max 06.01.09 at 5:50 am

Hi Clint,
got a question for you. Assume I have three static libraries, A, B, and C.

A depends on B, and B on C.

In xcode, assume I have project A open.

It seems I also need to have B and C as subprojects.

I would have hoped that I just need B. That it would know that B has a dependency on C.

Any thoughts?

Cheers,
Max

Max 06.01.09 at 9:15 am

OK I think I have figured this out. I need to point to both B and C.

By the way I have encountered a bizarre problem. When I mark the static library to include “recursive” in the user header search path, sometimes it doesn’t find it.

I mark that entry non-recursive, and it finds it ok.

Only seems to happen with one library.

Quite strange. Must be a good reason for it…

Nonnus 06.03.09 at 3:29 pm

hi,

awesome piece of info you got here !
i got lost for a while until i remembered i had spaces in some folder names on the shared build path so all worked seamlessly as soon as i unescaped them, i hope this tip is helpful for anyone in same situation

i am just missing having the header files available on the target app using the library,
what is the best way to have them available like the sdk framworks do ?

abraços

nonnus

Alexei 06.08.09 at 3:38 pm

I have tried this approach and while it builds just fine, I am running into a weird problem with some of my methods optimized out when the library is compiled. So, when I run the app, it throws an unrecognized selector exception.

Any ideas?

Victor Costan 06.08.09 at 3:47 pm

@Alexei: try adding -ObjC to “Other Linker Flags” in the Build options for the binary (not necessary for the library).

Alexei 06.08.09 at 3:56 pm

Yes, -ObjC worked. Thanks a lot!

David Roach 06.13.09 at 6:21 pm

I love this article, great job!

I have two such libraries that I have developed and this method is similar to that of Visual Studio.

My question to you is, is there a way that if you are doing adhoc development of a library at the same time you are developing an application, can you have both source trees appear in Xcode. Right now all you get is the lib reference.

Kind regards

David

Max 06.15.09 at 4:15 am

Hi David,
I reckon you could just import the other source. Then set up two different targets in your workspace. One to generate your static library and the other to generate your executable.

Iphone Architect 06.17.09 at 6:59 am

nice tutorial, thanks
i’ll try to implement Victor’s solution with zerg-xcode

Paul 06.17.09 at 5:07 pm

Thanks for the great tutorial!

One note from my own experience was that to use some of the Dynamic Binding capabilities of Objective-C (in particular the NSClassFromString method) it was necessary for me to include the -ObjC compile flag in the build settings of both targets.

I also had two questions: 1) What is the best way to share a CoreData project as a library between multiple projects?

2) Is there any way to share NIB’s between multiple iPhone projects?

Thanks again,

Paul

Paul Carter 07.01.09 at 2:20 pm

This is fantastic!

I would never have figured out how to do this without this tutorial.

Many many thanks

Paul.

Mathieu 07.18.09 at 7:59 am

Hi Clint! That’s an awesome tutorial, thanks an lot.
I’m just a beginner at iPhone / Xcode programming and I managed to implement all what you described.

However, I still have a problem I can’t figure out. It’s about headers importing… Well I try to import cocos2d.h in a .m file, but then the project won’t build (”no such file or directory”).
It seems that Xcode refuses to actually search into my user header path.

I tried #import and #include, and “cocos2d.h”.
I used the source tree variable (recursive) for “User header search path” and “Header search path” in project and target preferences, for all debug and release configurations…

Not working…

I tried to address the complete absolute path directly in the import and then it works, but all subsequent headers are then not found.

If I copy the header file in my classes path, it is found, but same error, all other subsequent headers are not found.

I verified the path I entered for COCOS2D_SRC and it fits with the actual path so I’m running out of solutions…

Well I’m becoming crazy now, so if someone can help!

Regards,

Mathieu

Derek Bolli 07.24.09 at 7:49 pm

Hi Clint,

Thanks for the great article, I have successfully repeated the steps above with the latest cocos2d-iphone-0.8-rc2. I didn’t need the shared build output directory step as I am happy to re-build the cocos2d project from time to time but the above will save me a great deal of time when updating my cocos2d-iphone projects when new releases are updated (which is often thanks to the pace at which cocos2d-iphone team is developing the product).

With regard to Mathieu’s question above, I had the same problem and it was due to spaces in the path in the COCOS2D_SRC source tree xcode preferences setting (as mentioned in the article in the source tree variable tip) which I solved by adding a relative path i.e. ../cocos2d-iphone-0.8-rc2 rather than /Path/to/my/dev/dir with spaces/cocos2d-iphone-0.8-rc2. Have a look in the build commands transcript area of xcode project Build Results and you’ll probably see a mangled path e.g. -iquote /Path/to/my/dev/dir -iquote with -iquote spaces/cocos2d-iphone-0.8-rc2. If your xcode project folder is in the same dir as cocos2d-iphone-0.8-rc2, you can use a relative path as above.

Thanks again for the great article :)

Regards,

Derek Bolli, Head Hacker,
Bolli World HQ Computing Facility,
North Sydney NSW 2060
Sydney, Australia email: dbolli at bigpond.net.au (home)

tonton 07.30.09 at 3:02 pm

that ’s a great job , thanks for all your job
fyi i had some problems with the intermediate build path (xcode global settings) then i put the same absolute path (= build product location )

when using that config i dont need step 6 (Finally, click and drag the static libraries)

is it a better way or i missed sthg?

Derek Bolli 08.07.09 at 11:38 pm

Hi Clint, just an update to say that your tutorial saved me more than an hour today updating my cocos2d xcode projects from v0.8-rc2 to v0.8. All I had to do (after I’d downloaded and expanded cocos2d v0.8 into the same parent folder as my xcode project folders) is:
Edit Xcode->Preferences->Source Trees->Path from ../cocos2d-iphone-0.8-rc2 to ../cocos2d-iphone-0.8
Delete linked project cocos2d-iphone-0.8-rc2.xcodeproj
Project->Add to Project cocos2d-iphone-0.8.xcodeproj from ../cocos2d-iphone-0.8
Add cocos2d Direct dependencies in Target->General tab
Drag static libs from under cocos2d-iphone-0.8-rc2.xcodeproj to Target->Link binary with Library
Clean All
Build and Go

Great work, thanks again :)

Derek Bolli, Head Hacker,
Bolli World HQ Computing Facility,
North Sydney NSW 2060
Sydney, Australia email: dbolli at bigpond.net.au (home)

Jayant 08.16.09 at 3:52 pm

Wondering if anyone has had success getting Categories to work. So I have ‘MyStaticLib’ (Cocoa Touch Static Library) project which is referenced in ‘UsingMyStaticLib’ (View-based application) project, using the steps in this post.

MyStaticLib: TestClass & NSObject+MyCategory with (void)time & (void)printTime respectively.

In ‘UsingMyStaticLib’ I reference ‘MyStaticLib’ and reference the 2 header files. My test code:

TestClass *test = [[TestClass alloc] init];

if ([test respondsToSelector:@selector(printTime)]) {
NSLog(@”ok we’re in business!”);
[test printTime];
}
else {
NSLog(@”sorry, no go!”);
[test time];
}

[test release];

The code above always goes to the ‘else’ block and prints the time using the TestClass’s own selector. If I remove the conditional block and call the printTime method anyways, the app crashes.


UsingMyStaticLib[959:20b] *** -[TestClass printTime]: unrecognized selector sent to instance 0xd17570
UsingMyStaticLib[959:20b] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘*** -[TestClass printTime]: unrecognized selector sent to instance 0xd17570′

Any ideas on what I might be doing wrong? Please help!

Jayant 08.17.09 at 12:13 pm

The -ObjC option in ‘other linker flags’ solved the issue, thanks to ‘Victor Costan’ for that. And thanks to Chris for this awesome article. Cheers.

SEQOY 09.02.09 at 8:06 am

One nice tip: If when you import the .xcode project to your own project.

All the links are broked (red). Probably you forget to build all the libraries on the project your are importing.

Work for us. All the best.

Brad Parks 09.02.09 at 6:10 pm

For anyone who’s interested in implementing this with Cocos2d, a user on the cocos2d forums (thanks tjweir!) put together a nice project template with this already integrated

http://github.com/tjweir/cocos2d-application/tree/master

Brad

PS: The thread I found it in is here:

http://www.cocos2d-iphone.org/forum/topic/213#post-1155

ramzez 09.08.09 at 3:29 pm

how to share pre-processor definitions (macros) across different projects? i.e. if i want to setup pre-processors only in root project but so that they are picked up by static libraries projects?

BG 09.20.09 at 6:56 pm

Very helpful!

FYI: While doing this, I had a weird issue with a missing symbol (___restore_vfp_d8_d15_regs) that I resolved by compiling the library with GCC 4.0 instead of GCC 4.2. This was on Xcode 3.2, OSX 10.6, iPhone OS 3.1.

Michael 10.04.09 at 9:19 pm

I tried using this method with the facebook-connect iphone library but am having some issues. What I’m doing is simply opening a facebook session and attempting to display the login dialog. This works fine when I import the facebook source code directly into the project. With the project-reference approach, everything compiles and links fine. But when I try to run, no dialog. Any idea what might be going on? Thanks for the excellent article.

Evgen Bodunov 10.19.09 at 9:29 am

There is more simple way to do that:
1. add reference to xcodeproj file in your project (in Frameworks for example)
2. double-click on target and open general tab
3. add library bundle target in direct dependencies list.
4. drag and drop libLibName.a from xcodeproj reference (in you Frameworks) to “Link Binary with Libraries” folder of your target.

that’s all.

Evgen Bodunov 10.20.09 at 3:19 am

oh. i miss step 5.
5. in project configuration add path to your library headers into variable “user headers path”

Tony John 11.12.09 at 5:43 am

Really helpful.

Is there any way to set break points in side the static library project so that we can debug it. What happens when the project gets built, first it builds the library and makes .a file out of it. So XCode ignores the break points. How do we debug the cross referenced project?

Victor Costan 12.06.09 at 6:15 am

I figured out how to build static libraries that package the simulator and device code into a single file. This can come in very handy when distributing your static libraries. Read my blog post for details.

Victor Costan 12.06.09 at 6:18 am

@Tony: that’s one of the reasons why I include the library source code in my application projects. You can read about my method in this blog post.

chief 12.09.09 at 3:41 pm

Just wanted to thank you for this article. I hit a few snags, but worked through them. If your Source Tree’s path includes spaces you have to quote it (”Users/chief/iPhone Projects/test”). Otherwise this worked like a charm. Thanks again for sharing!

Martin Kovachev 12.09.09 at 3:48 pm

We are using pretty much the same type of technique over here to handle a shared part of a mac / iphone application - all in a single static library.

So far it was running ok, but recently we’ve moved to Snow and the new SDK’s and we found the need to use Universal binaries to ship 32/64 binary in a single package.

What we did is select universal binary project option in both the static library - and the project that uses it itself.

Both the project and static library have two profiles - Release / Debug (I am assuming that when i build the project - the static library is somehow - magically switching to the same profile as the project itself, because there are no options to select this within the project - for a specific static library project attached to it).

The problem is that when building starts - we get that static library blablabla - is of an incompatible architecture.. duh? Wasn’t it supposed to build all necessary linking objects for an universal binary and figure out a way to link them?

Anyone having the same problem?

How do you build an universal binary with static libs attached to the project?

Quentin 02.03.10 at 3:10 pm

Does anyone have it working with Xcode 3.2 ? ’cause i remember i had it working months ago, but not anymore.

Derek Bolli 02.04.10 at 3:15 am

@Quentin
I updated my linked Xcode project with cocos2d-9.0-beta2 and it compiles using Xcode 3.2.2 with the iPhone 3.2 iPad beta

Quentin 02.04.10 at 5:28 am

Hello,

Ok, wrong try from me. I think i had something “broken” in my project paths, because i created a new project and it works now.

Thank you for your quick reactions guys.

Kenny Wyland 03.13.10 at 2:15 pm

I want to have your babies. I’ve tried a couple of other tutorials on how to get this working, but they’ve never worked for me. Not only is this one working, but the instructions were far more simple than the other sites!

Thank you!
Kenny

Derek Knight 04.09.10 at 11:50 pm

Great tutorial. I’ve been using XTool for two weeks now and am having a lot of minor problems (I have used Visual C for 20 years!). Tutorials like your, which are so clear and easy to follow and a god-send.

Thanks a lot
Derek

Derek Knight 04.09.10 at 11:52 pm

Twit, Derek. You can probably figure out what I meant. “Tutorials like yours, which are so clear and easy to follow are a god-send”. Doh!

Pixels 4 Charities 04.30.10 at 5:21 pm

Hi, I can’t quite seem to figure out how to include and access a function in the static library. I followed the steps perfectly and the application builds fine (without any calls to the library).

But now I want to actually call something and it says “TestLib.h: No such file or directory”

I tried #import, #include, , “clsTestLib.h” and every combination. I even added the the -ObjC and -all_load Linker flags to the Project BUILD settings and no luck.

Any ideas?

Pixels 4 Charities 05.03.10 at 4:33 pm

I had a user error. The tutorial is awesome but I guess I did something incorrectly. After no luck, I decided to create a new project and re-follow the steps. This time is worked out great. FYI for others who might not know, this should work with:

#include “clsStaticLib.h”

if you followed the steps correctly.

Thanks again!

Dean P 05.07.10 at 1:09 pm

First off I’d like to join everyone else in commending and thanking Clint for producing a well written and concise article that has helped us all greatly.

Next I’d like to pose a question, to both Clint and others, regarding the role of headers. From my understanding there are 3 options, Project, Private and Public. The first ‘does as it says on the tin’ making a header only accessible to the project code (that within the static library) therefore omitting it from being copied over in the build process. The result is that the project (our app) that uses the static library won’t have access to them. The second two options both copy over the header(s) and only differentiate by where they are copied to using the build settings PRIVATE_HEADERS_FOLDER_PATH and PUBLIC_HEADERS_FOLDER_PATH respectively. The Apple documentation on the matter seems to suggest this is a way of suggesting those headers ready for end user use and those currently in development that should perhaps be restricted to either ourselves or our direct collaborators.

Having the followed the tutorial with great success I gather using the Xcode environment variable and the recursive option will provide us access to both ‘types’ of headers unless we pick to place either of them in an obscure location outside of the path covered by the environment variable (bad idea).

So what, if any, strategies do people use to exploit some of the functionality we could gain from differentiating between the private and public headers of our static library? For instance is it possible to leverage this in such a way that only certain parts of our code using the static library have access to the private headers? Could this be achieved with a more descriptive (but less enforced) paradigm by which including a header requires specifying the final folder of the path. e.g.

#include “private/foo.h”
#include “public/bar.h”

Therefore any file/code/object in our app *could* use a private header, but we would be aware we were doing so at the point of import.

Thoughts fellow iPhone-ians?

Dean P 05.07.10 at 2:58 pm

As an addendum, it seems that regardless of the role set for a header all headers remain accessible to the code that uses the static library. For instance using the procedure outlined by Clint, try adding to the static library three headers, each with a simple macro, each marked as having one of the three different roles (project,public,private). e.g.:

myPrivateHeader.h (role is private)
… containing …
#define myPrivateMacro(i) (i + 1);

My understanding was that code outside of the project would only have access to private and public marked headers and only then based on how the header search path aligned with the relevant HEADERS_FOLDER_PATH. Project marked headers remained accessible only to the project in which they’re declared (i.e. the static library project). However from my app, I can quite happily execute the macro declared in the header marked with role ‘project’ in my static library:

myProjectMacro(10)

Is this because using a cross-project reference makes the ‘project’ the one using the static library, not the one containing the it?

MS 05.13.10 at 6:50 pm

This is a great guide, thanks. But one problem I noticed after setting up a shared project directory as suggested has to do with my unit testing targets.

I have a static library project with a unit testing target that is run whenever the project builds.

I have an application project that has its own unit tests and a cross-project reference to the static library project.

With a shared build directory, when I build my application project, the static library’s unit testing target actually runs the unit tests from the application project, which results in the application project’s unit tests being run twice, and the static library tests are never run.

For now I’m using separate build directories and have not seen any problems (though I’m only coding and compiling at this point, not running anything in the simulator yet, or doing any release builds). So I’m curious to know when I’ll have the problem that Xcode won’t “be able to find products from other projects builds.”

Mike 05.18.10 at 5:07 am

I just wanted to say thanks! I didn’t want to use their template but was having issues using the cocos2d in my existing project and this helped greatly.

GamingHorror 05.19.10 at 6:09 pm

I made a cocos2d Xcode Project Template partially based on this article and of course using the latest cocos2d version (0.99+). Then i added a ton of other features (various build configs, IPA building, proper debug stripping, targets for debugging memory crashes, etc). All the things i need for my professional Xcode project work on a daily basis.

The result is a 100+ page cocos2d Xcode Project setup Tutorial:
http://www.learn-cocos2d.com/knowledge-base/tutorial-professional-cocos2d-xcode-project-template/

I hope you don’t mind the shameless plug.

B2 06.22.10 at 5:49 pm

Nice article, and I went through the same issues about the same time you did. Here is the problem I’ve just discovered.

I am using xcodebuild. My app has PRODUCT_NAME set to a user-defined build setting, MY_NAME.
Its target has a project reference to the project that builds my static lib ORIG . When xcodebuild is launched with:
xcodebuild -configuration … MY_NAME=foo
it looks for libMY_NAME.a instead of libORIG.a, doesn’t find it and rebuilds the static lib as libMY_NAME.a

This is happening because PRODUCT_NAME is being used by the static lib target to build its output name and it is getting overridden.

Any idea about how to stop this from happening? I’m losing all the advantages of a static lib just because I want to vary my app name at build time.

Michael 07.04.10 at 7:51 am

Fantastic article. Thank you so much for taking the time to write and share this information. Until now I’ve never been very aware of what static libraries are and how to properly integrate them.

Joe Falo 07.10.10 at 4:43 pm

Great tutorial, Clint. It worked initially but then I ran into the same problem that Mathieu ran into. This especially became a problem when I have a static library that uses another static library (both of my own making). I read somewhere else about the solution being to change the name of the project directory. This also worked initially but at some point, adding files, etc. it gets stuck… “No such file or directory”. You change the name of the library’s directory and set the Source Trees environment variable to match, it works. Only to break later when adding more files to the project. And curious enough, once it breaks on a directory name, you can’t use it again: you have to pick something new. I know it sounds crazy. I can only think that there is something in the project that gets corrupted. I am following instructions to the letter. I’d love to use this technique instead of copying files around. If anyone has a clue about this, please let me know. P.S. I’m fairly new to Apple computers and iPhone development.

lee 07.28.10 at 5:32 am

Hi,
I see that the article focus on sharing code between iphone apps, is there a way to do this on projects between iphone apps in which the same source code will also be used in a custom command line project in cocoa OSx?

MarqueIV 08.01.10 at 2:57 pm

Ok… I’ve got a tough one. The app compiles just fine and runs just fine… until I try to load a XIB file that references one of the UI classes in the static library. This class exists in the XIB only and doesn’t have any outlets in the code file itself. (Even so, I manually added an outlet just in case to see if perhaps the class was getting optimized out.) However, no matter what I do, the XIB file will not load saying it can’t find the class.

I’ve tried the -objC linker flag, I’ve tried every other thing I’ve found here in all of the comments, but nothing.

On a side note, what I ended up doing was just adding (not copying, but adding a reference to) a folder containing all of the static library’s source files themselves (actually, at this point, it’s not technically a project anymore but just a ’shared code’ folder with .h, .m and .mm files, plus a few resources in it and of course a main ’someLib.h’ file that pulls in all the other headers), then just setting the target membership for the ones that I’m actually using in the product. That worked first time and also doesn’t create copies of the source files, plus it has the added benefit of not having extra code added to the main application from the static library that isn’t actually used by the application. My static library had an ‘a’ file of 450KB but the compiled app when doing it this way was only 87KB. That’s a huge savings. (Granted I don’t know how ‘bloated’ the .a files are, but that’s still significant if you ask me.)

Another benefit is you just edit the ‘library’s’ files right in the one project. You don’t need a second instance of an Xcode window open. And finally, there’s only the build path of the executable itself.

Of course this approach has one major downside of you manually having to select each and every file you *do* want to use while excluding those you don’t, but at least this ‘library’ only has about 30-40 files in it so it isn’t that bad. I do however see the appeal of Clint’s approach for much larger projects.

Another tip, in the project tree, right-click on the headers and just choose ‘Target Membership’ You’ll get a nice list of checkboxes on the left that make it very easy to select what gets compiled and what doesn’t. It also lets you select target membership for the .a files so you don’t need to drag them to the target’s ‘linker’ phase. It’s automatically added there.

But back to the original point. Anyone know why my XIB wouldn’t load the class from the static library in the first place?

Louise22PARKS 08.28.10 at 12:16 am

Buildings are not very cheap and not everybody can buy it. Nevertheless, home loans are created to support people in such kind of situations.

Leave a Comment

You can use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>