February 1, 2010

Static Libraries in iPhone Projects (Part 1)

One of the conclusions I have come to in my year of iPhone programming is this: in order to be successful in the iPhone market, a lone developer has to take advantage of code reuse as much as possible. We part-timer coders don't have time to reinvent the wheel for every project.

This is why Osmorphis has released a few libraries (frameworks) of reusable, general-use functionality. Things like the BackgroundTask class, the JESON package, plus a few others I have not released to the public (I gotta protect some of my "secret" weapons ;-) are all designed to be dropped into a project and used.

At first, this dropping of code, literally copying the code, worked fine. But I knew this was not a maintainable way to manage code, especially when the number of projects using these packages increased.

I finally bit the bullet and figured out how to maintain these packages in separate projects, build a static library, and reference them from other projects. This allows me to maintain the source code in one project. The product (the library) is referenced from other projects instead of the source code being copied in. This prevents several versions of the same code scattered throughout your projects.

(Note that these must be used as static libraries for iPhone development; Apple does not allow custom "frameworks" of shared libraries.)

In this two-part article, I will share how I do it. I feel this is a fairly cumbersome task, and I am constantly in awe with how both powerful and clunky Xcode is. There may be easier/better ways to do this. If you know how, please comment. I would love to simplify my process.

If you want to get to the summary of steps, jump to the bottom.

Part 1. Creating a Static Library Project


This first part discusses how to create a static library project. I will run through the steps of converting my BackgroundTask project to a proper static library. This project will contain the library itself plus a small test app.

In the part 2, I discuss how to reference this library project from another project in order to use it.

Create a New Project


From the menu in Xcode, select File > New Project...

Up comes a dialog that looks something like this:

You want to select iPhone OS/Application, and then select "Window-based Application". Hit the "Choose..." button, then name and locate your project where ever you see fit.

For my example, I created a new directory called backgroundtask and named the project the same, backgroundtask.

Your fresh project will look something like this:

Adding the Library Target

The project we've created so far is for a window-based application, so it is not set up for creating a library. But that's ok; we can use the application as our test app. I'll get to this later. For now, we need to create a new target for our static library.

Right click on the "Targets" element in the Groups & Files tree, and select Add > New Target...


Another dialog will come that looks something like this (your icon for static library may look like a red bullseye):

You want to select iPhone OS/Cocoa Touch, then select the "Static Library" target. Hit "next".

Enter the name of your library target. You probably want to use the "lib" prefix. For my example, I entered libbackgroundtask.

After you enter the name, you will be presented with a dialog of properties for this project:

Cleaning up the product name


Xcode will actually call your product "lib" + whatever you entered as the name. Which means my product name will be "liblibbackgroundtask". You can see this in the red entries under Products in the image below. I like the "lib" prefix as part of my target, but I want to clean up the product name to remove the extra "lib".

To do that, we need to make some changes on the properties dialog. If you already closed this, you can always bring it back up by selecting in the Groups & Files tree Targets > libwhatever > Get Info.


This will bring up the properties dialog again. Select the "Build" tab. The property we need to change is the Product Name property. You can find it by typing in the search box. All you need to type is "product n" before it shows up. This is what you'll see:


For libraries, Xcode prefixes the "lib" for you, so all we have to do is remove the "lib" already there. For me, I will change the Product Name to just "backgroundtask".

(Now, you might ask: Why didn't you name the target that in the first place? Because there was already a target with that name.)

Adding Code to the Library


Now we need to add some code to the library. In my case, and may be for most people, I already have the code. I have it sitting in another application. So I open a Finder window, locate the .h and .m files, and drag them to the Classes folder of the Groups & Files tree.

This will bring up a dialog asking you for more details what to do with these files:


Be sure to select "Copy items into..." because you want this project to "own" the sources to this code from now on. Other projects will merely reference this in the future. Select the libwhatever target to receive these files. The test app will actually link against the library rather than include the sources directly; this will test that we have the library built properly and can be linked.

Making things public


Now we should configure which header files are public. These files are distributed with your library. Private header files are not. So select "Targets/libwhatever" from the Groups & Files tree.

This brings up a window with the files you added to this target. Go to the "detail" tab if it's not already selected. Locate those header files you want to make private and change the role from "project" (the default?) to "public".


Setting Dependencies


Before we can try building this, we need to configure the test app to use the library. Select the application target under "Targets" in the Groups & Files tree. Right click on the target and select Get Info. Go to the General tab.

Here we need to add our library as both a dependency and a linked library. The first one makes sure the library is built when we build our application. The second one links in the library.

Click the little + button underneath each section and add the static library.




Compiling


Now we can try compiling our project. This should build both the test app and the library. Of course, we haven't done anything with our test app yet, so when you run it, all it does is display a white window.

To really test your library, you should add some code to the test app that uses/tests the library. I will leave this as an exercise for the reader.

What's Next?


At this point, you have a static library project ready to be used in other projects. In part 2 of this article, I will describe the steps to do this. As expected, it's not trivial.


Summary of Steps

I never remember how to do all the above. The following checklist is a quick list of steps to take to create a static library project. I use this myself to jog my memory what steps need to be done:

  1. File > New Project..., iPhone OS, Window-based Application
    1. enter name of project
  2. Targets > Add New Target..., iPhone OS, Cocoa Touch, Static Library
    1. enter name of library, with "lib" prefix
  3. Build tab, search for "product name"
    1. remove extra "lib" prefix from name
  4. Copy code files to project
    1. select "Copy items into..."
    2. select library as target
    3. deselect app as target
  5. Targets > lib..., "Detail" tab
    1. change role of public header files to "public"
  6. Targets > app, Get Info, "General" tab
    1. add library to dependencies and linked libraries
  7. Build and test

Acknowledgments


I used the following web sites to learn how to do this:

1 comment: