February 21, 2010

Static Libraries in iPhone Projects (Part 3)

This is an addendum to my earlier 2-part article on how to create and share static libraries for iPhone projects. Part 1 discusses how to setup Xcode to create a static library target. Part 2 discusses how to share a reference to that library from within another project.

This addendum to address a missing step from part 1. According to Apple documentation, static libraries do not need to be code signed. This is only important if you are an "official" Apple iPhone developer ($99/year).

To remove code signing, select the library target under Groups & Files > Targets. Double click the target to bring up the info dialog window. Select the Build pane.

Scroll down to the Code Signing group. It should look something like this:

See the highlighted line and how it says "iPhone Developer"? Click on that value and you will get a little menu. Select "Don't Code Sign".

That's it!

February 8, 2010

Static Libraries in iPhone Projects (Part 2)

This is the second part of a two-part article on how to create and use static library projects in your iPhone apps. In Part 1, I discussed how to create a static library project, as well as including a test application.

In this part, I will describe how to include this static library project into other projects. The steps outlined here offer several advantages. One is the ability to load the static library project simply by double-clicking on the project reference in your other project. This brings up a new Xcode window for the library project, where you can browse the code and even make changes. If you do make changes in the static library code, not to worry because when you build your application, it will also build your static library project.

In order for this convenience to work, though, you must follow these steps closely. Xcode is powerful, but it is also non-intuitive and finicky in some areas.

In this part, I will continue my example of using my BackgroundTask static library I put together in the first part. This static library is available with its project and source code at the Osmorphis group. I will be "importing" this library in another project I am working on that needs the BackgroundTask functionality.

So let's begin!

Bringing over the static library project


Bring up your client project, the project that wants to use the static library, in Xcode. To bring over the library project, all you need to do is drag the project over. You can do this one of two ways:

You can drag the whatever.xcodeproj file (it is really a directory) from the Finder. This is the file to drag over:



The other option is to drag the project from one Xcode window. This is the root element of the Groups & Files tree:


Where you drag this is the Xcode project of the application that wants to use the library. You can drag and drop anywhere in the Groups & Files tree. When you drop, a dialog sheet comes down asking for the details of what you want to do:


Be sure you do not select "Copy items..." and the reference type is selected as "Relative to Project".

What you'll see after this is a new item in your Groups & Files tree with the same blue "A" icon for Xcode projects. Here is an example of my application that has several static libraries referenced:


If you have many of these, you could group them in a folder called "libs" or something like that.

Specify the Product


Now you have to tell Xcode what products from this external project you are interested in. Select the library project you just dropped in. If you don't see the "Detail", "Project Find", ... tabs, then you may have your editor window taking up the entire content pane, so drag down the split pane to show:


As shown above, you want to select the library product, but not the test app.

Locate Header Files


You need to tell your application where to find the header files of the referenced library. It would be nice if the reference would be sufficient, but it is not, so you have to muck with compile flags.

Double-click on the project of your target application. Important: don't click on the root element of the Groups & Files tree. This is not what you want. You want to click on the application in the Targets element of the tree. If you edit the wrong one, you'll be banging your head on the wall for hours with missing header file problems (like I did).

Go to the "Build" Tab.

Make sure you are editing for all configurations, since the location of the header files won't change, if you use the instructions below.

We need to edit the "User Header Search Paths" property, so go ahead and start typing this in the search box (or you can hunt for it manually). Double click on the value field. This should bring up a dialog that may contain other entries.

Add a new entry for your static library. The public header files are stored in a long-winded location, and it depends on whether you're building a Debug or Release build, and if you're building for the simulator or an actual iPhone device. These specifics are encoded in some variables. So copy and paste the following for your new user header search path:

${PROJECT_DIR}/../staticlib_project/build/${BUILD_STYLE}-${PLATFORM_NAME}/usr/local/include

Replace the "staticlib_project" part with the directory of the static library you are trying to set up. This assumes that your static library project is in a peer directory.

I have several static libraries referenced in my application, so my user header search paths property looks like this:


The ${BUILD_STYLE} and ${PLATFORM_NAME} variables will be replaced automatically to match your current build. This is how we can use the same settings for all builds.

Setting Linker Flags


While we're in the build properties, we need to add an option to the linker flags: -ObjC. This tells the linker to load all classes and categories from static libraries. If you don't do this, then you may get linker errors.

Clear out the "user header search" in the search box and type "other linker flags". Add -ObjC to this:


Set the Dependency


Now we would like to set a dependency on the library so that it will get compiled when you compile your application. Double-click on the application under Targets, and go to the "General" tab.

Click on the + button for the Direct Dependencies. Select your new library.

You also may have to add the library to the Linked Libraries panel, also found on the "General" tab. Hit the + and select the library. I have seen times where the library is not available to choose. In these cases, drag the .a file from the project down to the "Link Binary With Libraries" section of the target.

Good to Go


At this point, your static library should be properly referenced. The nice thing about this setup is that your library is built whenever something is changed; no need to switch over to that project to build it separately. But, if you need to look at the source code, simply double-click on the project from your tree and the other project is opened up in a new window.

It is also to step into the source files of your referenced libraries in the debugger.

So stop dropping copies of reusable code into your projects. Build a library and reference them!

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: