Static VS dynamic frameworks in Swift: an in-depth analysis
By: Andrés Cecilia Luque
Tags: Performance, Swift, Framework, iOS, App Development

There has been quiet some discussion lately in the Swift community about the benefits of using static instead of dynamic frameworks (see 1, 2, 3). I was specially interested in this post, that explains how switching from dynamic to static frameworks can cut your app’s launch time in half.

I decided to test this by myself. The app I am working on at the moment depends on a total of 27 dynamic frameworks, 6 consumed using Carthage and 21 through CocoaPods.

The first two sections of this post explain how I generated static frameworks with Carthage and CocoaPods. The third one shows the results I got, in terms of app size and launch times. If you are just interested in the conclusions, scroll to the bottom.

Generate static frameworks: Carthage

In order to generate static frameworks using Carthage, the Xcode projects under the Carthage/Checkouts folder have to set the MACH_O_TYPE build setting to staticlib . Using this and this as a reference, I came up with the following script:

xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)

echo "MACH_O_TYPE = staticlib" >> $xcconfig

export XCODE_XCCONFIG_FILE="$xcconfig"

carthage bootstrap --platform ios --no-use-binaries

The two first lines generate a temporary xcconfig file with the MACH_O_TYPE build setting set to staticlib .

file with the build setting set to . The third line sets the path to the temporary xcconfig file in the XCODE_XCCONFIG_FILE environmental variable. xcodebuild overrides the build settings of the Xcode project with the ones found inside the XCODE_XCCONFIG_FILE .

file in the environmental variable. overrides the build settings of the Xcode project with the ones found inside the . The fourth line runs Carthage, which uses xcodebuild under the hood to generate the frameworks, and then places the static ones under the path Carthage/Build/iOS/Static .

Using this method there is no need to modify the Xcode projects found under Carthage/Checkouts .

Generate static frameworks: CocoaPods

In order to generate static frameworks using CocoaPods, the .podspec file has to set the static_framework specification to true , as explained here. To simulate this without making modifications to the .podspec files, it is possible to add a preinstall step to the Podfile , as explained here:

dynamic_frameworks = [] # Make all the other frameworks into static frameworks by overriding the static_framework? function to return true

pre_install do |installer|

installer.pod_targets.each do |pod|

if !dynamic_frameworks.include?(pod.name)

puts "Overriding the static_framework? method for #{pod.name}"

def pod.static_framework?;

true

end

end

end

end

The results: app size and launch time

I managed to switch 25 out of the 27 dependencies to static frameworks. In order to measure the app size, I archived and exported it as a Development build. In order to measure the launch times I set the DYLD_PRINT_STATISTICS environmental variable to 1 , as explained here.

App size

These are the results:

Launch times

The launch time tests were performed running the app from Xcode using the release configuration, on an iPhone 8 with iOS 12.1.1. In order to get a better understanding of the results, I ran the app 15 times:

Before the first run, the device is rebooted to clear the dyld cache, and a fresh installation of the app is done. Before the 11th run the device is also rebooted to clear the dyld cache (but a fresh installation of the app is NOT done). Runs 2nd to 10th and 12th to 15th happen without rebooting the device, and without doing a fresh installation of the app.

These are the results, where the total launch times can be visualised in the first graph, while the second graph shows the time spent in each of the four launch steps. The results for runs 2nd to 10th and 12th to 15th are averaged: