How to Not Create an iPhone Framework

a.k.a. How to Organize your reusable code into a shared library, Incorporate it as a dependency of your application project, Build, and Debug, without wanting to kill anyone.

The Problem

If you have done much iPhone development with Xcode, you may have noticed that it's a huge pain, if not impossible, to share code cleanly between projects. If you are working on a Mac OS X project, there is a "Cocoa Framework" template, but there's no corresponding template for iPhone OS projects, and the general consensus is that Xcode won't let you create shared libraries for the iPhone at all.

You can, however, create static libraries easily using the "Cocoa Touch Static Library" template. The problem is that unless your library is really, really, static – i.e. unless you aren't updating / modifying / or rebuilding it any more – it's still a big hassle to incorporate it into your application project's build. But if, like me, you're developing your application and shared libs simultaneously, you want your application's build process to detect changes in the library's code and do the right thing, regardless of whether you're building for the simulator or a device, release or debug, or whatever!

The Contenders

There are a few common methods for achieving this, but they each leave something to be desired:

  • Multiple Copies – Just make a separate copy of all the "shared library" source files within each dependent application's source tree. This is easy unless there are a lot of them; plus you lose shared revision control of your shared sources.
  • Foreign RCS subfolders – Have a separate copy of all the shared library source files in each dependent application's source tree, but utilize git submodules or svn:externals, etc. to retain coherent revision control of the shared sources. This is probably easy to set up if you are a git / svn expert; I'm not. Also I imagine I'd be causing a bunch of RCS conflicts with myself.
  • Frameworks using fat binaries and lipo 1) 2) 3) 4) – This seems like a nice method for releasing a library to application developers. The headers and both "device" and "simulator" builds of the library are all together in a nice bundle that you can easily add to your Xcode project. But, as far as I know, you can't lipo debug and release builds together, so this method isn't convenient during library development. (Update: My lipo script for dealing with Release vs Debug builds at the bottom.)

The Motivation

I want a solution that doesn't require me to:

  • change a combinatorial number of build settings
  • store a bunch of absolute or ugly relative paths
  • use a project folder layout, to make my public headers accessible
  • write custom .plist or custom build scripts to fix everything

The Solution

The most convenient and best illustrated solution I've found so far is:

Code Sharing Via Static Libraries And Cross-Project References by Clint Harris from April 1st, 2009

My only objection is that I don't want to hard code the path to my library's "Source Tree".

Version #1 (I use this one.)

Your target names do need to be unique across all your projects, because they will all be built in the same folder, and you otherwise risk collisions.

Your .h file names do not need to be unique across all projects, because you will prefix them with the library name when you reference them from your application source, as you would with frameworks.

  1. Configure Xcode
    1. Set up a shared build output directory (Xcode → Preferences → Building → Place Build Products in: → Customized Location → <wherever>)
  2. Configure each library
    1. In your library target, set the role for all the public .h files to Public. (You can select more than one, and right-click → Set Role)
    2. In the Build Settings for your library project (or target), set the Public Headers Folder Path to include/${TARGET_NAME}. This will copy all the .h files you marked as "public" into <wherever>/include/cocos2d/, for example.
  3. Set up each application
    1. Add each library project file to your application project.
    2. Drag each library product to your application's target's link step.
    3. In the General settings for your application target, add your library target to the Direct Dependencies.
    4. In the Build settings for your application project (or target), set the User Header Search Path to ${BUILT_PRODUCTS_DIR}/include
  4. You can now #import "cocos2d/cocos2d.h" and Build and Rejoice!
Version #2

Your target names do need to be unique across all your projects, because they will all be built in the same folder, and you otherwise risk collisions.

Your .h file names do need to be unique across all projects, because you will not prefix them with the library name when you reference them from your application source, as you would with frameworks.

  1. Configure Xcode
    1. Set up a shared build output directory (Xcode → Preferences → Building → Place Build Products in: → Customized Location → <wherever>)
  2. Configure each library
    1. In your library target, set the role for all the public .h files to Public. (You can select more than one, and right-click → Set Role)
  3. You don't have to set the Public Headers Folder Path; it will default to /usr/local/include. Go get yourself a healthy snack instead!
  4. Set up each application
    1. Add each library project file to your application project.
    2. Drag each library product to your application's target's link step.
    3. In the General settings for your application target, add your library target to the Direct Dependencies.
    4. In the Build settings for your application project (or target), set the User Header Search Path to ${BUILT_PRODUCTS_DIR}/usr/local/include
  5. You can now #import "cocos2d.h" and Build and Rejoice!

How to (actually) Create an iPhone Framework

As I started to write How Not to Create an iPhone Framework, I realized that method is probably not suitable for deploying a finished library to other developers. I use the method described above to manage libraries which I am already able to build in Xcode (e.g. I have the source.)

Fortunately, Mark Smith has written a very slick article on tricking Xcode into actually making a framework itself:

Making Your Own iPhone Frameworks. In Xcode. May 25th, 2010

As he mentions, you probably want to do some lipo surgery on your framework afterward.

Here is the script I use for mine: (I modified Dmytro Golub's.)

# The product Xcode just built
THIN_FRAMEWORK="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework"
 
# The location to install to -- notice the ${CONFIGURATION} suffix; we can build separate -Release 
# and -Debug versions
FAT_FRAMEWORK="${INSTALL_PATH}/${PRODUCT_NAME}-${CONFIGURATION}.framework"
rm -rf "${FAT_FRAMEWORK}"
mkdir -p "${INSTALL_PATH}"
 
# Copy the Framework structure and Headers into place.
cp -a "${THIN_FRAMEWORK}" "${FAT_FRAMEWORK}"
 
# Merge the -iphoneos and -iphonesimulator versions of the static library.  
# This will fail until you've tried to build version (device + sim) at least once.
# After that, it will merge the most recent version of each to the ${FAT_FRAMEWORK} location
lipo "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PRODUCT_NAME}.framework/${PRODUCT_NAME}" \
     "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PRODUCT_NAME}.framework/${PRODUCT_NAME}" \
     -create -output "${FAT_FRAMEWORK}/${PRODUCT_NAME}"
 
# When I import the framework into my application project, I want the default configuration to 
# be "Release", so I'm making a symlink to "-Release"
DEFAULT_CONFIGURATION="Release"
cd "${INSTALL_PATH}" && ln -sf "${PRODUCT_NAME}-${DEFAULT_CONFIGURATION}.framework" "${PRODUCT_NAME}.framework"
 
# OR, you can comment out the previous two lines, and uncomment the following, to make the most 
# recently built configuration the default.  I'd use the former for release, and the latter for development.
# cd "${INSTALL_PATH}" && ln -sf "${PRODUCT_NAME}-${CONFIGURATION}.framework" "${PRODUCT_NAME}.framework"
 
# Open the install folder for you, so you know where your framework went.
# You'll probably want to comment this out pretty soon.
open "${INSTALL_PATH}"

The End

Some good background reading:

Building static libraries with the iPhone SDK by Brian Stormont from November 17th, 2008

Understanding Xcode Projects by Apple from December 12, 2005


Hope this helps!

- Arya aka Refried

1) iPhone Static Framework by Paul Jarratt from May 10th, 2010
2) iPhone static framework by Dmytro Golub from April 16th, 2010
3) Creating a Framework for the iPhone by Pete Goodliffe in December, 2009
4) How to (almost) create your own iPhone OS framework by Peter Bakhyryev from November 25th, 2008
iphone/development/project-dependencies.txt · Last modified: 2010/08/02 08:57 by arya
 
Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki