[Proposal] XCFramework binary size
Closed this issue · 3 comments
Hello, before sending any pull requests, I opened this issue to propose some improvements that could benefit the binary size of the pre-compiled xcframework.
Current size
First, I calculated the current size of the SDK by uploading the MagicWeatherSwiftUI
app with and without the SDK to TestFlight.
The values correspond to an iPhone 6S.
These are the results:
Download | Install | |
---|---|---|
Including it | 1.26 MB | 3.56 MB |
Excluding it | 110 KB | 357 KB |
Therefore, RevenueCat SDK has a size impact of:
1.15 MB
in Download size and 3.2 MB
in Install size
Stripping symbols
I found out that the binary contains local symbols used for debugging purposes, so they can be stripped from the binary since they aren't necessary in production.
By running:
xcrun strip -x RevenueCat.framework/RevenueCat -o RevenueCat.framework/RevenueCat
I had these results:
Download | Install | |
---|---|---|
Base | 1.26 MB | 3.56 MB |
Symbol stripping | 1.21 MB | 3.3 MB |
Difference | -0.5 MB (-4%) | -0.26 MB (-7%) |
Compilation optimization
Currently, the frameworks have been compiled using -O
optimization, also called speed optimization.
But the SDK code doesn't look cpu intensive, so I suggest changing it to -Osize
to further reduce the binary size.
By changing it and also stripping the symbols, these are the results:
Download | Install | |
---|---|---|
Base | 1.26 MB | 3.56 MB |
-Osize & symbol stripping | 1.18 MB | 3.04 MB |
Difference | -0.8 MB (-6%) | -0.52 MB (-14%) |
Linking
Using static linking benefits the binary size since the linker can know which symbols are actually used and only copy them to the app's binary. Also, it can perform additional optimizations as machine outlining, while the dynamic linker (dyld) can't, because it links the framework at runtime.
It also benefits the app launch time; when you have a large number of dynamic frameworks, it slows down.
By changing the optimization to Osize and linking the framework statically, these are the results:
Download | Install | |
---|---|---|
Base | 1.26 MB | 3.56 MB |
Osize & static linking | 1.12 MB | 2.83 MB |
Difference | -0.8 MB (-11%) | -0.52 MB (-20%) |
Note: we can't strip symbols in this scenario because the app is the one who will link the binary.
Note 2: There is a scenario where static linking can increase the binary size instead of reducing it, and it is when the SDK is being used by the app and an app extension. In this case, the symbols will be duplicated in both binaries, so in this case, it is preferred to link dynamically.
Mergeable framework
A mergeable framework takes the best of both worlds! In debug configuration, the linker reexports the code so it can be linked dynamically. This benefits the developer by not linking it in each incremental build.
While in the Release config, the linker merges the code into the app's binary, similar to static linking. This benefits the user, as said before, by having a smaller binary and a faster app launch.
I did some tests, and I was able to compile and create a mergeable library, but at runtime it fails since the load commands are being duplicated for some reason. This will take more time to make it work.
Recommendations
I recommend providing your clients with the 3 options:
1.- The current dynamic framework xcframework
Just adding a code strip script and changing the compilation optimization to -Osize
optimization
2.- A static framework xcframework
Compiling it with -Osize
optimization
3.- A mergeable framework xcframework
This kind of fwks are not extensively being used by many companies yet, but why not being one of the first ones 😄
👀 We've just linked this issue to our internal tracker and notified the team. Thank you for reporting, we're checking this out!
Any ETA on this?
Thanks
Hi, thank you for that feedback! I've forwarded it to the team. I am not able to share whether any of these changes are planned, however.