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.
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!
There are a few common methods for achieving this, but they each leave something to be desired:
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.) I want a solution that doesn't require me to:
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".
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.
.h files to Public. (You can select more than one, and right-click → Set Role)include/${TARGET_NAME}. This will copy all the .h files you marked as "public" into <wherever>/include/cocos2d/, for example.${BUILT_PRODUCTS_DIR}/include#import "cocos2d/cocos2d.h" and Build and Rejoice!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.
.h files to Public. (You can select more than one, and right-click → Set Role)/usr/local/include. Go get yourself a healthy snack instead!${BUILT_PRODUCTS_DIR}/usr/local/include#import "cocos2d.h" and Build and Rejoice!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}"
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