Compiling a Framework

top

3. Create a Framework Target:

top
OK, let’s create a target to compile our framework. Click on the icon of your project in the project navigator at the left and hit the button “Add Target”. A new window will come up. Now is our first trick. Instead to create a “Cocoa Touch Static Library” or a “Cocoa Framework” we will create a “Bundle” target.

A Bundle? Really? Yes! I can explain. A “Cocoa Framework” target can’t be compiled to armv6/armv7 and Xcode doesn’t allow us to use “Static Libraries” in a “Cocoa Framework”, so we can’t use this target. On the other hand, we can’t use “Cocoa Touch Static Library” either, because it doesn’t use the framework structure that we want.

Now, the Bundle target could be the best choice. It can hold any file we want, we can compile source code inside it and… we can turn it into a framework. To say the truth, almost all “Framework & Library” targets could be turned into a framework too, even the “Cocoa Touch Static Library”, throughout this article you probably will figure out why. For now, let’s create a Bundle target.

Creating Universal Framework to iPhone iOS  2_休闲

Create a Bundle target rather than Cocoa Touch Static Library.


4. Bundle Setup:

top
Creating Universal Framework to iPhone iOS  2_职场_02It’s time to make all the necessary changes to the Bundle target. First of all, make sure you have cleaned up all the default files from the Bundle target. Remove the linked frameworks (you can find it clicking in the “Build Phase” tab and then they are in the section “Link Binary With Libraries“) and delete the Preference List file (.plist), the pre-compiled headers (.pch) and the language files.

I’m sure you already know this, but just to reinforce, here is the Build Setting screen, you can find it by clicking on the project icon in the left project navigator and then clicking in the “Build Setting” tab.

Creating Universal Framework to iPhone iOS  2_Creating Universal F_03

You must make a special Build Setting to turn a Bundle into a framework.

Here is our second great trick, or should be better to say “tricks”. Let’s change the “Build Setting” following this list:

  • ArchitecturesStandard (armv6 armv7) (the values for this property depend on the value of the item bellow).
  • Base SDKLatest iOS (iOS X.X) (in the X.X will appear the number of the lastest iOS SDK installed on your machine).
  • Build Active Architecture OnlyNO (this is a very important set, if it’s YES then we can’t compile to armv6 and armv7 at the same time).
  • Supported Platformsiphonesimulator iphoneos.
  • Valid Architecture$(ARCHS_STANDARD_32_BIT) (it’s very important to be exactly this value, seems there is a bug in Xcode 4, once you set i386 as supported platforms to iOS, it can’t be removed anymore and it could generate errors in your project. So, to avoid any architectures error, use this value).
  • Installation Directory: [optional change] I like to set this to $(BUILT_PRODUCTS_DIR), but this is not really relevant to our purposes here. You can set it to a path of your choice.
  • Mac OS X Deployment TargetCompiler Default.
  • Dead Code StrippingNO.
  • Link With Standard LibrariesNO.
  • Mach-O TypeRelocatable Object File. This is the most important change. Here, we instruct the compiler to treat the Bundle as a relocatable file, by doing this, we can turn it into a framework with the wrapper setting.
  • Other Linker Flags: [optional change] -ObjC, could be good make sure the compiler understands what’s the language it is compiling for, this can reduce the warnings at the compilation.
  • Info.plist Fileempty, remove any value from this field.
  • Wrapper Extensionframework. Here we change the Bundle to a Framework. To Xcode, frameworks is just a folder with the extension .framework, which has inside one or more compiled binary sources, resources and some folders, a folder, usually called Headers, contains all the public headers.
  • Precompile Prefix HeaderNO.
  • Prefix Headerempty, remove any value from this field.
  • Generate Debug SymbolsNO (this is a very important setting, otherwise your framework will not work on another computer/profile).


5. Adding code and resources to the Bundle (Framework)

top
It’s time to place the content in our framework and define the public headers. To do that, with the Bundle target selected, click on the “Build Phase” tab. At bottom, hit the button “Add Phase” and then “Add Copy Headers“.

