iOS-Universal-Framework
文件大小: unknow
源码售价: 5 个金币 积分规则     积分充值
资源说明:An XCode project template to build universal frameworks (arm7, arm7s, and simulator) for iOS / iPhone.
FINALLY!
=======

With Xcode 6, Apple has added iOS framework support to their build tools, so this repo can at last be retired!

Please use Apple's framework target for all new projects, as it is less hacky and is supported by Apple themselves.



iOS Universal Framework Mk 8
============================

An XCode project template to build universal (arm6, arm7, and simulator)
frameworks for iOS.

![screenshot](https://github.com/kstenerud/iOS-Universal-Framework/raw/master/screenshot.png)

By Karl Stenerud


Notes
----------

### 2013-10-14:

#### Mk 8 is now out of beta!

I haven't been able to solve the problem of deeply nested projects within projects,
but the new python scripts have been working in my other projects for over a year now and
are quite stable for 90% of use cases.

Unfortunately, I don't have the time to solve the last 10% of use cases. As a compromise,
I've created a branch "mk7" which contains the shell script version of the build system.
If Mk8 doesn't work for your unique case, give Mk7 a try.

Development will continue in order to keep things working for the other 90% of use cases.

If you can help, please feel free to contact me or send pull requests. All scripting is done
in Python now. All template development happens within the "devel" directory. build.py
builds the templates and all template source files are in "src".


### 2012-06-16:

#### Updating your project to use the new scripts

You can now update existing projects to use the newest build scripts.
Running the **update_project.py** script will replace your project's universal
framework build script with the script in devel/src/BuildFW.py.

**Before upgrading, please back up your project file!**

##### Steps to Upgrade (Mk 7 or earlier):

If your project was built using Mk 7 or earlier, delete the first two universal
framework build scripts. The first will be right after "Target Dependencies"
and starts with the following (or something close):

    set -e
    
    set +u
    if [[ $UFW_MASTER_SCRIPT_RUNNING ]]
    then
        # Nothing for the slave script to do
        exit 0
    fi
    set -u

The second script is after "Copy Bundle Resources" and starts with the
following (or something close). Note that this script may not exist in very
early versions of the framework project:

    HEADERS_ROOT=$SRCROOT/$PRODUCT_NAME
    FRAMEWORK_HEADERS_DIR="$BUILT_PRODUCTS_DIR/$WRAPPER_NAME/Versions/$FRAMEWORK_VERSION/Headers"
    
    ## only header files expected at this point
    PUBLIC_HEADERS=$(find $FRAMEWORK_HEADERS_DIR/. -not -type d 2> /dev/null | sed -e "s@.*/@@g")

The final script (the one you want to keep) will start with something similar
to the first script you deleted.

Now proceed with the next steps below.


##### Steps to Upgrade (All versions)

* Make sure the top of the "Run Script" phase for the universal framework
  script starts with the following comment: "# TAG: BUILD SCRIPT". If it
  doesn't, add it in!

* Close your project

* Run the project update script from shell:
    $ ./update_project.py ~/Projects/MyProj/MyProj.xcodeproj/project.pbxproj

* Reopen your project

The project update script will create a backup (project.pbxproj.orig) of the
old project file. To disable this behavior, use the "-n" switch.


### Selecting Framework Type

The script now requires you to select which kind of framework (normal or
embedded) you will be creating, using the **config_framework_type**
configuration variable. Only the selected framework type will be created and
shown to the user.

**Note:** Xcode requires the normal framework dir to exist, so when building an
embedded framework, the script simply creates a symlink to the copy inside the
embeddedframework. Be sure to tell your users not to to copy the regular
"framework" symlink by mistake!


### 2012-06-12:

#### New Build Process

When you build normally (by selecting Build or CMD-B), the project will **NO
LONGER** build a universal framework. It will build for the **CURRENT
ARCHITECTURE ONLY**!

To build a universal framework, you must select **Archive** from the
**Product** menu. Upon completing the archive build, it will automatically open
the folder containing the fully built framework.

This cuts the compilation time down by 2/3, since it no longer has to do a full
build process when building as a dependency.

#### Building From Command Line

Since "archive" is not a supported xcodebuild build action, you must specify
the env variable "UFW_ACTION=archive" in your xcodebuild command to build it as
a universal framework.

To avoid opening the destination folder when building from command line, set
the env variable "UFW_OPEN_BUILD_DIR=False" in your xcodebuild command.

### Only One Script

The initial beta version had 2 scripts: a clean script and a build script. Mk 7
has 3 scripts. With the new build process there is only need for one script.


### Older stuff:

#### Xcode Bugs and their Workarounds

When Xcode creates the initial header and module file for a framework, the
header file won't be included as a member of the framework target (This is a
bug in Xcode; it does the same thing with Mac frameworks), so you need to do
this manually. In **Build Phases** under **Copy Headers**, click the + and add
the header, then drag it to the **Public** section.

