# iphone中自定义Universal Framework

let's start.

iOS支持framework?真的吗？

Universal, Dynamic与Static相关概念

1。创建一个新工程

2。添加framework的类

3.创建一个frame target

4。Bundle设置

5。添加代码与资源到该Bundle(Framework)

6。Schemes设置

7。Building该Framework

Building为Universal Framework

8. 创建一个Universal Target

9。Lipo工具与脚本的应用

10.测试

iOS支持framework?真的吗？

A framework is a hierarchical directory that encapsulates shared resources, such as a dynamic shared library, nib files, image files, localized strings, header files, and reference documentation in a single package.

http://db-in.com/blog/2011/05/creating-universal-framework-to-iphone-ios/

Hello my friends,

I felt a good impulse to write a little article about this theme, because there are few poor informations about it around the web. In many cases, are wrong or incomplete informations. So if you want to write a framework to iPhone and distribute it, you are in the right place!

I’ll treat here about how to construct an Universal Framework to iOS, what are the necessary configurations and everything else related to. We’ll focus on Xcode 4, but this is also valid to Xcode 3.x.

Let’s start!

## Framework on iOS? Really?

Ok buddies, let’s make something clear, many people had said:”iOS doesn’t support custom Frameworks!”, “Custom Framework is not allowed at iOS!”, “Doesn’t exist custom Framework on iOS!” and many other discouraging things like these. Look, I’ve made many frameworks and worked with many others, I don’t believe that is really impossible to use a Framework on iOS. According to my experience and knowledge about Frameworks, it’s absolutely feasible a custom Framework on iOS Devices. If we think more about the issue we can find an elegant solution, right? First, let’s understand what a Framework really is, here is the definition of framework by Apple’s eyes:

A framework is a hierarchical directory that encapsulates shared resources, such as a dynamic shared library, nib files, image files, localized strings, header files, and reference documentation in a single package.

Doesn’t make many sense something with this description not be allowed in iOS, thinking in architecture and structure. Apple also says:

A framework is also a bundle and its contents can be accessed using Core Foundation Bundle Services or the Cocoa NSBundle class. However, unlike most bundles, a framework bundle does not appear in the Finder as an opaque file. A framework bundle is a standard directory that the user can navigate.

Good, now thinking about iOS security, performance and size, the only thing in a Framework definition which doesn’t fit in iOS technology is the “dynamic shared library”. The words “dynamic” and “shared” are not welcome in the iOS architecture. So the Apple allows us to work and distribute something called “Static Library“. I don’t like it! It’s not so easy as a Cocoa Framework, if a developer got your Static Library, he needs to set a header path too, or import the header files… it’s not useful, that’s a shit!

Well, so a Framework concept is absolutely compatible with iOS, except by the “dynamic shared library”, on the other hand Apple says that a “static library” is OK for iOS. So if we replace the “dynamic shared libraries” by a “static library” we could construct a Custom Framework to iOS, right?

Right!!!!

This is exactly what we’ll make in this article, let’s construct a Framework with Static Library, by the way, an Universal Framework.

## Understanding Universal, Dynamic and Static concepts

• Universal: Which works perfect on all architectures. iOS devices uses armv6 and armv7, iOS simulator on MacOS X uses i386.
• Dynamic: The compiler doesn’t include the target files directly. The classes/libraries are already pre-compiled (binary format) and lies on the system path. Besides, the dynamic libraries can be shared by many applications. This is exactly what Cocoa Framework is.
• Static: It represents that classes/libraries which is compiled by the compiler at the build phase. These files can’t be shared by other applications and relies on application path.

Simple as that. If you need more informations about Dynamic VS Static libraries, try this Apple’s Documentation.

No more concepts, hands at work!

## Constructing a Framework Project

### 1. Create the Project:

I want to show you step by step of the entire process, so let’s start with the most basic, create an iOS project. You can choose one application template in Xcode, this is not really so important, but remember to choose one template which could test your Framework code before export it.

Create an application project.

### 2. Framework Classes:

Now it’s time to create (or import) your framework classes, just as you are used to in any other application. Remember that organization is 80% of a good framework, so try follow all the Apple advices to create your classes names, methods, properties, functions, etc.

Remember to create an import header to make everything more simple and organized to the user of your framework. Remember to write this header file with a framework notation, just as shown in the image bellow. Also remember to create your classes taking care to hide the classes which should not be visible to the other developers (users of your framework). We will set the public and private headers soon, but it’s very important to you protect the “core” classes, I mean, that classes which you don’t want to make visible to other developers.

For those private classes, you could import their header inside the “.m” (or .mm, .cpp, etc) file of a public class, by doing this you protect the header of private classes. Well, I know you probably already know that, I’m saying just to reinforce.

