Ad

Our DNA is written in Swift
Jump

Making Your Own iPhone Frameworks. In Xcode.

Back in April Oliver wrote an excellent article entitled “Making Your Own iPhone Frameworks”, in which he explained how to do what many developers still proclaim as impossible: how to create custom frameworks that you can use in your iPhone apps! I would recommend that you read Oliver’s article first, especially if you’re wondering what a framework is… or, possibly, why you should consider making your own frameworks. I wont talk about that here :).

To build frameworks Oliver wrote a simple shell script that first creates a typical framework bundle, and then copies resources into the appropriate directories. In this followup article, I’ll show you how to create the framework bundle entirely within Xcode, without any scripts, plugins, product-, or package-types. While this has a few advantages, I’m not claiming that my approach is in any way perfect, and there may be problems that I haven’t encountered yet. As always, this is a work in progress, so if you have any suggestions, please feel free to leave your comments below.

Enough talking. Lets get started –

The process is remarkably simple once you recognize that a framework is just a bundle like any other (whether we’re talking about Mac OS X or the iPhone OS). And since Xcode already knows how to work with bundles, all we  need to do configure one properly.

Step 1. Open Xcode and Create a new “Cocoa Touch Static Library” project, and delete the existing target.

Step 2. Add a new “Loadable Bundle” target from the Cocoa section by clicking “Projects > New Target…”.

Selecting the "Loadable Bundle" Target Template

The "Loadable Bundle" target template.

At this point you may be tempted to add a “Framework” target from the Cocoa section instead. Don’t. The iPhone SDK doesn’t support framework products, and it’s more than happy to tell you that if you ever try to build one. Our “Loadable Bundle” target just creates a generic (cfbundle ) bundle product, which is supported.

Step 3. Now comes the fun part: reconfiguring this bundle to produce a framework that Xcode can link against!

Bring up the “Build Settings” window for this new target selecting it and clicking “File > Get Info”.

The "Build Settings" window for "Loadable Bundles".

The "Build Settings" window for "Loadable Bundles" target.

Now we’re going to change a few build settings.

  1. Change the “Wrapper Extension” to “framework”.
  2. Change “Mach-O Type” to “Relocatable Object File”.
  3. Turn “Dead Code Stripping” off.
  4. Turn “Link With Standard Libraries” off.
  5. Remove all references to “AppKit” and “Foundation”.

Step 4. Change the bundles type from “BNDL” to “FMWK” in the frameworks Info.plist file. You can do this in the “Properties” section of the targets “Info” window (a bit right of the “Build Settings” section.)

The "Properties" window for the "Loadable Bundle".

The "Properties" window for the "Loadable Bundle" target.

Note: the Info.plist file isn’t required for linking in Xcode. In fact, if you check out Apples frameworks you’ll notice that they don’t actually contain the Info.plist file at all. If you’re creating a framework with a lot of resources however, it’s nice to have Info.plist setup for you, so I just leave it in.

Step 5. Add some code to the project if you haven’t already. I’ve added a HelloWorld class to the example.

The newly added "HelloWorld" class.

The newly added "HelloWorld" class.

Step 6. Add a new “Copy Headers” build phase with “Project > New Build Phase > New Copy Headers Build Phase”. This build phase is responsible for putting public and private headers into the right directory.

The newly added "Copy Headers" build phase.

The newly added "Copy Headers" build phase.

Step 7. Add your headers to the to the “Copy Headers” build phase and set their role appropriately. The easiest way that I’ve found to do this is to right click “Copy Headers” and hit “Set Role > Public”. This will make all of the headers public, copying them to the framework bundles “Headers” folder.

Note: you can also set the role of headers individually if that floats your boat :).

Step 8. Add your source files to the “Compile Sources” build phase. These will be compiled to create a relocatable object file, which is then linked against automagically by Xcode.

The target with header files and source files added.

The target with header files and source files added.

Tangent: I haven’t had any problems with this approach yet, nor do I expect to, but if this does prove to be a problem you can just build a the “Static Library” in Xcode and add it to the frameworks “Copy Bundle Resources” build phase. This would also be useful when building a universal framework, as Oliver described in “Universal Static Libraries”.

Step 9. Build the framework bundle.

Step 10. Enjoy your new framework!

The finished framework, built and ready to go!

The finished framework, built and ready to go!

Now for some conclusions.

That’s really all there is to it, although it doesn’t have to end here. You can use this technique to build almost any kind of custom bundles from within Xcode! Personally I’ve used this approach to build plug-in bundles for a number of projects, and found it to be very convenient. Certainly much more convenient than building the bundles by hand!


