Measuring the size of AAR and XCFramework libraries is not easy

Loading...

For these past few weeks I have been trying to come up with a plug-and-play solution to measure the size of AAR and XCFramework libraries. Ultimately, bringing this feature to rockjs, making it possible to measure React Native packages size locally, as well on CI/CD in order to track and detect size increases on each PR.

It turns out this is more complicated than I thought. The main reason for this being that there aren't any tools to measure AAR and XCFramework libraries.

Android

For Android, the easiest option would be to just get the size of the generated .aar library file, however this wouldn't necessarily be accurate. One of the reasons is because there are multiple architectures included, like x86, x86_64, armeabi-v7a and arm64-v8a. Also, when generating the AAB or APK files, there are additional optimizations done by the compiler which decrease the size of the final AAR library.

AAR library - size breakdown

AAR library - size breakdown

The most accurate option in this case would be to create a playground app that will generate two AAB files, one without the AAR library and one with the AAR library included. Calculate the size for each AAB and subtract the results, resulting in an accurate size of the final AAR library. I have documented some part of this process in my recent post on X.

iOS

For iOS, things are more or less the same. We could also consider calculating the size of the .xcframework library. However, this won't result in an accurate size. The XCFramework will include multiple targets like ios-arm64 and ios-arm64_x86_64-simulator, but we should only care about the main target and ignore the other ones, like simulators. And just like on Android, compiler will perform additional optimizations which will decrease the XCFramework size even further.

XCFramework library - size breakdown

XCFramework library - size breakdown

We also have the option to use the playground app to measure the XCFramework accurately. On top of the already complicated setup, we would also have to handle manual signing for generating the IPA files which complicates the matter even more. I have also documented some part of this process in my recent post on X.

Choosing the best option

While second option results in a very accurate library size, it has a few flaws compared to the first approach. The biggest one is scalability. The second one is the time it takes to generate both AAB and IPA files. This is especially noticed when running on CI/CD.

Also, while this approach will work for simple apps that don't have any native peer dependencies, it won't work for apps that need to include native dependencies required by the AAR and XCFramework libraries. We would have to include those libraries manually and add any additional configurations that are required to run the build. Essentially, we would have to mimic the real app which will make it impossible to turn this into a plug-and-play solution.

After brainstorming with Oskar Kwaśniewski and Burak Güner during this year's React Universe Conference, I decided to go back to the first solution and figure out if I can ignore some of the included files in the final AAR and XCFramework libraries in hopes to bring the size closer to the real size when embedded into the final app.

The simplest solution for Android, would be to just subtract the older architectures like x86, x86_64, armeabi-v7a from the final AAR file size. Basically, only include the arm64-v8a architecture, along other files.

On iOS, I could also subtract the simulator targets like ios-arm64_x86_64-simulator from the final XCFramework file size. Basically, only include the device targets like ios-arm64, along other files.

Even after these changes, the final package size still won't be as accurate as the size calculated using the playground app solution. However, I think it should be a good enough solution for tracking the package size changes during development. It also unblocks me from making this a plug-and-play tool with CI/CD support which ultimately is the whole purpose of this!