Search code examples
xcodestatic-librariesstatic-linkingdynamic-linkingdynamic-frameworks

Is it possible to build a dynamic framework without the symbols of a static library that it links to?


I have an app that links to two dynamic frameworks which both link to the same static library, as follows:

|--App
  |--DynamicFramework1      
    |--StaticLibrary   
  |--DynamicFramework2      
    |--StaticLibrary <- the same library that DynamicFramework1 links to

The static library's symbols are included in each framework's binary because of the way dynamic frameworks are built by default. The app therefore finds duplicates of the static library's symbols at runtime.

Is it possible to link a dynamic framework to a static library (and to reference the classes and methods of the static library within the dynamic framework) in a manner that symbols from the static library are excluded from the dynamic framework's binary?

My hope in doing this is that the binary of each of the two dynamic frameworks will exclude the symbols of the static library. I will then make it the responsibility of the app to link to the static library directly.

Notes

  1. I have tried linking my dynamic framework with the static library in two different ways thus far: (1) I added the static library to my framework's "Link Binary with Libraries" Build Phase; and (2) I referenced the static library in my framework's "Other Linker Flags" Build Setting. Both result in the static library's symbols being included in the framework's binary.
  2. I am aware that changing a framework target's "Mach-O Type" from "Dynamic Framework" to "Static Library" will build the framework's binary without the symbols of the static libraries that it links to. I want to keep my frameworks as dynamic frameworks so that (1) I can benefit from how Xcode bundles together resources (strings, storyboards etc) automatically for dynamic frameworks; and (2) users of my framework can benefit from Mergeable Libraries in the near future.
  3. I am aware that I can solve this problem by changing the static library to a dynamic framework. I want to avoid this as much as possible since the static library is a binary from a third-party. I want to avoid forking the static library's source code and messing with its build scripts if I can.
  4. See here for a minimal Xcode project that demonstrates the problem.

Solution

  • Workaround solution

    Let's say you have two dynamic frameworks named DF1 and DF2 which both link to the same static library named SL.

    I couldn't find a way to build DF1 and DF2 in a manner that they link to SL whilst excluding the symbols of SL from their binaries.

    The best solution that I've been able to come up with thus far is as follows:

    1. Introduce a new dynamic framework – DF – that uses the -all_load linker flag and links to SL.1
    2. Change DF1 and DF2 to link to DF instead of SL.2
    3. Change the app so that it links to DF as well DF1 and DF2.3

    In pictorial form, this is:

    App
     |--> DF1 --> DF --> SL   
     |--> DF2 --> DF --> SL
     |--> DF --> SL
    

    See here for a minimal Xcode project that demonstrates this workaround.

    Addendum

    The Benefits of mergeable libraries section of the Meet mergeable libraries talk from WWDC 2023 offered promise. In that talk, the speaker said:

    ... When merging, the linker can de-duplicate content, such as strings, across all libraries. For instance, it removes redundant symbol references, Objective-C selectors and obj_msgsend stubs...

    I experimented with it but, sadly, I was not able to get it to solve the problem at hand. This question perfectly summarises my experience.

    Footnotes

    1The symbols of SL will be in DF's binary.
    2The symbols of DF will not be in DF1 and DF2's binaries.
    3The app will now get the symbols of SL from DF only.