标签:
Hello my friends,
Due to some bugs and questions with the old tutorial, I‘m creating this new one, much more simpler and less bugs than the another one. I‘ll not post the old link here because everything you need to know you can find right here.
Nowadays, exist few alternatives to create a Framework to iOS, changing the default Xcode Script, which could not be a good choice if you want to publish the APPs constructed with your custom Framework. I‘ll treat here about how to construct an Universal Framework to iOS, using the default tools from Xcode.
Let‘s start!
Here is a little list of contents to orient your reading:
List of Contents to this Tutorial | |
---|---|
You can download the template instead of doing it manually:
Unzip it and place it at /Library/Developer/Xcode/Templates/Project Templates/
(create the path it needed)
top
First off, I want to make sure you understand what this Framework to iOS can do, this can safe your time reading this article:
top
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 this 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.
top
Simple answer:
Simple as that. If you need more informations about Dynamic VS Static libraries, try this Apple‘s Documentation.
No more concepts, hands at work!
top
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 important, but remember to choose one template which could test your Framework code before export it.
Remember to create an "import header" to make everything simpler 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.
Remember that organization is 90% of a good framework, so try follow all the Apple advices to create your classes names, methods, properties, functions, etc.
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 how. For now, let‘s create a Bundle target.
top
It‘s time to make all the necessary changes to the Bundle target. Different than the old tutorial. You don‘t need to clean up anything. Just know that everything else will be ignored (linked frameworks .plist files, .pch etc...).
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.
Here is our second great trick, or should be better to say "tricks". Let‘s change the "Build Setting" following this list:
IMPORTANT: Since the Xcode 4.x the architectures armv6 has no longer support. So, to create a real Universal Framework we must make a small "hack":
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. Later, when we generate the final framework, all your resources will be placed in a folder called "Resources", but you can change it. That folder is very important, because it will be part of the path to retrieve your resources from the framework product.
Tip: To add many files at once, click on the “+” button and write the files’ extension on the search field. For example “.m”, “.c”, “.cpp”, “.h”, etc. This can save a lot of time.
This is how your "Build Phase" will looks like:
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.
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/or run some scripts, exactly what we want! To use the Lipo Tool we‘ll need to create a "Run Script" at the "Build Phase".
top
This will be our greatest trick. The following script will make everything we need. It will compile the Framework Target to iOS Device and Simulator at once, will merge them with Lipo tool and will organize a good Framework Bundle structure.
Copy and paste this on your "Run Script" phase:
Xcode Script to Lipo Tool |
---|
# Sets the target folders and the final framework product. FMK_NAME="FI" FMK_VERSION="A" # Install dir will be the final output to the framework. # The following line create it in the root folder of the current project. INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework # Working dir will be deleted after the framework creation. WRK_DIR=build DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework # Building both architectures. xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator # Cleaning the oldest. if [ -d "${INSTALL_DIR}" ] then rm -rf "${INSTALL_DIR}" fi # Creates and renews the final product folder. mkdir -p "${INSTALL_DIR}" mkdir -p "${INSTALL_DIR}/Versions" mkdir -p "${INSTALL_DIR}/Versions/${FMK_VERSION}" mkdir -p "${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources" mkdir -p "${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers" # Creates the internal links. # It MUST uses relative path, otherwise will not work when the folder is copied/moved. ln -s "${FMK_VERSION}" "${INSTALL_DIR}/Versions/Current" ln -s "Versions/Current/Headers" "${INSTALL_DIR}/Headers" ln -s "Versions/Current/Resources" "${INSTALL_DIR}/Resources" ln -s "Versions/Current/${FMK_NAME}" "${INSTALL_DIR}/${FMK_NAME}" # Copies the headers and resources files to the final product folder. cp -R "${DEVICE_DIR}/Headers/" "${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers/" cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/" # Removes the binary and header from the resources folder. rm -r "${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/Headers" "${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/${FMK_NAME}" # Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product. lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}" rm -r "${WRK_DIR}"
Now build the Aggregate target. Doesn‘t matter you build for iOS Device or Simulator, this script will create a working folder, compile the framework target twice in there (device + simulator) and will output a folder called "Products" located in the project root folder. There is your Universal Framework to iOS!
Congratulations!
top
To test your Universal Framework, 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. Remember to import your Framework Principal Header as a framework notation. Xcode will use your public headers in the Code Completion.
Let‘s understand what happens until here:
When we set the "Mach-O Type" to "Relocatable Object File" the Xcode understand that everything related to that package will be like a "Binary Archive" like a ZIP. But that archive must be compiled again in new projects.
Then, when we create a Framework Bundle structure, Xcode understand that everything inside it is organized in folders like "Headers" and . But, as any other external bundle, to retrieve the resources you must load the external bundle. To iOS this could be an annoying step, however our Framework structure can help. Just Click and Drag on your Framework icon from "Project Navigator" to the "Copy Bundle Resources". By doing this all the resources in your Framework will be copied to your Application Main Bundle.
Now, to retrieve the resources, make use of the main bundle, just as you are used to:
Framework Bundle |
---|
[[NSBundle mainBundle] pathForResource:@"FI.framework/Resources/FileName" ofType:@"fileExtension"];
top
Well done, my friends! As we are used, let‘s make a final review and take care with some possible problems.
One last advice: 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.
And one last tip: Notice in the sample project I removed the scheme for the "Bundle Target". We don‘t need that scheme any more, because the new script will manage the compilation to us.
That‘s all, buddies.
Enjoy your Framework to iOS!
Thanks for reading,
标签:
原文地址:http://www.cnblogs.com/zhaoguowen/p/4489506.html