After you have all the classes (and also other files, like images, sounds, etc.), you are ready to compile a custom framework. In the next step, we’ll create a target to compile our framework, this will be our first big trick!

## Compiling a Framework

### 3. Create a Framework Target:

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.

### 4. Bundle Setup:

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:

• ArchitecturesStandard (armv6 armv7).
• 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 very important to be NO, otherwise we can’t compile a framework version to the Simulator).
• 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.
• 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.
• Prefix Headerempty, remove any value from this field.

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

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“.

• 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 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:

### 6. Schemes Setup

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.

### 7. Building the Framework

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.

## Building the Universal Framework

### 8. Creating Universal Target:

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.

### 9. Lipo Tool Script:

We are almost there, now it’s time to create the Run Script to use the Lipo Tool. Well, you probably will need to make some changes in the code bellow to fit it to your framework name or folders (if you set different folders to build for). Here is the script to use:

XCODE SCRIPT TO LIPO TOOL

.
if [ "${ACTION}" = "build" ] then # Set the target folders and the final framework product. INSTALL_DIR=${SYMROOT}/FI.framework
DEVICE_DIR=${SYMROOT}/Release-iphoneos SIMULATOR_DIR=${SYMROOT}/Release-iphonesimulator

# Create and renews the final product folder.
mkdir -p "${INSTALL_DIR}" rm -rf "${INSTALL_DIR}"
rm -rf "${INSTALL_DIR}/FI" # Copy the header files to the final product folder. ditto "${DEVICE_DIR}/FI.framework/Headers" "${INSTALL_DIR}/Headers" # Use the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product. lipo -create "${DEVICE_DIR}/FI.framework/FI" "${SIMULATOR_DIR}/FI.framework/FI" -output "${INSTALL_DIR}/FI"
fi
.

Lipo script to join products for architectures i386 and armv6/armv7.

Now build the Aggregate target. At this time doesn’t matter if you build for iOS Device or Simulator, because all that it will make is run the script, nothing more. The above script creates the final Universal Framework at the ${SYMROOT} path, this is the same path of your Bundle target. So you can right click on the Framework product in the “Products” again and hit “Show in Finder”. Move to the level with “Release-iphoneos” and “Release-iphonesimulator” folders. Now, there is a new folder with the “.framework” extension, it’s your Universal Framework to iOS! Congratulations! ## Testing your Universal Framework ### 10. Testing: To test your Universal Framework could be a good idea take it from that folder and place it in another one more easy to find, like the Desktop or ~/Library/Frameworks. Well, to avoid take it from that folder every time, you can change the Run Script above. Just change the “INSTALL_DIR” variable and set a path of your choice, make sure you choose a path with the write permissions. A good choice could be use the Xcode variable${SRCROOT} which represents your project folder root, so you can set ${SRCROOT}/Products or something like that. Anyway, with your Universal Framework in an “user friendly path”, create a new Xcode project, select the Application target and go to “Build Phase” tab. Open the section “Link Binary With Libraries” and hit the “+” to add a new Framework. Click the “Add Other…” button and select your Universal Framework. Remember, you must to select the “.framework” folder. After the importing, Xcode will know everything about your custom framework. So try to write some code using your framework classes. Remember to import your Framework Principal Header as a framework notation. Xcode will use your public headers in the Code Completion. Xcode knows your public headers and can use them in the Code Completion. I like to use the Xcode Midnight Color Theme, so the classes of the project are green and the framework classes are blue. I love blue classes! ## Conclusion Well done, my friends! As we are used, let’s make a final review and take care with some possible problems. • In a common Xcode project, create a Bundle target. • Make the necessary setup, place your sources, headers and resources in it and finally change the its Schemes configuration. • Compile to iOS Device (armv6/armv7) and to Simulator (i386). • Create an Aggreate target and place a Run Script in it. • Enjoy your real Universal Framework to iOS! To finish I want to talk about some issues that I had: • Take special care with the architectures. Don’t write the i386 directly in the Valid Architectures setting, always use the$(ARCHS_STANDARD_32_BIT). Plus, always set the Build Active Architecture Only to NO.
• Take care with your classes structure. If you set, for example, the ClassB.h as a Project or Private header, but in your code you import it into a Public header, this will cause conflicts.
• Take care with the Install Directory and Skip Install settings. They can generate warning if the folders permissions or paths are not right set.
• Finally, take a special care with the Lipo Tool script. It could generate warnings and/or errors if the paths was not found or the folder permissions was not right set.

This is all, buddy.
As I told you, the annoying thing is that you need build 3 times to generate a new Universal Framework: 2 x Bundle target (one to iOS Device and other to the Simulator) and 1x to the Aggregate target (which will run a script). But by my experience, this is not to much, you will create the final product only few times. At the great majority, you will test your Framework classes directly with the Application target in your Framework Xcode Project.

Enjoy your real Framework to iOS!

See you soon!