tweet @echoz email jeremy@ornyx.net
I’ve always loved backend code to be as cross platform as possible. Writing everything based upon Foundation and CoreFoundation only is one of the ways to do that and I’ve always strived to adhere to that principle as much as possible.
While that is cool, the problem begins when you actually want a universal library to work on both OS X and iOS. There is no such thing as Xcode clearly separates both. Understandably, this is seems to be an engineering and architecture decision. Still it would be cool to have a library that you can drop in either your OS X or iOS project.
There is largely 2 schools of thought.
There is inherently more flexibility with option 1, but the complexity of building such a framework is huge. Additionally it doesn’t allow you package everything in an Xcode project that can act as a dependency in your other projects.
Using lipo presents its own problems too as a Fat binary must contain code for unique architecture. In short, you cannot have a fat binary with code for the iPhone Simulator and OS X as both contain the same architecture.
After much experimenting I’ve figured out my own solution for this which I feel gives a much better workflow. The genius of the solution is that independently the project will build and create fat binaries for both iPhone Device + iPhone Simulator as well as iPhone Device + OS X.
Additionally, it can be added to existing projects as a dependency and build the appropriate static library which you can then use to automatically link to your project.
One thing to note though with this approach is that if you have the usage of Categories in your code, you will have to add to the Other Link Flags configuration of the Xcode build settings the “-ObjC” flag. Related Technical Q&A here.
Currently two of my projects use such a method of sharing code.
***Update: If you are using the dependency way of getting things done, just link to the “Device” build target. Every time you want to switch the build target you are working on, just do a Clean all. Xcode will build the architecture of either the simulator or the device depending on selection.
On to the steps.
1. Start with an empty Xcode project.
2. Add your cross platform code to the project.
3. Add a new target to your project. Make sure it is of the type “Static Library”. Do the same for every architecture you want. Remember to add a descriptive name to it.
There are a few architectures that would cover everything 1. iPhone OS Device 2. iPhone OS Simulator 3. OS X
4. Finally add an “Aggregate” build target
5. Expand all the targets you’ve added and begin adding all your headers into the “Copy Headers” section. All other source code files into the “Compile Sources” section.
6. Now configure each build target to the specific architecture that you want. One thing to note is that under the “C Language Dialect” make sure C99 is selected. Don’t forget to specify your Prefix Header if you have one.
7. Finally, the last piece of the puzzle is to deal with the aggregated build target. What this stage does is that it will build every other build target and also run the build script to lipo specific architectures together. Get in the inspector and add all your other build targets as dependencies.
8. Finally add a “Run Script Phase” for the aggregated target.
Add and tweak the following code in the script area. Close it and you are done.
# Remove iOS lipo target (device + simulator)
rm -fr build/${BUILD_STYLE}/libEdventurous-iOS-${BUILD_STYLE}.a
# Remove universal lipo target (device + os x)
rm -fr build/${BUILD_STYLE}/libEdventurous-${BUILD_STYLE}.a
# Create iOS lipo of device + simulator
lipo -create \
"build/${BUILD_STYLE}/libEdventurous-Device.a" \
"build/${BUILD_STYLE}/libEdventurous-Simulator.a" \
-output build/libEdventurous-iOS-${BUILD_STYLE}.a
# Create universal lipo of device + os x
lipo -create \
"build/${BUILD_STYLE}/libEdventurous-Device.a" \
"build/${BUILD_STYLE}/libEdventurous-OSX.a" \
-output build/libEdventurous-${BUILD_STYLE}.a
Once this is done, there are 2 ways you can use your new universal framework. One would be to link directly to the products of the lipo script.
However, I prefer the other way which would be to add the universal framework project into your existing projects and have dependencies made to the respective static library.
Just remember to set your Header Search Paths and also to add the above mentioned Other Linker Flags.
Hope this was useful.