top
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.
Create a Bundle target rather than Cocoa Touch Static Library.
top
It’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.
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:
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:
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 images, 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:
Define your compilable source and the headers.
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.
Build the custom framework only to Release.
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.
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.
top
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!
Use the "Aggregate" target to construct a run script.