App icon for Proton VPN

Proton VPN

October 24, 2024

Sometimes a tweet thread isn't enough.

Yes, Proton VPN did reduce app size by 50 MB (actually closer to 40 MB). But the size they reduced should have never been in the app in the first place. It was introduced several months prior, when Proton made a change that helped improve its app launch time.

Let's walk through the changes that were made, why app startup improved, and how Proton can reduce app size even further.

Dynamic vs. Static Frameworks

This was Proton in v4.4.1 (Sept 2023). Most of the app is made up of dynamic frameworks like ProtonCore_UIFoundations.framework. Understanding Dynamic vs. Static frameworks are key to Proton's changes, so we'll give a brief overview. You can read our blog on the subject to get more detail.

Proton VPN v4.4.1 X-Ray
Proton VPN v4.4.1 X-Ray

Dynamic frameworks let you share code between different app targets, which helps avoid duplication. But, there are two main performance tradeoffs to consider:

  1. Dynamic frameworks are linked at runtime by DYLD – the dynamic linker. As you use more dynamic frameworks, DYLD has to do more work, which can lead to increased pre-main time during app launch.
  2. Because dynamic frameworks are linked at runtime, this makes it hard for the compiler and linker to make certain optimizations. Most notably, there is limited dead code stripping because it's not possible to tell which symbols will be used externally, meaning the entirety of the library is included. If you're only using 1/100 functions in a dynamic framework, the other 99 functions will still ship in your app.

Static frameworks are linked at compile time and are typically faster and smaller than dynamic frameworks. The main performance tradeoff is that static frameworks cannot share code between app targets; you need to include them in every target you need the functionality in.

Proton's Size Changes

Back to Proton VPN. If we look at our Size tracker graph, we see the app more than double between v4.4.1 & v4.4.3

ProtonVPN size tracker showing app size doubling in October 2023 (+150 MB) and then a smaller reduction of -38 MB in October 2024

We can compare these two builds to see exactly what changed.

X-Ray diff showing framework changes
X-Ray diff between v4.4.1 and v4.4.3

Our X-Ray diff is showing that Proton VPN removed every dynamic framework, which removed 95 MB of total size. But, we also see that static assets like ProtonCoreUIFoundationsResourcesiOS.bundle/Assets.car (35 MB) were added in two app extensions + the main app target (+105 MB total). This asset catalog was already included in a dynamic framework in v4.4.1 so Proton VPN could have easily avoided the duplication by keeping the assets in a dynamic framework.

Here's our X-ray for v4.4.3 – everything in red is a duplicated file.

X-Ray showing file duplication
v4.4.3 X-Ray highlighting duplicated files in red

Proton went from having 20 MB of duplicated files to 135 MB of duplicated files in this one change. Most of the duplication is from static resources (images and localizations) and were previously being shared across targets as a dynamic framework.

But there's more to this change. Going back to the X-Ray diff, we can see the binary increased in two app extensions and the main app binary.

X-Ray diff showing binary size changes
Binary size increases across app targets

This is some of the dynamic frameworks now being linked as static frameworks in the respective targets. We can zoom in to see libraries like Lottie, Alamofire, and Sentry.

Detailed view of static frameworks

As a dynamic framework, Alamofire took up ~3 MB as opposed to 223 kB now. This is dead code stripping at work. Alamofire is roughly 220 kB in each of the three targets. In this case, the net size impact of including Alamofire three times is still smaller than it was as a dynamic framework.

Performance Impact

We ran a performance test between two versions of Proton VPN - one where the app was primarily dynamic frameworks and one where it was mostly static. As we'd expect, pre-main time (<early startup>) decreased by 8%.

Performance test results showing 8% decrease in pre-main time

*This perf test was run between v5.7 & v 4.3. Proton VPN appeared to add a number of functions to its startup path. Because this test was not run between v4.4.3 & v4.4.1, we can't conclusively determine if dynamic → static improved total startup time, but that is the likely outcome.

Looking over this change, Proton was able to improve its pre-main launch time, but more than doubled in size, mainly from including a large asset catalog 3x. The net impact of duplicating libraries like Lottie, Sentry, and Alamofire as multiple static libraries is smaller than a single dynamic framework. A more optimal change for Proton VPN would have been to do something like keep the static assets like images & strings as a dynamic framework

The Recent Size Decrease

Which brings us to why we're here in the first place - Proton VPN reduced its app size by 50 MB between v5.7 & v5.8 (which we have as -37.4 MB reduction).

The reduction came from removing ~19 MB of vector files from the ProtonCore Asset catalog. In v5.8 the asset catalog is only duplicated twice now, once in the main app bundle and once in Quick Connect Widget.

Size reduction in v5.8

Here's Proton VPN now (v5.8)

X-Ray of Proton VPN v5.8

30% of the app size is still from duplication introduced in the v4.4.3 change.

As for the initial tweet's sentiment of "let's give some praise," context is important here. Generally, Proton's decision to go from dynamic → static is probably the right one for their app. But in execution, Proton went "all or none". If Proton just kept the single dynamic framework with the large asset catalog, they would've gotten the best of both worlds (and still can).

Analysis Links

Here are the links to all build analysis pages referenced:

Sign up for our newsletter 🛸

Never miss a post or product update

Related articles



2024 © Emerge Tools, Inc. All rights reserved.