The **Run Script** build phases will have the option **Show environment
variables in build log** checked. A bug in Xcode causes it to ignore the
template setting and leave it checked always. This can cause issues when
diagnosing a build failure because Xcode will only show the first 200 log
entries in a build phase, most of which are taken up by spitting out all of
the environment variables! So be sure to turn it off manually.

So to sum up, when starting a new framework project, always do the following:

* Manually add the header file it creates to your build target and mark it
  public.
* Uncheck **Show environment variables in build log** in all **Run Script**
  build phases.


Why a Framework?
----------------

Distributing libraries in a developer-friendly manner is tricky. You need to
include not only the library itself, but also any public include files,
resources, scripts etc.

Apple's solution to this problem is frameworks, which are basically folders
that follow a standard structure to include everything required to use a
library. Unfortunately, in disallowing dynamically linked libraries in iOS,
Apple also removed static iOS framework creation functionality in XCode.

Xcode is still technically capable of building frameworks for iOS, and with a
little tweaking it can be re-enabled.

Static frameworks are perfectly acceptable for packaging code intended for the
app store. Despite appearances, it's just a static library at the core.



Kinds of Frameworks
-------------------

#### Dynamic Framework

A dynamic framework is designed to be installed in your operating system and
shared by many programs. By default, Xcode only supports dynamic frameworks,
and only for Mac since you can't use dynamic frameworks in iOS.

#### Static Framework

A static framework gets linked into your app like a static library would.
However, Xcode doesn't include support for static frameworks. These templates
add in that support. Frameworks are superior to libraries because they can
include code as well as public headers in a single package.

#### Embedded Framework

Although frameworks are an improvement over libraries, Xcode ignores any
resources contained within frameworks. So if you have xibs, images, sounds, or
other files in your framework, Xcode won't see them. An embedded framework is
a way to trick Xcode into seeing the included resources. As far as Xcode is
concerned, they are simply folders, and so there are a few minor issues with
embedded frameworks:

* They don't show up in the **Products** group.

* When you delete an embedded framework from a project, Xcode will not delete
the outer folder (XX.embeddedframework), so if you try to re-add later, it
will complain. You need to manually delete the XX.embeddedframework folder
manually using Finder.

* Things get a little tricky when you have a framework project as a dependency
  if your framework has resources that the parent project needs. You may need
  to manually add the resources to the parent or sibling project.




Choosing Which Template System to Use
-------------------------------------

In this distribution are two template systems, each with their strengths and
weaknesses. You should choose whichever one best suits your needs and
constraints (or just install both).

The biggest difference is that Xcode can't build real frameworks unless you
install the static framework xcspec file inside the Xcode app, which might be
a dealbreaker for some (this applies to the *PROJECT*, not the framework
itself).


### Short decision chart for the impatient ###

Note: Both types will build the exact same binary. The only difference is in
how Xcode treats the project.

* I don't want to modify Xcode: **Fake framework**

* I'm just distributing the final framework binary (not the project):
  **Either kind**

* I'm distributing my framework **project** to other developers who may not
  want to modify Xcode: **Fake framework**

* I need to set up the framework project as a dependency of another project
  (in a workspace or as a child project): **Real framework**
  (or **Fake framework**, using the -framework trick - see below)


### Fake Framework ###

The fake framework is based on the well known "relocatable object file" bundle
hack, which tricks Xcode into building something that mostly resembles a
framework, but is really a bundle.

The fake framework template takes this a step further, using some scripting to
generate a real static framework (based on a static library rather than a
relocatable object file). However, the framework's **project** still defines
it to be of type 'wrapper.cfbundle', which makes it a second class citizen
according to Xcode.

So while it produces a proper static framework that works just as well as a
"real" static framework, things can get tricky when you have dependencies.

#### The problem with dependencies ####

If you're just setting up a standalone project, then you're not using
dependencies, so there's no problem.

If, however, you use project dependencies (such as in workspaces), Xcode won't
be happy. The fake framework won't show up in the list when you click the '+'
button under "Link Binary With Libraries" in your main application project.
You can manually drag it from "Products" under your fake framework project to
add the dependency.

