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"

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