Building Universal Frameworks in iOS

Apple does not provide an easy way to generate universal frameworks using Xcode alone. Because of this, developers generally write a shell script to build the frameworks for simulators and devices. We then use the “lipo” command to combine the different architectures into one universal framework. Prior to app submission to the App and Play stores, developers execute a script to remove the unnecessary architectures. While this approach works well it raises the following important questions:

What will happen if Apple removes this command utility?

Without using the “lipo” command, Xcode allows developers to link separate frameworks for simulators and devices in the following way using xcconfigs:

OTHER_LDFLAGS[sdk=iphoneos*] = -framework Test.framework
OTHER_LDFLAGS[sdk=iphonesimulator*] = -framework Test.framework

The advantage to this method is if Apple ever removes the “lipo” command from MAC OS X, developers won’t need to change anything.  Plus developers won’t be required to use a tool or utility which isn’t explicitly documented and supported by Apple.

However, if we have three variants, we will need to distribute six different frameworks which increases the effort required to maintain the distributions.  Moreover, this approach does not work with the cocoapods because there is no easy way to specify which pod should be installed for which simulator or device.

Does Apple itself use the lipo command?

To answer this question, I conducted two experiments with a sample project built against a Release configuration and a Generic iOS Device.

Dynamic framework using Objective-C language

Xcode Build Output Lipo Create Command

Dynamic framework using swift language

Xcode Build Output Lipo Create Command

By looking at these screenshots, we can clearly say Apple uses the lipo command. We can then say with fairly high confidence that it isn’t likely this command utility will be removed in the near future.

What is the impact on performance of the build process?

To verify the performance of using the lipo command, I performed the following experiments.  (To capture the script run time, I used the “time” command built into OS X.)

Calculate the time to combine the architectures

I created a script that executes the “lipo” command only to combine architectures.

#Script to combine architectures.
time lipo -create -output \
"universal/Test.framework/Test" \
real 0m 0.021s
user 0m 0.003s
sys 0m 0.008s
Calculate the time to remove simulator architectures

I created a script that executes the “lipo” command only to remove architectures.

#Remove i386 architecture
lipo -remove i386 \
"universal/Test.framework/Test" \
-output "universal/Test.framework/Test"
#Remove x86_64 architecture
lipo -remove x86_64 \
"universal/Test.framework/Test" \
-output "universal/Test.framework/Test"
real 0m 0.036s
user 0m 0.004s
sys 0m 0.012s

According to these results, the average time to combine and remove architectures is 21ms and 36ms, respectively.

Since, Apple uses it in Xcode already, developers can also use the “lipo” command to combine or remove architectures. As a bonus, it has an unnoticeable impact on the performance of the app build process.

Image courtesy of henriok

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s