Monday, November 17, 2008

Building static libraries with the iPhone SDK

I have a client that wants to use my iPhone Radio Player code for their own radio station, however they want to be able to submit the app to iTunes under their own developer account. The gotcha is they are not purchasing my source code - they want to pay a smaller fee and just have me supply them with a binary image which they can then submit to Apple.

I do not have access to my client's Apple developer account, so I needed a way to supply the client with an Xcode project that includes a skeleton of source code along with a binary library that includes all the custom radio player functionality I have developed. This will allow the client to build the iPhone app and submit it via their account without giving the client access to my source code. Normally, one would probably create a custom framework to wrap up the custom radio player code, but in the case of the iPhone, this is not an option. Apple forbids the use of external frameworks or dynamic library linking in an iPhone app. Instead, I needed to create a static library containing my radio player code.

Fortunately, creating a static library for the iPhone turned out to be a fairly simple, but not at all well documented, process. If you already have an Xcode project, you can easily turn any portion of the source code into a static library. Here's how.

1) In your Xcode project, locate the "Targets" section under the "Groups & Files" sidebar. Right click on "Targets" and choose to add a new target:



Then, in the following dialog box that appears, choose a Cocoa Touch "Static Library" target:



Name the library whatever you'd like. In this example, I named the library "TestStaticLib."

2) Now, assign source code files to your static library. You can simply drag existing files from your project list. Note: don't add the .h files.



3) Then, remove those same source code files from your app target:



4) Add the new static library to your target app via the "General" settings for the target:



5) Edit your target app's linking settings to add "-ObjC" to the "Other Linker Flags". This is only required if your static library defines Objective-C classes that your app is going to reference:



6) Build your new static library for all the SDK targets (such as simulator, iPhone Device, etc.).

7) Build your app. It should now be referencing the static library rather than compiling from the original source code.

If you need to then distribute the app to a client, you can simply remove the source code for the static library and the client will still be able to build the app without having access to the original source code.

This technique might also be useful if you have some shared code you'll use in multiple applications, but don't want to recompile the shared code each time you make a new app that references it. Simply copy the shared library binary images to a specific location in your development environment and then have all your apps link with that code.

33 comments:

Biappi said...

Just one deployment tip

I've found that creating an Universal Binary gluing togheter the device and the simulator buids "Just Works"

It's considerably better to have only one .a than one for each architecture

d235j said...

Not really; only the developer will be testing the app in the simulator, so this will just result in extra cruft for the end user.

Phil said...

I don't think there will be extra cruft. If you make a "fat" library (with device and simulator), the linker will only link in the relevant portion of the library. This is different from dynamic linking.

costan+aw said...

If you're using static libraries to share code between iPhone projects, you can use a tool I wrote that takes the content of one project, and merges it with another project.

The tool is on GitHub, with complete instructions for using and extending it - http://github.com/costan/zerg_xcode

With my tool, you would wrap the library in an Xcode project, with all the files tucked away nicely under one folder and one group. Then the consumers would download the project somewhere, then “zerg-xcode import” from the project directory. The targets and files would be inserted in the consumer’s project, and the files would be copied on disk as well.

Most importantly, this also works for updates, even if files are added / removed.

Victor Costan said...

Thank you so much for the information!

I started with your post, and tried to automate it as much as possible. This is what I came up with: iPhone Development and Code Sharing

I would be grateful for any feedback from you and your readers.

Felix said...

Awesome! You saved me quite a bit of time in trying to figure this out by myself. Thank you.

Clint said...

Great post. I realize that in this case the source code can't be shared, but if other folks come across this tutorial and are thinking about sharing static libraries, along with the Xcode project and source code, they might want to consider using a cross-project reference. This allows you to avoid pre-building each static library for multiple runtime environments (and is perhaps more flexible than the "fat" Universal Binary option). I actually wrote up a tutorial on this at http://www.clintharris.net/2009/iphone-app-shared-libraries in case it's helpful to anyone else.

Phong said...

Great, let's try this

Nic said...

This doesn't solve the problem of having a company wide library that you would like to update on the fly.

Isn't there a way I can just add the header files to my search path and add the lib to the linker phase?