Open the recently created “Copy Headers” section and separate your public headers from the private or project headers. The difference here is:

  • Public: Headers that other developers must know in order to work with your framework. In the final framework product, these headers will be visible even to Xcode.
  • Private: Headers that is not necessary to other developers, but is good for consult or for reference. These headers will not be visible to Xcode, but will be in the framework folder.
  • Project: Headers that the other developers nor Xcode have access. In reality these headers will not be placed in the final product, this is just to instruct the compiler to create your custom framework.

Now, open the “Compile Source” section and put there all your .m.c.mm.cpp or any other compilable source file. If your framework include not compilable files, like p_w_picpaths, sounds and other resources, you can place them in the “Copy Bundle Resources” section, in the example of this article, I used no resources. This is how your “Build Phase” will looks like:

Creating Universal Framework to iPhone iOS  2_休闲_04

Define your compilable source and the headers.


6. Schemes Setup

top
This is an important change to compile our framework. You don’t need to compile it in the Debug modes, we just need the Release mode, because if you have any Macro in your code which works only in Debug mode, like DEBUG macro, it will assume the configuration of the product that the other developers will make. So to us now, only the Release is important.

You can change it accessing the menu Product > Manage Schemes… or by the schemes short cut: the drop list in front at the Build/Run bottom in the top left corner of the Xcode window.

At this point could be a good idea to delete all the current schemes and then press the button “AutoCreate Schemes Now“. The important is select your framework target and hit the button “Edit” at the bottom. In the next window, change the “Build Configuration” from Debug to Release. Change to Release in all the situations: Run, Test, Profile, Analyze and Archive. By doing this, we ensure that our framework target will always compile the Release version.

Creating Universal Framework to iPhone iOS  2_休闲_05

Build the custom framework only to Release.


7. Building the Framework

top
OK, here is the annoying step of this process. Until now, I don’t figure out a more easy an elegant way to do this. You must to compile this target twice: one to iOS Device (this will compile for the architectures armv6/arvm7) and to iOS Simulator (can be iPhone or iPad with any SDK, doesn’t matter, the simulator always will compile for the architecture i386).

I’ve tried another way, like create an “integrator” and I placed two targets to its “Target Dependencies”, but trust me, it’s worst, because you need to change 2 targets instead 1 in cases when you change the framework content. Besides, the dependencies will assume the architectures of the “integrator”, so you need to compile twice too. Anyway… the best solution I found until now is use only 1 target and build it twice. If you find a better one, please tell me.

Creating Universal Framework to iPhone iOS  2_Creating Universal F_06

Compile the framework target twice: iOS Device and Simulator.

After the compile complete, you can see in the “Products” folder in the Project Navigator at the left, that your framework product now is active. Right click on it and hit “Show in Finder“. Take a look at this product, its is your iOS Framework! Great!

But we’re not done yet. You can test this product in other applications if you want, but it will only work to one architecture. We must to create an Universal version of this product. If you look around your framework product in the Finder, you’ll see that it is in a folder called “Release-iphoneos” (if you didn’t change the destination folder in the Build Settings). And if you have compiled to Simulator too, you’ll see another folder called “Release-iphonesimulator” which contains the same framework product, but this version is for architecture i386.

So, let’s join both products into one.


Building the Universal Framework

top

8. Creating Universal Target:

top
To join both architectures products into one, we must to use the Lipo Tool. It’s a tool which comes with iOS SDK, just to know, it is in “/Platforms/iPhoneOS.platform/Developer/usr/bin”, its file name is “lipo”. But we don’t need to know this path, Xcode can deal with it to us.

To use the Lipo Tool we’ll need to create a “Run Script” at the “Build Phase“, you can create it in your previously Bundle Target, but my advice is to create another target. I say to create another target to avoid compiling errors. This script will need to use “Release-iphoneos” and “Release-iphonesimulator” folders, so if the folders or products inside them not exist yet, the compiler will generate errors.

Let’s add a new target, hit the “Add Target” button, just as you did with Bundle Target. At this time a good choice is the “Aggregate” target. It doesn’t create any product directly, its purposes is just to aggregate another targets and run some scripts, exactly what we want!

Creating Universal Framework to iPhone iOS  2_休闲_07

Use the "Aggregate" target to construct a run script.