**Note:** In older versions of Xcode, you'd get warnings like the following:

    warning: skipping file '/somewhere/MyFramework.framework'
    (unexpected file type 'wrapper.cfbundle' in Frameworks & Libraries build phase)

This would be followed by linker errors for anything in your fake framework.
As of Xcode 4.3.1, this doesn't seem to happen anymore.

If you do encounter this issue, you can work around it by adding a "-framework"
switch with your framework's name in "Other Linker Flags" in the project that
uses the framework:

    -framework MyFramework

It won't get rid of the warning, which is annoying, but it does link properly.


### Real Framework ###

The real framework is real in every sense of the word. It is a true static
framework made by re-introducing specifications that Apple left out of Xcode.

In order to be able to build a real framework project, you must install an
xcspec file inside the Xcode installation.

If you are releasing a **project** (rather than the built product) that builds
a real framework, anyone who wishes to **build** that framework must also
install the xcspec file (using the install script in this distribution) so
that their Xcode can understand the target type.

Note: If all you're doing is distributing the fully built framework, and not
the framework's project, then the end user doesn't need to install anything.

I've submitted a report to Apple in the hopes that they'll update the
specification files in Xcode, but that could take awhile.
[OpenRadar link here](http://openradar.appspot.com/radar?id=1194402)



Upgrading from previous iOS-Universal-Framework versions
--------------------------------------------------------

If you are upgrading from iOS-Universal-Framework **Mk 6 or earlier** and were
using the **Real Static Framework**, and are still using **Xcode 4.2.1** or
earlier, please run **uninstall_legacy.sh** first to remove any patches that
were previously applied to Xcode, then run **install.sh** and restart Xcode.

If you are using **Xcode 4.3** or later, just run **install.sh** and restart
Xcode.



Installing the Template System
------------------------------

To install, run the **install.sh** script in either the "Real Framework" or
"Fake Framework" folder (or both).

Now restart Xcode and you'll see **Static iOS Framework** (or **Fake Static
iOS Framework**) under **Framework & Library** when creating a new project.

To uninstall, run the **uninstall.sh** script and restart Xcode.



Creating an iOS Framework Project
---------------------------------

1. Start a new project.

2. For the project type, choose **Static iOS Framework** (or **Fake Static
   iOS Framework**), which is under **Framework & Library**.

3. Optionally choose to include unit tests.

4. Add the auto-generated header file to the **Public** section of the
   **Copy Headers** build phase (workaround for Xcode bug).

5. Turn off **Show environment variables in build log** for both
   **Run Script** build phases (workaround for Xcode bug).

6. Add your classes, resources, etc with your framework as the target.

7. Any header files that need to be available to other projects must be
   declared public. To do so, go to **Build Phases** in your framework
   target, expand **Copy Headers**, then drag any header files you want to
   make public from the **Project** or **Private** section to the **Public**
   section.

8. Any static libraries or static frameworks that you'd like to have linked
   into your framework must be included in the **Link Binary With Libraries**
   build phase. Be careful doing this, however, as it can cause linker issues
   if the users of your framework also try to include the same library in
   their project for other purposes.


Building your iOS Framework
---------------------------

1. Select your framework's scheme, iOS Device target.

2. Under **Product**, select **Archive**.

3. When the build finishes, it will open the folder containing the framework
   and embedded framework variants in Finder.

There will be two folders in the build location: **(your framework).framework**
and **(your framework).embeddedframework**

If your framework has only code, and no resources (like images, scripts, xibs,
core data momd files, etc), you can distribute **(your framework).framework**
to your users and it will just work.

If you have included resources in your framework, you **MUST** distribute
**(your framework).embeddedframework**.



Using an iOS Framework
----------------------

iOS frameworks are basically the same as regular dynamic Mac OS X frameworks,
except they are statically linked.

To add a framework to your project, simply drag it into your project.
When including headers from your framework, remember to use angle bracket
syntax rather than quotes.

For example, with framework "MyFramework":

    #import 



Template Development
--------------------

If you're interested in tinkering with the templates themselves, I've changed
the way they are generated. There are now template metatemplates, which are
used to build the Xcode templates. Inside the devel folder there is a script
**build.py**, which rebuilds the templates under **Fake Framework** and
**Real Framework**. The source files are under src:

