XCFramework with Common Third-Party Dependencies Causing Duplicate Symbol Conflicts

What is the recommended approach for distributing an XCFramework that uses common third-party dependencies (like Google Maps) when client apps may also use the same dependencies, resulting in duplicate symbol conflicts?

I'm developing a closed-source SDK distributed as an XCFramework. My SDK internally uses Google Maps for mapping functionality. However, when clients integrate my XCFramework into their apps that also use Google Maps, we encounter duplicate symbol errors.

What I've Tried: Static vs Dynamic Linking: Both approaches result in conflicts

Static linking: Google Maps symbols compiled into my binary

Dynamic linking: GoogleMaps.framework bundled with my XCFramework

Build Configuration:

Set "Build Libraries for Distribution" = YES

Tried various linking strategies

Architecture Changes:

Used @implementation_only import

Wrapped code with #if canImport(GoogleMaps)

However, the dependencies still get linked at build time

Answered by DTS Engineer in 864461022

There isn’t a great answer to this in the general case. Ignore the build-time issue right now, and focus on the run-time one:

  • Imagine an app A which depends on F.
  • It also links to a library L which depends on F.
  • The process running A can’t load two different copies of F [1].
  • Which means L must use a specific version of F, and A must agree to use the same version.

If F supports ABI compatibility then you can resolve this conundum by having A embed the newest version of F. But not all frameworks support ABI compatibility.

IMPORTANT ABI compatibility has both technical and social aspects. You can’t tell just by looking at the framework whether it’ll be ABI compatible in the long term. You’ll also need a promise from the framework author.


Coming back to the build side of this, that presents significant practical challenges:

Static linking: Google Maps symbols compiled into my binary

Don’t do that. It’ll result in two copies of the framework being loaded in the client app’s process, and that’s unlikely to end well.

Dynamic linking: GoogleMaps.framework bundled with my XCFramework

That won’t work either, at least on iOS. You can’t nest frameworks on iOS. All frameworks must be present at the top level of the app. So, if your library relies on a framework then you must inform your clients of that so that they can add that framework to their app.

I talk about this stuff in much more detail in this thread.

Share and Enjoy

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

[1] In theory this is possible, but the practical concerns means that it’ll only work under very limited circumstances. And even if you get it to work, you’re paying both an on-disk and in-memory cost for having two copies of the framework.

There isn’t a great answer to this in the general case. Ignore the build-time issue right now, and focus on the run-time one:

  • Imagine an app A which depends on F.
  • It also links to a library L which depends on F.
  • The process running A can’t load two different copies of F [1].
  • Which means L must use a specific version of F, and A must agree to use the same version.

If F supports ABI compatibility then you can resolve this conundum by having A embed the newest version of F. But not all frameworks support ABI compatibility.

IMPORTANT ABI compatibility has both technical and social aspects. You can’t tell just by looking at the framework whether it’ll be ABI compatible in the long term. You’ll also need a promise from the framework author.


Coming back to the build side of this, that presents significant practical challenges:

Static linking: Google Maps symbols compiled into my binary

Don’t do that. It’ll result in two copies of the framework being loaded in the client app’s process, and that’s unlikely to end well.

Dynamic linking: GoogleMaps.framework bundled with my XCFramework

That won’t work either, at least on iOS. You can’t nest frameworks on iOS. All frameworks must be present at the top level of the app. So, if your library relies on a framework then you must inform your clients of that so that they can add that framework to their app.

I talk about this stuff in much more detail in this thread.

Share and Enjoy

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

[1] In theory this is possible, but the practical concerns means that it’ll only work under very limited circumstances. And even if you get it to work, you’re paying both an on-disk and in-memory cost for having two copies of the framework.

Is there no solution for this? I checked your other thread as well. Is there a way for me to remove the third party libraries from my SDK?

I run the below commands to generate the SDK.


xcodebuild archive \
  -project Abc.xcodeproj \
  -scheme "AbcSDK" \
  -configuration Release \
  -destination "generic/platform=iOS Simulator" \
  -archivePath "BuildArtifacts/iossim.xcarchive" \
   | tee build.log

xcodebuild archive \
  -project Abc.xcodeproj \
  -scheme "AbcSDK" \
  -configuration Release \
  -destination "generic/platform=iOS" \
  -archivePath "BuildArtifacts/ios.xcarchive" \
   | tee build.log


xcodebuild -create-xcframework \
  -framework BuildArtifacts/iossim.xcarchive/Products/Library/Frameworks/AbcSDK.framework \
  -framework BuildArtifacts/ios.xcarchive/Products/Library/Frameworks/AbcSDK.framework \
  -output Frameworks/AbcSDK.xcframework

As soon as I run, it gives me the list of resolved source packages that includes all the third party libraries. If I try removing the libraries, it gives error at all the import statements in my code. I even tried using the below, but it didn't help..

@_implementationOnly import GoogleMaps 

To quote every domain expert, ever: It depends |-:

The full answer depend on how your framework is packaged, how the third-party library is packaged, and how it uses that third-party library. For example, imagine that the third-party library is packaged as a non-mergeable framework within an XCFramework. In that case, the Xcode build system can only record a reference to that framework. It can’t embed the code from that third-party library in your framework because it only has access to a dynamic library. OTOH, if the third-party library ships as a static library or framework, a mergeable library or framework, or a Swift package, Xcode could potentially merge the code into your framework.

Share and Enjoy

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

I have a standalone iOS app and an SDK created out of it to distribute to a third party. During the process of creating an SDK, I created multiple framework targets for different modules. Now one fo the module depends on a third party library say ABC. I don't want to make it static and hence i moved just the ABC to a different framework and made it dynamic. Now I am getting linker errors. I don't want the ABC binary to be there in the SDK as this is giving duplicate error issue at client end.

What’s the format of ABC? A framework? A mergeable framework? A static framework? A static library? Or something else?

If it’s packaged as an XCFramework, you’ll have to look inside that to determine the actual format, because an XCFramework can wrap all of the above.

Share and Enjoy

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

ABC(Google Maps) is a third party library. Its a static framework thats installed via SPM. I tried using linkers and many more configurations, but it always gets bundled within the SDK that's causing the conflict at the client end. Please suggest!

So, lemme recap:

  • You are publishing an XCFramweork that contains a dynamic (or mergeable) framework
  • You want to use another third-party library.
  • That’s published as a static framework.

That makes things tricky because:

  • Assuming that the third-party library contains Swift or Objective-C, there should only be one copy of it running in your client’s process.
  • You can’t static link it into your framework because then the client can’t access it.
  • The client can’t statically link it into their app because then you can’t access it.

There are some tricks you can play but the easiest option would be for your library vendor to ship a dynamic framework rather than a static one [1]. Have you check whether they offer that option?

Share and Enjoy

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

[1] Or, ideally, a mergeable framework, which means the clients get to decide whether they want to link statically or dynamically.

XCFramework with Common Third-Party Dependencies Causing Duplicate Symbol Conflicts
 
 
Q