What exactly an Xcode framework does?

I created 2 iOS projects in Xcode:

Project 1:

  • 4 targets (main app + 3 app extensions)
  • 4 static libraries
  • the main app's target dependencies include - 3 app extensions and the 4 libs.
  • the main app's binary is linked to all 4 libs
  • similarly, each extension is linked to all 4 libs

Project 2:

  • 5 targets (main app + 3 app extensions + 1 framework)
  • 4 static libraries
  • the main app's target dependencies include - 3 app extensions and the framework
  • each extension is dependent only on the framework
  • the framework's target dependencies include all the 4 static libs

As per my understanding, the app bundle size for Project 2 should be less than that of Project 1, since we eliminate duplicating the static libs for each target by using a framework instead.

However, I have found that the bundle size is more for Project 2 as compared to the bundle size of project 1.

I do not understand, why?

Answered by DTS Engineer in 855817022

A framework is a fancy wrapper around a shared library. Shared libraries have numerous benefits on Apple platforms:

  • They can help reduce build times.
  • If the same code is used by multiple programs in your product, like an app and an app extension, putting that code in a shared library can reduce the product’s size.
  • If those programs can run simultaneously, a shared library can reduce your product’s memory footprint because only one copy of the code is loaded at a time.
  • In some cases — and this is particularly important with Objective-C — using a static library can result in incorrect behaviour at runtime [1].

Oh, and when you bundle a shared library into a framework, there’s one more benefit:

  • A framework can have resources used by the shared library’s code.

Having said that, they’re not the right choice in all situations. And that brings me to this:

However, I have found that the bundle size is more for Project 2 as compared to the bundle size of project 1.

I can see why you’d be confused by that result, but the devil is in the details here. For example:

  • A shared library has its own overhead, and if these static libraries are small then this overhead might outweigh the benefit.
  • It’s easy to embed your shared libraries incorrectly, resulting in multiple copies of the library.
  • Other factors, like debug symbols, can significantly affect your results.
  • Static libraries are more amenable to dead code elimination. So, if you have a huge amount of shared code but each client only uses a small fraction of it, you’ll definitely win by putting that in a static library.

It sounds like your created a test project to explore this issue. Are you able to share that? If so, please do, because that’ll let us focus on one specific test case.

ps I have a lot of info about linker stuff, including shared libraries and frameworks, in An Apple Library Primer. I assume the terminology defined there, so if I use a term you don’t understand you should check there first.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] If you want to learn more about that, see my responses on this thread.

A framework is a fancy wrapper around a shared library. Shared libraries have numerous benefits on Apple platforms:

  • They can help reduce build times.
  • If the same code is used by multiple programs in your product, like an app and an app extension, putting that code in a shared library can reduce the product’s size.
  • If those programs can run simultaneously, a shared library can reduce your product’s memory footprint because only one copy of the code is loaded at a time.
  • In some cases — and this is particularly important with Objective-C — using a static library can result in incorrect behaviour at runtime [1].

Oh, and when you bundle a shared library into a framework, there’s one more benefit:

  • A framework can have resources used by the shared library’s code.

Having said that, they’re not the right choice in all situations. And that brings me to this:

However, I have found that the bundle size is more for Project 2 as compared to the bundle size of project 1.

I can see why you’d be confused by that result, but the devil is in the details here. For example:

  • A shared library has its own overhead, and if these static libraries are small then this overhead might outweigh the benefit.
  • It’s easy to embed your shared libraries incorrectly, resulting in multiple copies of the library.
  • Other factors, like debug symbols, can significantly affect your results.
  • Static libraries are more amenable to dead code elimination. So, if you have a huge amount of shared code but each client only uses a small fraction of it, you’ll definitely win by putting that in a static library.

It sounds like your created a test project to explore this issue. Are you able to share that? If so, please do, because that’ll let us focus on one specific test case.

ps I have a lot of info about linker stuff, including shared libraries and frameworks, in An Apple Library Primer. I assume the terminology defined there, so if I use a term you don’t understand you should check there first.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] If you want to learn more about that, see my responses on this thread.

Hi, I've uploaded the test project on github: https://github.com/Raunit-TW/xcode-framework-poc

Adding more context to the problem, it was first discussed here: https://developer.apple.com/forums/thread/751522.

To summarize, we have an application with a bunch of extensions, all of them dependent on some common C++ source files. Building each target individually leads to a bloated app bundle and final binary, since the contents are copied to each binary. As per our understanding, frameworks can solve this issue of increased binary size by packaging the common C++ files separately such that they can be shared among the extensions.

I've uploaded the test project on github

Thanks.

Building that I see this structure:

TWiOSApp.app/
  Base.lproj/
  Frameworks/
    TWFramework.framework/
      Info.plist
      TWFramework
  Info.plist
  PkgInfo
  PlugIns/
    NotifContentExt.appex/
      Base.lproj/
      Info.plist
      NotifContentExt
    NotifServiceExt.appex/
      Info.plist
      NotifServiceExt
    ShareExt.appex/
      Base.lproj/
      Info.plist
      ShareExt
  TWiOSApp

That seems reasonable enough, in that there’s only one copy of your framework embedded at the top level, which is the correct structure for an iOS app.

Looking at the project I see that you have a TWFramework target and three ‘utils’ targets (TWUtils, StringUtils, NumberUtils, ModelUtils) each of which produce a static library that’s then linked into the framework. That’s a bit convoluted — you could just compile the code the framework directly — but there’s nothing fundamentally wrong with it.

Two things to note about this test project:

  • Your ‘utils’ libraries are very small, so it’s possible that any saving you get from using a framework is overwhelmed by overhead. You’ll need to retest with a larger codebase.
  • None of your executable targets call any of the code in the framework. If you tested this by comparing it to the statically linked version, and its targets also didn’t call any of your ‘utils’ code, then that’s not a valid test because:
    • In the framework version, the framework has to retain all of the code.
    • In the statically linked version, the linker only needs to include the code you reference, and if you don’t reference any of the ‘utils’ code then it’ll not include any of it.
    This is the dead code case I described early.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

What exactly an Xcode framework does?
 
 
Q