iPhone Framework Support - Shipping Libraries

02 Dec 2008, 16:42 PST

Introduction

Due to Apple's restrictions, third party apps for the iPhone may not include embedded dynamic frameworks or libraries, necessitating the use of static libraries for implementing shared code.

To make things more difficult, iPhone projects are actually targeted at two distinct platforms: iPhoneOS (the phone) and iPhoneSimulator (the simulator). The two platforms are very different, and one can't build a universal binary for both the simulator and the phone by specifying both i386 and armv6 architectures, as you would if building a universal binary for Mac OS X.

These issues complicate shipping re-usable libraries for the iPhone, and I've struggled to find a reasonable method for releasing Plausible Database, as well as the number of other open source libraries I plan on releasing with our (yet to be approved) iPhone application, Peeps.

Now that the NDA is no longer in effect (yay), this article is intended to be the first in a (likely sporadic) series on iPhone development and to-be-released open source libraries.

Frameworks are Good

A significant advantage of frameworks over simple static libraries is the bundling of headers, the library, and any resources into an easy to install bundle -- to use a framework, simply copy it into your project and add it to your target. The include and linker paths will be automatically configured, and resource references will even be resolved to the correct file path.

One doesn't have to give up all of functionality of frameworks when developing for the iPhone -- one undocumented (and, to quote, "semi-supported") feature of framework linking is "static frameworks". Generally a framework includes a dylib -- "MyCode.framework/Versions/Current/MyCode". By replacing this dylib with a static library, the framework can be easily imported and used within an iPhone project.

Static frameworks are not a full replacement for standard frameworks -- standard methods for accessing the framework resources, such as +[NSBundle bundleForClass:], will not return the expected bundle. The bundle path is determined by querying dyld for the library path corresponding to the symbol address -- In the case of static linking, this will return the path to the binary into which the static library was linked. (see the CFBundle implementation (APSL, requires free ADC account)). Additionally, NSBundle/CFBundle will examine the previous stack from for the return address, and use that return address to determine the calling code's dyld binary path. Given these restrictions, some work-arounds are possible, such as using -[NSBundle privateFrameworkPath] to locate the path to the embedded framework.

Additionally, the iPhoneOS and iPhoneSimulator SDK specifications do not support the 'Framework' product type, meaning that the framework directory structure must be built by hand. Fortunately, frameworks are fairly simple directory structure. For Plausible Database, I simply the built Mac OS X framework, and then replacing the enclosed dynamic library with the iPhone static library. (To see where supported build types are defined, take a look at /Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Specifications/iPhoneOSProductTypes.xcspec on your development system.)

While static frameworks are fairly limited (by a significant margin), I've found them to be a convenient, if small, improvement on shipping standalone static libraries. Your mileage may vary.

Universal Binaries: iPhoneOS vs. iPhoneSimulator

While Mac OS X bundles (applications, etc) include support for multiple platforms:

Application.app/Contents/MacOS
Application.app/Contents/Windows

frameworks do not:

MyFramework.framework/Versions/A/MyFramework

As noted earlier, iPhone projects actually target two distinct platforms -- the simulator, and the phone.

It is possible, if incorrect, to lipo together a single static library that may be used for both platforms. With such a binary, the i386 binary will be used when linking against the simulator SDK, and the armv6 binary will be used when linking against the iphoneos SDK. While this will work without error, it may break in the future. For example, if Apple ever releases an i386 based iPhone (using, for instance, the Intel Atom, then binaries intended to be used with the iPhoneOS SDK must be built armv6/x86 universal, and the result will not work when used with the simulator.

As an alternative, one can use the PLATFORM_NAME or EFFECTIVE_PLATFORM_NAME to link against a platform-specific static library; simply add -lSomeLibrary${EFFECTIVE_PLATFORM_NAME} to your target's linker options.

Conclusion

As it stands, shipping librares for the iPhone is fairly messy. Until a more approachable mechanism is provided for shipping iPhone libraries, I plan on building universal i386-simulator/armv6-iphoneos static frameworks for my open source projects and internal libraries. I might change my mind later.

In the meantime, I've filed rdar://6413456 -- RFE: Support for dylibs (and embedded frameworks) in 3rd-party iPhone apps. I'm sure it's a duplicate. =)