Building a Universal Framework for iOS

Apple has invested quite a bit of time into making it easy to compile for a number of different architectures in XCode. For instance, compiling a library into itsarmv6, armv7, and i386 variants is just a matter of specifying the supported architecture types. However, there isn’t a built-in mechanism to take the binaries built for the various architectures and merge them into a universal iOS framework.

Before we go through the steps of building a universal iOS framework we should first reviewwhat a framework is and why they are useful.

What is a ‘framework’ and why are they useful?

Apple defines a framework as:

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

So instead of having header files and binaries in disperate locations a framework brings everything together into one package (a directory with a known structure). Packaging a library as a framework simplifies things for developers because it not only provides a binary to link against but it includes all of the necessary header files to reference as well.

What is a ‘universal’ framework?

A universal framework can be defined as a framework that contains a binary which has been built for a number of architectures (armv6, armv7, i386) and can bestatically1 linked against. This is particularly useful in iOS development because an application can be built for the simulator (i386) and the device (armv6, armv7).

1 Statically linking a library resolves symbols at compile time and embeds the library into the application. It is not possible, currently, to create a dynamic framework for iOS.

Building a ‘universal’ framework

Firstly, there is a project called iOS Universal Framework that simplifies the process of building a universal framework by providing an XCode template. However, I think it is still a meaningful exercise to understandhow a universal framework is built using XCode.

Lets get started:

Create a new project

  1. File -> New -> New Project (Command-Shift-N)
  2. Select Frameworks & Libraries under iOS
  3. Select “Cocoa Touch Static Library” and click “Next”
  4. Provide a name for the library

Configure architectures

By default the static library is configured to only build for armv7 so we need to add armv6 and i386 to ensure that the static library is built for older devices (iPhone 3G/Original, early generation iPod Touch) and the simulator.

Create aggregate target

An aggregate target aggregates other targets together. In effect, it wraps a number of script executables and copy file tasks in a specified order.

  1. File -> New Target
  2. Select “Other” under iOS
  3. Select “Aggregate”
  4. Give it a name (MyLibrary-iOS for example)

Build static libraries

Under the “Build Phases” section of the aggregate target we are going to add a build phase that compiles a static library for i386 andARM. In the next step we’ll merge the binaries into a fat binary.

  1. Select MyLibrary-iOS target
  2. Select “Build Phases”
  3. Click “Add Build Phase”
  4. Select “Add Run Script”
  5. Name it “Build Static Libs”

12 3
         
         
xcodebuild -project ${ PROJECT_NAME }.xcodeproj -sdk iphonesimulator -target ${ PROJECT_NAME } -configuration ${ CONFIGURATION } clean build CONFIGURATION_BUILD_DIR = ${ BUILD_DIR }/ ${ CONFIGURATION }-iphonesimulator
         
         
 
         
         
xcodebuild -project ${ PROJECT_NAME }.xcodeproj -sdk iphoneos -target ${ PROJECT_NAME } -configuration ${ CONFIGURATION } clean build CONFIGURATION_BUILD_DIR = ${ BUILD_DIR }/ ${ CONFIGURATION }-iphoneos
view raw gistfile1.sh This Gist brought to you by GitHub.

Build universal binary

We are going to add another “Run Script” phase that builds the framework itself. The script is going to:

Create a directory structure that mimics the same directory structure seen in Apple’s dynamic frameworks:

SIMULATOR_LIBRARY_PATH="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/lib${PROJECT_NAME}.a" &&
DEVICE_LIBRARY_PATH="${BUILD_DIR}/${CONFIGURATION}-iphoneos/lib${PROJECT_NAME}.a" &&
UNIVERSAL_LIBRARY_DIR="${BUILD_DIR}/${CONFIGURATION}-iphoneuniversal" &&
UNIVERSAL_LIBRARY_PATH="${UNIVERSAL_LIBRARY_DIR}/${PRODUCT_NAME}" &&
FRAMEWORK="${UNIVERSAL_LIBRARY_DIR}/${PRODUCT_NAME}.framework" &&
 
# Create framework directory structure.
rm -rf "${FRAMEWORK}" &&
mkdir -p "${UNIVERSAL_LIBRARY_DIR}" &&
mkdir -p "${FRAMEWORK}/Versions/A/Headers" &&
mkdir -p "${FRAMEWORK}/Versions/A/Resources" &&

Merge the static libraries built for the various architectures into a fat binary using a tool calledlipo:

# Generate universal binary for the device and simulator.
lipo "${SIMULATOR_LIBRARY_PATH}" "${DEVICE_LIBRARY_PATH}" -create -output "${UNIVERSAL_LIBRARY_PATH}" &&

Move the appropriate files into place:

# Move files to appropriate locations in framework paths.
cp "${UNIVERSAL_LIBRARY_PATH}" "${FRAMEWORK}/Versions/A" &&
ln -s "A" "${FRAMEWORK}/Versions/Current" &&
ln -s "Versions/Current/Headers" "${FRAMEWORK}/Headers" &&
ln -s "Versions/Current/Resources" "${FRAMEWORK}/Resources" &&
ln -s "Versions/Current/${PRODUCT_NAME}" "${FRAMEWORK}/${PRODUCT_NAME}"

The full script can be found here.

Copy header files into place

  1. Select “Add Build Phase”
  2. Click “Add Copy Files”
  3. Set “Destination” to “Absolute Path”
  4. Set subpath to ${BUILD_DIR}/${CONFIGURATION}-iphoneuniversal/${PRODUCT_NAME}.framework/Versions/A/Headers/
  5. Add header files that should be public to the list

Configure aggregate target build against the ‘Release’ configuration

  1. Product -> Edit Scheme
  2. Choose aggregate target (MyLibrary-iOS)
  3. Select “Run”
  4. Choose “Release” under Build Configuration

Build and verify framework

  1. Select aggregate target
  2. Build it (Command-B)

Verify that the binary was built for the correct architectures using lipo:

lipo -info build/Release-iphoneuniversal/MyLibrary-iOS.framework/MyLibrary-iOS 
 
Architectures in the fat file: build/Release-iphoneuniversal/MyLibrary-iOS.framework/MyLibrary-iOS are: i386 armv6 armv7

Ensure that the header files are copied into place (XCode is known to mess this up on occasion):

tree build/Release-iphoneuniversal/MyLibrary-iOS.framework/
build/Release-iphoneuniversal/MyLibrary-iOS.framework/
├── Headers -> Versions/Current/Headers
├── MyLibrary-iOS -> Versions/Current/MyLibrary-iOS
├── Resources -> Versions/Current/Resources
└── Versions
    ├── A
    │   ├── Headers
    │   │   └── MyLibrary.h
    │   ├── MyLibrary-iOS
    │   └── Resources
    └── Current -> A
 
7 directories, 3 files

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值