I've tried doing this but the only way to get it to work is what you have listed here ='(

Phil said...

Nic:

Look into Xcode / Preferences / Source Trees. You can store your private library / sources in a separate repository and separate folder, and Xcode can find them using the Source Trees settings. It can be updated independently.

You can also look into svn:externals if you are using subversion, for instance.

sri lanka proposals said...

thank you! That looks like a great resource

Carl said...

Hi,

Thanks for your post, I am trying myself to have some code from my iphone app to use in other apps with similar functionality, so from what I understand, static libs are the way to go (due to frameworks restrictions for AppStore apps).

I have followed carefully the steps you describe, however I for some reason get stuck in step 6 (Build static lib for all SDK targets). When I build the static lib target (right-click target, select "Build lib") I get 99 build errors, basically of the type:

error: syntax error before 'UIButton'/'UIView'/etc
fatal error: method definition not in @implementation context

Then on the errors window, I click on the .m file line, and the errors seem to origin in some .h files in the .m file imports.

At first I thought it had to do with some missing frameworks in the lib target, so I added the same fw's I had in the app target (UIKit, Foundation, etc) but that did not work.

I assume there is some linking missing that should be included. May you have any clue on what could be missing? I also see there is a Copy Headers directory in the lib target. Should I add the headers there too?

I would appreciate any help. Please let me know in case you need further information.

Thank you advance!

Carl said...

Hi,

I finally got build the static lib target. A bit of background:

The static lib I am trying to build in general consists of some view controller classes (UIViewController extending classes) and delegate classes (NSObject classes) which I plan to use as base classes on other projects. I use the delegate classes to hold some functionality outside of the view controllers, and in some cases I want some UI elements to be updated from these delegates, so they hold references to UIButton, UIView, etc.

Originally the delegates contained only a (Foundation/Foundation.h) import, and that was sufficient for a successful build of the project's target. However, when I added them to the static lib target and tried to build, it sent the "error: syntax error before 'UIButton'" type of errors and the build failed, again, only on the delegates.

The solution was to add the (UIKit/UIKit.h) import to the delegates and the UI* types were recognized so the build finally completed.

I still wonder why this was not needed on the project build. (it might be something simple. I am just beginning with ObjC and XCode - Please let know in case you know the answer!)

Again, thanks for the post.

andyeb said...

For anyone else having problems using categories from statically linked libraries, you need to add -all_load to "Other Linker Flags", in addition to -ObjC.

mad-boy said...

I can't get this to work with simulator and device. I compile 2 libraries, one for device and one for simulator. I add them both, but Xcode doesn't seem to know, which one to use! If I compile first for simulator it only runs on simulator and if I compile first for device it only runs on the device! Any suggestions here?

Anonymous said...

An answer and a question...

mad-boy:
To get the library so it can work on both simulator and device, you must find the build setting for "Architectures" and add to it two rows, one for "Any iPhone OS Simulator" and one for "Any iPhone OS Device". I don't know how to create the rows from scratch but you can drag-n-drop existing rows in from your main app which usually already has these two rows defined. After that, your library build will work on device+simulator for one but not both of the configurations: debug or release.

Which brings me to my question:
Whether I define this build setting for "All configurations", I cannot get a single "Obese binary" which contains all 4 libraries: simulator+device*debug+release. Making two libraries (one for debug, one for release) results in some kind of collision in xcode where neither will build.

My objective is to be able to distribute a header file and the compiled library and have any iphone developer be able to use the single library.

Has anyone figured out how to fix the fat binary problem and successfully build an obese binary?

Mostly Torn said...

I could be wrong, but I'm pretty sure you can't create a library that supports both debug and release at the same time.

As you mention, you can make one that supports both sim and real device, but not both debug and release at the same time.

Anonymous said...

Even so, putting two libraries in the project causes a build failure, so it's unclear how one can distribute libraries to other developers that they can use in their apps during testing and release.

Is there a build-setting where I can say "if debug, use this version and if release use this other version"?

The ideal would be the compound binary. To share libraries in my mind requires that it be secure (hence the compiled lib over source) and easy (a single dropped file).

Anonymous said...

what should we give to the client. the complete project or just the .a lib file...please help as i am new to iphone coding

Anonymous said...

If you run into missing UIKit-stuff, the static library target most probably doesn't use the prefix header file in your project. The header file usually contains #import . Just "Get Info" on the target and enable "Precompile Prefix Header" and type the file name in "Prefix Header" in the build tab.

Achilles said...

Great Tutorial!
I have a question tough, is there a way to hide a Category inside a archive?
I'm facing linking problem in doing that, the category methods are not found

windows 7 home basic said...

I just wanted to comment your blog and say that I really enjoyed reading your blog post here. It was very informative and I also digg the way you write! Keep it up and I'll be back to read more soon mate

KIRANJAGAN-MCA said...

One error has occured

Error is :'[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key window.'

Luke said...

noob life-saver thank you!
What happens with .h files? ok, I dont add them to the lib but if I remove them from my project I get errors?
should I still have
#import mything.h
in my code?

Mostly Torn said...

Yes, you still need to include the .h files as separate files.

Shurikosha said...

You may make "Direct Dependencies" into your Target's General tab to your library. So you needn't build your lib under all architecture. Your target automatically will build your lib under correct architecture.

Anonymous said...

How can add the images etc to the static library , I have tried to add the budle resources but it is no taking ?

Anonymous said...

Will Apple let your customer publish an application that uses your binary library, even though Apple will not receive the source code to that binary library?

Also what is the difference between a "framework" (which Apple will not let people use), and what you are providing for your customer? Is your library not a framework for building a music listening app? Where do you draw the line?

Doctor Vindershlots said...

I know somebody might have asked this before but I didnt find any answer ...

Does the lib includes all the xib files and images ?

Thanks

Anonymous said...

I have built a static library which I am using into one application. The static library is built for iPhone device and simulator and created universal library

When I used it in application everything works well. I could debug the library from application using line by line debug method in simulator mode.

While debug xcode is showing the code of the library which I want to hide during debug.

Is there any way to do this? Any command or method to remove code from library.

Thanks in advance..

niksu22 said...

Hi friends,
After including the library, when i try to call a method of that library , xcode gives me a waring 'No such method found' . But, the code gets compiled and even the method is called. Whats wrong ? need ur suggestions.

mano said...

I follow the blog upto creating static library. But, i'm struggling to use the library in a separate application. it's .a file only enough or have to use some other files?How to link the .a library file to my new application..Anyone explain me as brief as possible..

veibhav said...

How can I convert code into static library ( .a file) in Xcode 4.2..Is there any tutorial or steps??


All content copyright © 2009  Brian Stormont, unless otherwise noted.   All rights reserved.