Apple iPhone Developer agreement forbids using Frameworks in iPhone applications. The main reasons Apple does not allow using them is performance considerations and that developer-private frameworks won’t be reused in memory between iPhone applications anyway. But this doesn’t prohibit 3rd party iPhone developers to create static libraries for reusable code and possibly even distribute them for other developers while not providing source-code.
In this blog post I’ll try to describe the process of:
Nearly everything in the following applies to Mac OS X static libraries and projects too. I’ll indicated the differences.
Although Xcode doesn’t have iPhone OS Static Library template as it do for Mac OS X, we can use Static Library → Cocoa Static Library template and switch SDK for it or we can create iPhone application project based on iPhone OS → Window-Based Application template and then create new iPhone OS Static Library Target what Xcode has for iPhone projects.
I’ve chosen to use iPhone OS template because created for us iPhone app Target will come in handy anyway, for example as a demo project or for some real-world testing of static library.
So, let’s create a project what a bit later will be the home of our new static library. I’ll name it “LibDemo”.
Select File → New Project… → iPhone OS → Window-Based Application.
Next, let’s create a Static Library Target. Right click on Targets,
Add → New Target… → Cocoa Touch → Static Library
I’ll name the Target “libDemo” (with lower “l”).
Now we have two Targets:
and two Products:
liblibDemo.a certainly doesn’t sound right but it’s easy to fix. Xcode names Products based on Target setting “Product Name”, so let’s go and change it.
Right click on libDemo Target, select “Get Info”, click on “Build” tab. Search for “Product Name” and be shure to use “All Configurations” configuration. Set Product name to “Demo”, Xcode adds standard static library
Fine, now we have following Products:
Next we can create some dummy class what will be compiled in our static library and what we will use in dependent project.
AMZeeba class with header file what will expose
Select File → New File… → Cocoa Touch Classes → NSObject subclass
This class should be compiled in libDemo so select both LibDemo and libDemo Targets.
Fine, now we have a class with a header and in dependent project (depending on setup) we have the choice of using all static library headers or just subset of them what we declare as “public” here (like when creating Frameworks).
I’ve chosen to use only public headers so I can be sure I’m not using anything I consider private from static library. So, if I’m distributing static library in binary form with public headers only I can be confident I haven’t anything forgotten to add to them.
To declare header public, we need to set it’s Role to “public”. Select Targets → libDemo and set “public” as Role for AMZeeba.h:
..and build our library.
Xcode creates the following structure in librarie’s build folder:
ampatspell:~/Cocoa/static/LibDemo/build/Debug-iphonesimulator$ tree . |-- libDemo.a `-- usr `-- local `-- include `-- AMZeeba.h
And while we’re here, let’s look at library symbol table:
ampatspell:~/Cocoa/static/LibDemo/build/Debug-iphonesimulator$ nm libDemo.a libDemo.a(AMZeeba.o): 00000042 t -[AMZeeba greet] 00000000 t -[AMZeeba init] 00000000 A .objc_class_name_AMZeeba U .objc_class_name_NSObject U ___CFConstantStringClassReference U _objc_msgSendSuper
As we can see, the public headers are copied to
libDemo.a actually has
AMZeeba class (
.objc_class_name_AMZeeba) and methods (symbols) defined.
Everything looks fine, we can continue with
In general there are (at least) two ways how to include code from static library project in dependent application. The simplest is just dragging headers and implementation files to dependent project and adding them to desired Target. Other way is actually to link against static library and it’s preferred way from code organisation point of view.
I’ll start with linking and then, in next section, briefly describe “dragging way” with it’s benefits and drawbacks.
Before we start setting-up linking against our static library, we need to create “Client” iPhone project.
Select File → New Project… → iPhone OS → Application → Window-Based Application
I’ll name this project “Client”. Build & Run. We should see blank, white window background in iPhone Simulator or on actual device.
To link against a static library while continuing to work with both dependent application and static library we need to:
Quite a list of tasks to create this setup but fortunately it’s easy to do. We will start with first.
We need to start with a cross-project reference to static library project. This is Xcode specific feature what allows us to use Products and set build dependencies between projects.
Drag blue LibDemo project icon from “Groups & Files” and drop it inside Client project. Check “Client” in “Add To Targets” table, do not check “Copy items into destination group’s folder” in the sheet what will appear:
Press Add, the result should look like this:
As you can see, there’s LibDemo.xcodeproj reference what lists two Products — libDemo.a and LibDemo.app. We’re not interested in LibDemo.app what eventually will be just a demo of libDemo.a functionality (or just disappear). But libDemo.a is the product we will use by linking against it.
To link binary with a static library we need to add it to “Linked Libraries” list for binary Target.
Unfold LibDemo.xcodeproj and Targets → Client → Link Binary With Libraries. Drag libDemo.a to Link build phase.
Now we’re linking Client binary against our staric library but it doesn’t mean the library is automatically rebuilt if we make changes in it’s code. To inform Xcode we want to keep library up-to-date and link against newest version in this Target, we need to set library as direct dependency for Target.
To set libDemo library Target as dependency for Client Target, right click on Client Target, select Get Info then click on General tab. Click add button under Direct Dependencies list, select libDemo.
As you can see, this panel consists of two parts:
Actually we could use Add button under Linked Libraries to link against
libDemo.a as we could just drag dependent Target under Target we want it depend to. Just two ways of doing it, both yields the same result.
To make sure our direct dependency setting works, let’s clean both projects then build only Client. This should trigger building of
Select Build → Clean (⇧⌘K) and click “Also Clean Dependencies”
Select Build → Build (⌘B) and Build → Build Results (⇧⌘B)
We should see that two Targets are built successfully:
But before we can start actually using library, we need to tell Xcode where to find library headers.
First I want to show the problem. Let’s add
#import "AMZeeba.h" in
ClientAppDelegate.m, build and examine build results:
Of course, Client’s app project has no idea where to look for
External headers in Xcode are searched using “Header Search Path” (and “User Header Search Path”) build variables. We need to add
libDemo.a public header location to it.
Open “Client” Target info panel (Select Targets → Client and press ⌘I), type “header search” in search field under “Build” tab.
Double-click on “Header Search Path” and add:
The variables are:
|PROJECT_DIR||Client project’s root directory|
|BUILD_STYLE||Debug or Release|
Note: For Mac OS X Static libraries the search path value is:
Now we can build Client and it should find
…and this is the result we where looking for.
Creating a static library is not the only way how to create reusable code “bundles” for iPhone and for Cocoa development in general. On Mac OS X the preferred way is creating Frameworks but this is not an option for iPhone but there’s third way — dragging and dropping group of files from one project into another and adding them to desired Targets. This way we’re cross-project referencing source code. This has some benefits and drawbacks.
The benefit is simpler setup — we don’t need to create a static library Target, no header search pathes to set.
The drawbacks to name a few are:
In conclusion I must mention that it’s possible to mix static library approach with code cross-project references. It may be beneficial to start with static library by using drag-and-drop referencing and when library grows or there’s need to distribute static library in binary form, switch to static linking.
1 Actually I hope I’m wrong. Header Search Path alteration seems a bit hackish for something so simple.