I’ve been using this tool called Bloaty McBloatface1 to attribute the contribution of each swift module or file to our app’s binary. And it has worked out really well for me, the CLI tool is super fast, gives lots of information, supports diffing and has so many options that configurations and options that it’s difficult to explore it all in a single blog post.

In this blog post, we’ll go through the setup, and share a simple example of using the tool on an iOS app to identify what could be causing the binary size to increase over time for our iOS app.

Installing Bloaty

  • If you want to avoid building from source, you can install an older version by executing brew install bloaty. (Check the brew formula for current version)
  • To build from source and get the latest version, follow these steps:
  • Install cmake and ninja if not already installed.
    • Using homebrew:

      brew install cmake
      
      brew install ninja
      
  • Clone or download the source code2 from the github repo
  • We’ll have to build the code from source, so follow the instructions in the README.md of the github project to build bloaty
  • Once the installation succeeds, the bloaty binary should be ready to be executed from your terminal.
  • You can try playing with it by passing any linux/macOS binary as an argument to the CLI tool.
    • Fun fact: You can even pass bloaty it’s own binary and it’ll give you some information.
    • Read up more on bloaty on it’s github page or it’s user documentation or execute the following command for more info

      bloaty --help
      

Testing it out on our iOS app

Setting up the app project

  • To extract per file contribution to the final binary of our iOS app, we must change one setting in our xcode project if not already done so
  • Specifically, the DEBUG_INFORMATION_FORMAT build setting.
    • We should set the value of this setting to DWARF with dSYM File dwarf with dsym build setting screenshot
    • Later on this dsym is passed as an input to bloaty to extract per file information.
  • Build the app

Extracting dsym and built iOS binary

  • To get the dsym, we must go to the derived data folder where the .app is populated along with the .dsym file
  • For most of us, it should reside at ~/Library/Developer/Xcode/DerivedData/
    • Look for a folder with your project’s name, and go to Build/Products/{YourConfiguration}-{platform}/
    • Under this folder we are interested in:
      • the dsym: which should be located at the same level in this folder. If you don’t see a .dSYM file/folder, ensure the build setting is correctly set.
      • the app binary: which should be at {YourAppName}.app/{YourAppName}
        • If using finder, right click on {YourAppName}.app and click on “Show Package Contents” to get to the binary.

Passing the dsym and iOS binary to bloaty

  • To get app size split per file, we are interested in the compileunits feature of bloaty
  • Execute the following to get the detailed split:

    bloaty -d compileunits --debug-file=Path_to_dsym_file.dSYM/Contents/Resources/DWARF/{YOUR_APP_NAME} path_to_your_app_binary
    

    The above command will give you an output similar to the following:

bloaty on iOS app screenshot

  • Notice the [32 Others] entry in the above screenshot?
    • That’s because bloaty will limit it’s unique entries to a default of 20 entries. We can override this limit by passing -n 0 to the above command and it will now give a complete list of swift files contributing to the final binary size
    bloaty -d compileunits --debug-file=Path_to_dsym_file.dSYM/Contents/Resources/DWARF/{YOUR_APP_NAME} path_to_your_app_binary -n 0
    

    bloaty on iOS app full details screenshot

Size diffing

  • You can pass two dsyms and two binaries to bloaty and it will give you a diff on various changes between two binaries

  • For example, say i want to compare the changes to our app binary using an app built from develop (ahead of master) vs an app built from master. We can pass the additional binary by adding -- {baseline_binary} to the bloaty command, like so:

    bloaty -d compileunits --debug-file=Gitlab_Viewer_develop_dsym --debug-file=Gitlab_Viewer_master_dsym Gitlab_Viewer_develop -- Gitlab_Viewer_master
    

    The above command will give you this kind of output:

bloaty on iOS app size diff on builds from develop vs master

  • On develop I added a new swift file called RunnerDetailView.swift, notice how the above size diff shows RunnerDetailView.swift as a new addition.

Where to go from here?

  • Read this doc around how bloaty works
  • Try reducing your app’s size by finding out major contributors and refactoring code as needed
  • Read this blog post by emergetools which among a lot of useful suggestions also shows how adding something as simple as final to your classes helps reduce a few bytes/kilobytes from your overall app size.
  • Since bloaty is platform agnostic, I think this could be useful for android apps as well.

Footnotes:

  1. Love the name :) 

  2. A macOS package would have been nice, but then someone would also have to handle notarization on recent macOS versions, so i guess it’s best to build and install the binary from source.