Categories: Recipes

44 Comments »

  1. Excellent article. I do have one question though.

    Is this framework also a universal framework or do I have to build separate copies for simulator testing and release?

  2. In order to get a universal framework you need to compile two versions of a library and lipo them together. There is no way to do that inside Xcode at the moment. Refer to my other two articles on how to do that.

  3. As Oliver wrote below, there’s no way to do this within Xcode right now… but I’m of two minds whether this is a good idea. It’s not like on the Mac where we’re just compiling the app for two different processors. The iPhone OS and iPhone Simulator are separate (all be it similar) platforms. Building a universal framework on the iPhone is kind of like building a universal app that runs on the Mac and *nix. Why is this a problem? With architecture as the only thing distinguishing the two platforms, if Apple one day produced an iPhone/iPad/iWhatever that had an intel Atom chip in it, for example, linking with these libraries would link the iPhone OS version… and all hell would break loose. We’re still waiting for a solution which would allow us to build really universal binaries, so, until then I’ll be trying to stay away from them.

  4. Thank you for the reply. I have actually read both of the other articles to work. That’s one of the reasons I was confused because the article you referred to at the top of your post was using a universal library.

  5. Excellent Article!

    Only one problem. Using XCode 3.2.4 I get the following error trying to link my new Framework into a project:

    [WARN]warning: skipping file ‘/blah/blah/MyFramework.framework’ (unexpected file type ‘wrapper.cfbundle’ in Link Binary with Libraries build phase)

    I set the type to FMWK but it still thinks it is a wrapper.cfbundle AND I CANNOT CHANGE IT. Please hold me.

    I really really really want to get this working.

    Follow up question: If I wanted to include nibs and images in this framework, how could I do that?

    Thanks,

    RC

  6. I have no idea what you are trying to do. For me it’s working just dragging the framework into a new project. Nibs and images you can either embed as I showed in another post or add a separate bundle for them. You cannot add them to the framework bundle because that would cause the library to be copied to the app bundle as well.

  7. Hi… this is a really excellent article and it works fine for me as long as I don’t have any resource files. Right now I’m trying to bundle a Framework that needs a NIB file as resource. I want to embed this NIB file in the framework for easier distribution. It works excellent for the simulator but not for the device configuration. I always get the following error:

    *** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Could not load NIB in bundle: ‘NSBundle (loaded)’ with name ‘FrameworkTableView”

    I also tried the other article and build a universal library (“Making your own iPhone framework” from April 2010). With this approach the resources were not copied into the framewok bundle. Even after I copied them manually into the right folder within the bundle, it didn’t work for the device configuration. Why is it always working for the simulator but never for the device?? Any help would be appreciated.

    Thanks,
    Bjoern

  8. You have to put all your resources in a separate bundle and add this to the target project. Then you need to load the resources from there. Anything you put into a framework bundle is lost because it’s basically just a static library that gets linked with your app, nothing else is copied automatically.

  9. Thanks a lot for the fast answer. I thought I read somewhere in one of the article that I can put my resources in the bundle as well… Is there a way to create such a resource bundle at the same time I am building the framework bundle? If yes, what do I have to do? Thanks!

  10. Just add a new bundle to your project.

  11. Sorry that I’m asking so many questions but I added a new loadable bundle target to my framework project and added all needed resources to the “Copy Bundle Resource Phase”. I added this bundle as “Existing Files” to my Resources group. On the device it is showing the content of the xib file (UITableView) now so somehow it seems to work and finds the file now. But the png files (table cell background images) that are in this bundle, too, are not loaded. It seems that the app can’t find these resources. What am I doing wrong? Thanks!

  12. Okay… I got it working now. I created a new OSX Bundle Project, switched the target to iOS Bse SDK and added it to my test project with the framework. And I had to customize my framework so that it tries to load the needed resources from the bundle I created and not from the main bundle. It’s not as nice having only one file but it’s better than nothing 🙂 Thanks for your help

  13. I can’t seem to deploy this to the device but works great on the laptop. Anything special we need for linking when deploying to a device with these?

    The errors we get are:

    ld: warning: directory ‘/Users/alexandercastillo/nxcuniversal/vobmnt/mobile/netx/device/universal/../NXC/build/Debug-iphonesimulator’ following -F not found
    ld: warning: directory ‘/Users/alexandercastillo/nxcuniversal/vobmnt/mobile/netx/device/universal/../NXCFramework/build/Debug-iphonesimulator’ following -F not found
    ld: warning: in /Users/alexandercastillo/nxcuniversal/vobmnt/mobile/netx/device/uilib/build/Debug-iphonesimulator/NetXClient.framework/NetXClient, file was built for i386 which is not the architecture being linked (armv6)
    Undefined symbols:
    “_OBJC_CLASS_$_MainViewController”, referenced from:
    objc-class-ref-to-MainViewController in AppDelegate_iPad.o
    “_OBJC_CLASS_$_LoginViewController2”, referenced from:
    objc-class-ref-to-LoginViewController2 in AppDelegate_iPhone.o
    “_OBJC_METACLASS_$_Application”, referenced from:
    _OBJC_METACLASS_$_AppDelegate_iPhone in AppDelegate_iPhone.o
    _OBJC_METACLASS_$_AppDelegate_iPad in AppDelegate_iPad.o
    “_OBJC_CLASS_$_MainViewController2”, referenced from:
    objc-class-ref-to-MainViewController2 in AppDelegate_iPhone.o
    “_OBJC_CLASS_$_Application”, referenced from:
    _OBJC_CLASS_$_AppDelegate_iPhone in AppDelegate_iPhone.o
    _OBJC_CLASS_$_AppDelegate_iPad in AppDelegate_iPad.o
    “_OBJC_IVAR_$_Application.styleGuide”, referenced from:
    _OBJC_IVAR_$_Application.styleGuide$non_lazy_ptr in AppDelegate_iPhone.o
    (maybe you meant: _OBJC_IVAR_$_Application.styleGuide$non_lazy_ptr)
    ld: symbol(s) not found
    collect2: ld returned 1 exit status

  14. The library you are trying to link here does not have a version lipoed in that fits the architecture of your target.

  15. I got this same error using Xcode 3.2.5. I found a different way to accomplish the goals (mostly) – see below for my post.

  16. I had the same problem as genericrich above and could never get my “framework” to link. So I found a different approach: build a static library and a bundle at the same time, link to the library, and copy the bundle. So, the sort answer is to craft a project that builds a iOS static library and a bundle.

    To build the “framework” project:
    – create a new project of type iOS Static Library
    – add a new target of type Bundle (Cocoa)
    – add a new aggregate target (Cocoa)
    – make the aggregate depend on the library and bundle
    – insure that all resources get copied into the bundle
    – build the aggregate and verify it works

    In each app using the “framework”
    – add the above project to the app project
    – add the Static Library to the “Frameworks” section
    – add a new copy files phase and have the included bundle copied into the app resource folder
    – if using categories in library, add a -all_load flag to the app’s linker flags (working on getting -force_load to work)

    I also added a NSBundle category called bundleBundle that returns the included bundle, using full path semantics (need for both accessing images and nibs – you need this for all the UIViewController’s “initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle” in the static library.

    Right now this is only working in the simulator – have not tried on a device yet. YMMV.

  17. The guide still works as it’s written with one cavat: you cannot auto-build everything. Instead I am able to successfully build a framework like this:

    1) switch to simulator, build sim library
    2) switch to device, build device library
    3) execute lipo step
    4) make a new bundle and have the lipo’ed library copied in as a resource

    Et voilá.

  18. Twice I reproduced the steps in this article, and twice I got this error in the final app link (same as genericrich):

    [WARN]warning: skipping file ‘/blah/blah/MyFramework.framework’ (unexpected file type ‘wrapper.cfbundle’ in Link Binary with Libraries build phase)

    This was when trying to get it working in the simulator.

    Nothing I could do would prevent the error above. Not sure why the two of us have the problem and others don’t.

    The technique I mentioned above should work well for all permutations, since the current “Release” or “Debug”, Device or Simultator, get passed on to the included project.

    Also, note that Apple specifically prohibits Frameworks *dynamic linking” in their terms, so who knows what would happen if you submit an app that does. In my technique, it uses the static library but you get all resources in the bundle. Mostly the same end result but all perfectly “legal”.

  19. is the following possible ?

    I have about 30 classes, I want to have these classes in the library. The user should see only 10 classes, to work with. The other 20 (.h) will import from an umbrelle-header file.

    Sample:

    LibraryConfig.h (visible user) –> #import “umbrellaHeader.h” umbrellaHeader.h (not visible) –> #import all 20 other .h files

    If I use the single lib.a file, I can only #import these headerfiles I droped in the new project.

    I also added the to other linker flag : -all_load and -ObjC.

    Short Version of my problem: the classes (visible to the user) should be able to import and work with classes non visible to the user. These classes should be available in the fat lib.a

    Is this possible and how ?

  20. Easy, you only have the framework bundle add the public header file to the output.

  21. Thanks for the article. I must have messed up step 3 #5 as I am getting compiler errors with: AppKit/???: no such file or directory. I removed all references in Project Settings. Where else? Thanks in advance. -Dan

  22. Check you target and project build settings for such references.

  23. Hi,
    Am facing with the below problem when i try to link the custom framework. for me its not even working in simulator. Please help.

    Ld build/Debug-iphonesimulator/RBSCMRT.app/RBSCMRT normal i386
    cd /Users/rbs008/Desktop/RBSCMRT
    setenv MACOSX_DEPLOYMENT_TARGET 10.6
    setenv PATH “/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin”
    /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/gcc-4.2 -arch i386 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk -L/Users/rbs008/Desktop/RBSCMRT/build/Debug-iphonesimulator -F/Users/rbs008/Desktop/RBSCMRT/build/Debug-iphonesimulator -F/Users/rbs008/Desktop/RBSCMRT/../RBSServices/build/Debug-iphonesimulator -F/Users/rbs008/Desktop/RBSCMRT/../RBSCMRTUI/build/Debug-iphonesimulator -filelist /Users/rbs008/Desktop/RBSCMRT/build/RBSCMRT.build/Debug-iphonesimulator/RBSCMRT.build/Objects-normal/i386/RBSCMRT.LinkFileList -mmacosx-version-min=10.6 -Xlinker -objc_abi_version -Xlinker 2 -framework Foundation -framework UIKit -framework CoreGraphics -framework CMRTUILib -o /Users/rbs008/Desktop/RBSCMRT/build/Debug-iphonesimulator/RBSCMRT.app/RBSCMRT

    Undefined symbols:
    “_OBJC_CLASS_$_Service”, referenced from:
    _OBJC_CLASS_$_AuthenticationService in CMRTUILib
    “_OBJC_METACLASS_$_Service”, referenced from:
    _OBJC_METACLASS_$_AuthenticationService in CMRTUILib
    “_OBJC_IVAR_$_Service.region”, referenced from:
    _OBJC_IVAR_$_Service.region$non_lazy_ptr in CMRTUILib
    (maybe you meant: _OBJC_IVAR_$_Service.region$non_lazy_ptr)
    “_OBJC_IVAR_$_Service.serviceresponse”, referenced from:
    _OBJC_IVAR_$_Service.serviceresponse$non_lazy_ptr in CMRTUILib
    (maybe you meant: _OBJC_IVAR_$_Service.serviceresponse$non_lazy_ptr)
    “_OBJC_IVAR_$_Service.serviceMethod”, referenced from:
    _OBJC_IVAR_$_Service.serviceMethod$non_lazy_ptr in CMRTUILib
    (maybe you meant: _OBJC_IVAR_$_Service.serviceMethod$non_lazy_ptr)
    “_OBJC_CLASS_$_ServiceManager”, referenced from:
    objc-class-ref-to-ServiceManager in CMRTUILib
    ld: symbol(s) not found
    collect2: ld returned 1 exit status

  24. Hi Oliver!

    I’ve created an Xcode 4 template that lets you build a universal framework as easily as a static library.

    https://github.com/kstenerud/iOS-Universal-Framework

    Enjoy!

  25. This is a fantastic article; thanks! I’ve used it with great success on a few projects, though I’m now coming up short while trying to eliminate some warnings that users of my framework are experiencing.

    SimpleGeo.framework (https://github.com/simplegeo/SimpleGeo.framework) was built using your instructions and works great, modulo a few warnings. SimpleGeo-iPhone (https://github.com/simplegeo/SimpleGeo-iPhone) links to it and when built, I get the following output:

    GenerateDSYMFile /Users/seth/Library/Developer/Xcode/DerivedData/SimpleGeo-aavjdkugnatwwzaedjfdryxbtgeo/Build/Products/Debug-iphoneos/SimpleGeo.app.dSYM /Users/seth/Library/Developer/Xcode/DerivedData/SimpleGeo-aavjdkugnatwwzaedjfdryxbtgeo/Build/Products/Debug-iphoneos/SimpleGeo.app/SimpleGeo
    cd /Users/seth/src/simplegeo/SimpleGeo-iPhone
    setenv PATH “/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Developer/usr/bin:/Users/seth/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin”
    /Developer/usr/bin/dsymutil /Users/seth/Library/Developer/Xcode/DerivedData/SimpleGeo-aavjdkugnatwwzaedjfdryxbtgeo/Build/Products/Debug-iphoneos/SimpleGeo.app/SimpleGeo -o /Users/seth/Library/Developer/Xcode/DerivedData/SimpleGeo-aavjdkugnatwwzaedjfdryxbtgeo/Build/Products/Debug-iphoneos/SimpleGeo.app.dSYM

    warning: (armv6) /Users/seth/src/simplegeo/SimpleGeo.framework/iOS/build/SimpleGeo.build/Release-iphoneos/SimpleGeo.build/Objects-normal/armv6/ASIDataCompressor.o unable to open object file

    If I leave the build directory for the framework intact on my machine, no warnings. Otherwise, I see what everyone else does. strings does not show any part of that path as being present anywhere in the built framework, so I’m at a loss.

    I’ve tried fiddling with the debug information format, etc., but to no avail. Any ideas?

    Thanks a ton!

    seth

  26. Sounds to me like you are not correctly lipoing an universal static library containing arm6/arm7/i386.

  27. Seth, I had the same “unable to open object file” problem. I had to set the flag “GCC_GENERATE_DEBUGGING_SYMBOLS” to NO, that got rid of the warnings.

  28. Thanks for all such great posts as they are really useful.
    I am trying to create universal static framework in Xcode 4 by following your two post, but i am missing a link between creating universal static library and making it as a framework.
    I am able t create universal static library by building the aggregated target. I also added the bundle framework but i am not sure where should it go.
    So the issue i am having is how should a framework bundle should be linked to the aggregated target (lipo’ed library).
    I would really appreciate your help!

  29. This is excellent – thanks. Eventually worked for me, though I wasn’t able to build the project (build option was missing) until I’d done a bit more fiddling. I get a link error if I make the Dead Code Stripping change however (conflicts with -r apparently).

    A related newb question for you – why are the private headers distributed at all?

  30. Private headers are not distributed. Only the ones you mark as public.

  31. I stumbled across this article whilst looking for dynamic loading of bundles in iOS.. is that possible?

    For example, using the OSGI Java framework you can write a relatively simple generic application and provide it with a config file which tells it what bundles are required and where to get them (e.g web) from so by providing a different config file you actually end up with a completely different application (one config file could define bundles used in a game, another config file bundles for an enterprise application).. where the bundles contain both resources (images, text files etc.) and executable compiled code.

    Is there any way to use the techniques mentioned in the article to enable this kind of functionality on iOS? (I know it goes against the ethos of Apples iOS philospohy, but rules are afterall made to be broken)

  32. A bundle is just a folder that looks like a single file. For handling that there is NSBundle. Of course you can add content to your app like that. BUT you cannot dynamically load executable code, that’s prohibited by the Developer Agreement.

  33. i want ask a question,how to use xcode4.2 make a framework? I can not find some setting

  34. Use this template. I use the “fake framework” one, which builds a static library for both simulator and device, melds them together into a universal static library and packages it in a .framework bundle.

    https://github.com/kstenerud/iOS-Universal-Framework

  35. Hi thanks for the steps ..I was able to create the framework using them, but when I distribute the framework and add it to my project, custom methods are not identified.

    For eg. I created NSObject class in my framework called HelloWorld.h where in I have declared a function
    -(void)calc and a function -(void)show

    When I include my framework and import HelloWorld.h file in my project the show method works fine without errors because NSObject also has a show method, but when I try to use calc method it gives me compilation errors saying that calc method was not defined in the interface HelloWorld.

    Can you please help me solve this error..

  36. I am appreciate you work. Iam go through step by step but when i have build the framework was create with error. When i sa the header folder of the framework then it’s blank. I am getting following error. can you please help me?

    ** BUILD FAILED **

    The following build commands failed:
    Ld build/Release-iphoneos/exampleBundle.framework/exampleBundle normal armv7
    (1 failure)
    Build settings from command line:
    SDKROOT = iphonesimulator6.0

    === BUILD NATIVE TARGET exampleBundle OF PROJECT TestFramework2 WITH CONFIGURATION Release ===
    Check dependencies
    No architectures to compile for (ARCHS=, VALID_ARCHS=armv7).

    ** BUILD FAILED **

  37. I was getting “Dsymutil” Warnings for each class (.o object file). The solution is to go into the target Build Settings for the Framework.

    Xcode Project Build Settings: Search for: “Generate Debug Symbols” and set to “NO”
    Destroying Ambiguity: “GCC_GENERATE_DEBUGGING_SYMBOLS” =≈ “Generate Debug Symbols”