Swift binary size and public interfaces

At my job, we have a set of dynamic frameworks that we are building which are mostly (if not entirely) Swift. Recently, we have been looking into the binary size of these frameworks, and I've been doing a little testing to figure out ways to reduce the binary size.

The first thing I've tested is enabling all stripping for the building of the binaries, which reduced the size of the binaries by about 25-30%. I'm not surprised that the binary size decreased, but I am surprised at just how much of a difference it made. Stripping only at the final app compilation made no difference, but since these are dynamic frameworks, I think that's expected.

The second thing I tested is a bit more surprising. I removed all public modifiers I could while still allowing the build to complete (with the understanding that a lot would break). This also resulted in a decrease of 25-30%, and adding stripping on top of this reduced the binary size even further.

I'm not entirely sure what the specific reason for removing public functions/vars resulting in smaller binary size is.
Has anyone else seen this behavior? Is there some documentation or other information I can use to help craft a better plan to move forward on this?

It seems intuitive to me that public API cannot be removed from dynamic frameworks by any form of dead code elimination.

1 Like

That makes sense, but all of the code in question should in theory be heavily used. I know at least some of it can likely be optimized away (non-public vars that are read-only).

Are your public APIs generic in any way? Either generic parameters, or methods of generic types, or extensions to protocols with associated types?

1 Like

I don't think there are a large number of generic public APIs, but that's a good place to check. We do have I believe literallty a few thousands API interactions points because of some patterns we use. I tried removing the one class of public static vars, and it helped somewhat, but not as much as I would expect.

I will do some digging around anything that's generic.

I've done some more testing, and it appears that removing generics doesn't appear to make any significant difference. I'm still trying to dig more into this.

So after doing a bunch of work and making sure I have all the numbers I need to investigate (easy to get confused with all the variants I've been digging into), I have come up with the following information:

  • The baseline size of the framework binary is 37 MB
  • Removing nearly all public interaction points from the framework, I was able to reduce the binary size by ~8 MB (~22%)
  • Stripping the framework binary as part of the host app build process reduces the binary size by ~17 MB (~46%)
  • Combining the above two reduces the binary size by ~19MB (~51%)

I'm still a bit confused about why there is a decent change in binary size based on what is public, but given the numbers above, I do'nt think I'm going to spend too much additional time here, as we are likely just going to enable stripping to get the primary benefit in size reduction.

Terms of Service

Privacy Policy

Cookie Policy