How to link a Swift Package as dynamic

According to the SwiftPM documentation, when defining the type of a package:

Leave this parameter unspecified to let to let the Swift Package Manager choose between static or dynamic linking (recommended).

However, when using such libraries from Xcode in a fairly simple setup and running unit tests, Xcode complains about duplicated symbols:

objc[97739]: Class _TtC8Deferred18POSIXReadWriteLock is implemented in both /Users/pcifani/Library/Developer/CoreSimulator/Devices/835DF32C-44B3-4841-B6CB-A7B2D12744D7/data/Containers/Bundle/Application/C5053FF1-BF0D-434A-94C6-BAAE5FF414F0/VideoAsk.app/Frameworks/BSWFoundation.framework/BSWFoundation (0x10825ec48) and /Users/pcifani/Library/Developer/CoreSimulator/Devices/835DF32C-44B3-4841-B6CB-A7B2D12744D7/data/Containers/Bundle/Application/C5053FF1-BF0D-434A-94C6-BAAE5FF414F0/VideoAsk.app/VideoAsk (0x106c31310). One of the two will be used. Which one is undefined.

The app's dependency tree looks like this: libFoo depends on libBar and App depends on both libFoo and libBar, and the Tests bundle for the App depends on both the app and the library.

I can't find any option in Xcode's UI to "force" the libraries to be dynamic, and SwiftPM seems to not see this issue when linking the packages and choosing a dynamic configuration for me.

Any workaround except forking the library and setting all the types to .dynamic?

2 Likes

Yeah, that's one of the most annoying remaining problems with SPM in Xcode. SPM always chooses static libraries…

What you can do as a workaround is to create a new shim package (can live right inside your app's repo) which you explicitly mark as dynamic, and then have its manifest specify all the packages you need to share. Then your app and other targets previously using those, must only link to your dynamic package. This sucks for so many reasons, but is the only way I know how to fix this.

1 Like

Yeah, that is a workaround I've been using, but as you mention, it sucks for a lot of reasons...

Does someone have any updates regarding this topic?

No news here... :(

Do you mind elaborating a bit on the "shim" package? I'm guessing it would be one dynamic library package at the root (does it have a target?) with everything else added as dependencies? Did you have to turn off code coverage? Do you still import by individual target or just import MyDynamicLibraryHoldingEverything?

1 Like

I just created a new Framework using Xcode (called FacadeKit on the screenshot here below) and added all required Packages as dependencies. After that, you embed that into your .app and Unit Test target. After that the import SomeStaticPackage should all work without ever importing the shim framework on your code

1 Like

So in my case I've got a bunch of other embedded framework and static library targets within the project, is it safe to just link that embedded framework everywhere it's needed since it's dynamic and will be resolved later?

It should be fine, yes. Double check for dylib errors when running the app or running your tests.

For folks who interested in a step by step for that I created one: GitHub - renaudjenny/Swift-Package-Manager-Static-Dynamic-Xcode-Bug: Workaround about SPM (Swift package manager) deal with Xcode 11.4 and Swift 5.2 with external static libraries. Adding an internal dynamic library to resolve static code duplication error

6 Likes

Hey renaudjenny, thanks a lot for the nice writeup!

1 Like

I discovered that Xcode builds .frameworks for all your packages. They'll live in $(BUILT_PRODUCTS_DIR)/PackageFrameworks. You can drag those to your app, and as long as they exist before you try to build the app itself, then the compiler will be happy.

Not a great workaround but hey.