* **CleanFW.py**: The project clean script (the first script in a framework project)
* **BuildFW.py**: The project build script (the second script in a framework project)
* **FakeFrameworkTemplateInfo.plist**: The fake framework metatemplate
* **RealFrameworkTemplateInfo.plist**: The real framework metatemplate


Troubleshooting
---------------

### Headers Not Found ###

If Xcode can't find the header files from your framework, you've likely
forgotten to make them public. See step 7 in **Creating an iOS Framework Project**


### No Such Product Type ###

If someone who has not installed iOS Universal Framework in their development
environment attempts to build a universal framework project (for a real
framework, not a fake framework), they'll get the following error:

    target specifies product type 'com.apple.product-type.framework.static',
    but there's no such product type for the 'iphonesimulator' platform

Xcode requires some modification in order to be able to build true iOS static
frameworks (see the two diff files in the "Real Framework" folder of this
repository for the gory details), so please install it on all development
machines that will build your real static framework projects (this
isn't needed for users of your framework, only for builders of the framework).


### The selected run destination is not valid for this action ###

Sometimes Xcode gets confused and loads the wrong active settings. The first
thing to try is restarting Xcode. If it still fails, Xcode generated a bad
project (this can happen with any kind of project due to a bug in Xcode 4).
If this happens, you'll need to start over and create a new project.


### Linker Warnings ###

The first time you build your framework target, XCode may complain about
missing folders during the linking phase:

    ld: warning: directory not found for option
    '-L/Users/myself/Library/Developer/Xcode/DerivedData/MyFramework-ccahfoccjqiognaqraesrxdyqcne/Build/Products/Debug-iphoneos'

If this happens, simply clean and rebuild the target and the warnings should
go away.


### Core Data momd not found ###

Xcode builds managed object model files differently in a framework project than
it does in an application project. Instead of creating a .momd directory
containing VersionInfo.plist and the .mom file, it simply creates the .mom file
in the root directory.

This means that when initializing your NSManagedObjectModel from a model in an
embedded framework, you must specify your model URL with an extension of "mom"
rather than "momd":

    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyModel" withExtension:@"mom"];


### Unknown class MyClass in Interface Builder file. ###

Since static frameworks are statically linked, the linker strips out any code
it thinks is not being used. Unfortunately, the linker does not check xib
files, and so if a class is referenced only in a xib, and not in objective-c
code, the linker will drop that class from the final executable. This is a
linker issue, not a framework issue (it also happens when you build a static
library).

Apple's built-in framworks don't suffer this problem because they are
dynamically loaded at runtime and the complete, unstripped dynamic library
exists in the iOS device's firmware.

There are two ways around this:

1. Have end users of your framework disable linker optimizations by adding
   "-ObjC" and "-all_load" to "Other Linker Flags" in their project.

2. Put a code reference to the class inside another class in your framework
   that always gets used. For example, suppose you have "MyTextField", which
   is getting stripped by the linker. Suppose you also have
   "MyViewController", which uses MyTextField in its xib file and doesn't get
   stripped. You could do the following:

In MyTextField:
   
    + (void) forceLinkerLoad_ {}

In MyViewController:
   
    + (void) initialize
    {
        [MyTextField forceLinkerLoad_];
    }

They will still need to add "-ObjC" to their linker settings, but won't need
to force all_load.

Option 2 is more work for you, but if done right it saves the end user from
having to disable linker optimizations (causing object file bloat) just to use
your framework.


### unexpected file type 'wrapper.cfbundle' in Frameworks & Libraries build phase ###

This happens when you use a fake framework project as a dependency in a
workspace, or as a child project (real framework projects don't have this
issue). Even though the framework project produces a proper static framework,
Xcode only looks at the project file, which says it's a bundle, and so it
issues a warning during the dependency check and then skips it during the
linker phase.

You can get it to link properly by manually adding a command to link
your framework during the linker phase. Add a command to link your framework
in "Other Linker Flags" in the project that depends on the framework:

    -framework MyFramework

You'll still get the warning, but it won't fail in the linker phase anymore.


### Libraries being linked or not being linked into the final framework ###

Unfortunately, due to the way Xcode works, the "Real Framework" and
"Fake Framework" templates handle included static libraries/frameworks
differently.

The "Real Framework" template follows correct static library procedure, NOT
linking other static libraries/frameworks into the final product.

The "Fake Framework" template tricks Xcode into thinking that it's building a
relocatable object file, and so the linking phase treats it as if it were
building an executable, linking all static code sources into the final binary
(although it doesn't check for missing object code). To get the
"Real Framework" behavior, you should include only the header files from the
library/framework in your framework project, not the static library or
framework itself.


### Unrecognized selector in (some class with a category method) ###

If your static library or static framework contains a module with ONLY
category code (no full class implementations), the linker will get confused,
and will leave the code out of the final binary. Since it's not present in the
final product, you'll get "unrecognized selector" exceptions when any call is
made to those category methods.

To get around this, put a dummy class into the module containing the category
code. The linker, seeing a full Objective-C class, will link the module in,
including the category code.

I've made a header file [LoadableCategory.h](https://github.com/kstenerud/Objective-Gems/blob/master/Objective-Gems/LoadableCategory.h)
to make this easier to do:

    #import "SomeConcreteClass+MyAdditions.h"
    #import "LoadableCategory.h"
    
    
    MAKE_CATEGORIES_LOADABLE(SomeConcreteClass_MyAdditions);

    
    @implementation SomeConcreteClass (MyAdditions)
    
    ...
    
    @end

You will also need to add "-ObjC" to the "Other Linker Flags" build setting in
any project that uses the framework.


### Unit tests crash before executing any code ###

If you make a new static framework (or static library) target with unit tests
in Xcode 4.3, it will crash when you try to run the unit tests:

    Thread 1: EXC_BAD_ACCESS (code=2, address=0x0)
    0 0x00000000
    ---
    15 dyldbootstrap:start(...)

This is due to a bug in lldb. You can run the unit tests using GDB instead by
editing your scheme, selecting "Test", and from the "Info" tab changing
Debugger from **LLDB** to **GDB**.



History
-------

### Mk 1

The first incarnation. It used a bunch of script hackery to cobble together a
fake framework. It exploited the "bundle" target, setting its type to a
relocatable object file.


### Mk 2

This version took advantage of the template system to do most of the work
that the script used to do. Everything (including the script) was embedded
in the template.


### Mk 3

This version does away with the "relocatable object" hackery and builds a true
static framework, with all the abilities of an OS X static framework.
This solves a number of linker, unit testing, and workspace inclusion issues
that plagued the previous hacky implementations.

It also includes the concept of the embeddedframework, which allows you to
include resources with your framework in a way that Xcode understands.

Josh Weinberg also added some tweaks to make it build in the proper build
directory with scheme-controlled configuration, and behave better as a
subproject dependency.

It now requires some small modifications to Xcode's specification files in
order to support true static frameworks, and thus comes with an install and
uninstall script.


### Mk 4

This version gives you the choice of installing the "real framework" template
or the "fake framework" template. Both come with an install script, but only
the "real framework" installer needs to modify Xcode.

This also fixes some issues that the fake framework had in Mk 2 (such as
the curious behavior of embedding the full path to the compiled files within
the files themselves, resulting in warnings when building with that framework).


### Mk 5

This version does away with the extra target and script. Everything is self
contained in the framework target, and the framework under the "Products"
group is actually the universal framework (no more Debug-univesal or
Release-universal folders).

You can build and clean like you would in any other project.

As well, the "Fake" framework template now builds a proper static library
instead of a relocatable object file (although Xcode still doesn't believe
that it's real).


### Mk 6

This version makes the Xcode modifications for real static frameworks safer
by simply adding an extra specification file instead of patching existing
ones.


### Mk 7

This version was *supposed* to be the one that no longer modified Xcode, but
alas, Xcode behaves differently depending on *WHERE* the xcspec file gets
installed. Take a guess at which location doesn't work...

@wtfxcode

So instead, this version basically handles the new install location of
Xcode4.3.

Templates now build armv6 + armv7 by default instead of just armv7.

Note: If you previously installed real framework supprt under the broken Mk 7
(i.e. if you installed it on Feb 16th, or Feb 17th before 9:00 am PST, 2012),
run uninstall_legacy.sh to uninstall the xcspec file from your home dir, then
reinstall.


### Mk 8

This version replaces the bash scripts with Python scripts. This gives a LOT
more control over the build process.

To build the universal version of your framework, you must now use the
**Archive** command rather than the **Build** command.  **Build** only builds
for the current architecture (to speed up compilation when your framework
project is a dependency of another project).

Added a devel folder for template development.


License
-------

Copyright (c) 2011 Karl Stenerud

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
the documentation of any redistributions of the template files themselves
(but not in projects built using the templates).

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

本源码包内暂不包含可直接显示的源代码文件,请下载源码包。