Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swift Package Manager - Add static library variants to allow use inside frameworks #405

Merged
merged 1 commit into from
Feb 15, 2021

Conversation

bpollman
Copy link
Contributor

@bpollman bpollman commented Feb 8, 2021

What and why?

Datadog cannot be added to a Framework via SPM due to the dynamic property being setting in Package.swift

When adding Datadog using SPM as a package to a framework, the type: .dynamic property causes Xcode to insist on embedding the Datadog inside the framework. The Embed Framework step is automatically created and removing, causes Datadog to be removed from the project.

Note that with LaunchDarkly, the package only appears in Link Binary with Libraries as desired.

Debug builds of the project will build and run fine. However, archiving and uploading to TestFlight will succeed but the archive produced is not valid and will crash on launch as the Datadog library is missing

Screenshot

image

Crash on Launch Log

Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Termination Description: DYLD, dyld: Using shared cache: F1161374-C3FB-358B-897A-6C2AD68BB0CC | dependent dylib '@rpath/Datadog.framework/Datadog' not found for '/private/var/containers/Bundle/Application/8F88DB89-E6FF-48D8-9F50-52270379FC0C/Deputy.app/Frameworks/DeputyKit.framework/DeputyKit', tried but didn't find: 
'/usr/lib/swift/Datadog.framework/Datadog'
'/private/var/containers/Bundle/Application/8F88DB89-E6FF-48D8-9F50-52270379FC0C/Deputy.app/Frameworks/Datadog.framework/Datadog' 
'/private/var/containers/Bundle/Application/8F88DB89-E6FF-48D8-9F50-52270379FC0C/Deputy.app/Frameworks/DeputyKit.framework/Frameworks/Datadog.framework/Datadog' 
'/Users/distiller/project/app/DerivedData/Build/Intermediates.noindex/ArchiveIntermediates/Deputy/BuildProductsPath/Release-iphoneos/Datadog.framework/Datadog'
'/Users/distiller/project/app/../Carthage/Build/iOS/Datadog.framework/Datadog'
'@rpath/Datadog.framework/Datadog' '/System/Library/Frameworks/Datadog.framework/Datadog'
Highlighted by Thread:  0

How?

Removal of the type: .dynamic property allows Xcode to choose the right course of action for the project.

I am not sure if removing this property will cause issues for other applications but we are currently using ~20 other dependencies via SPM and this is the only one that sets the dynamic properly and causes this error.

Review checklist

  • Feature or bugfix MUST have appropriate tests (unit, integration)
  • Make sure each commit and the PR mention the Issue number or JIRA reference

@bpollman bpollman requested a review from a team as a code owner February 8, 2021 23:18
@buranmert
Copy link
Contributor

buranmert commented Feb 9, 2021

Hi @bpollman 👋
Thanks for raising this issue 🙏

When we let Swift Package Manager decide on dynamic vs static linking, it fails to choose the correct option.
We specify .dynamic in Package.swift in order to support, for example, app extensions (e.g: Notification Service Extension) sharing code with their host app.

We are aware of "Do not embed -> framework disappears" bug in Xcode and we hoped it would be resolved in the next version but unfortunately it's still there in Xcode 12.4

I created a new iOS app project with 1 framework and 1 app extension target bundled to it. I used Datadog framework in all 3 of them by using one of the workarounds below:

The workaround

Link Datadog to all 3 targets with Embed & Sign or Embed without signing
While internal framework target / app extension target is selected, open Build phases -> Embed frameworks
Destination should be Frameworks by default; change it to Products Directory

Screenshot 2021-02-09 at 15 21 46

  1. SomeApp embeds Datadog to Frameworks
  2. SomeFramework / SomeExtension embeds Datadog to Producsts directory

Now, at compile time the compiler should be able to find symbols in Products Directory and at launch/runtime the dynamic linker should be able to find Datadog binary in Frameworks folder of the host app

I hope that may help you 🤞

Last resort workaround

As suggested in a discussion in Swift Forums you may remove Embed Frameworks for Datadog manually from pbxproj.

Please let me know if that works for you

@buranmert buranmert self-assigned this Feb 9, 2021
@buranmert buranmert added the awaiting response Waiting for response / confirmation from the reporter label Feb 9, 2021
@bpollman
Copy link
Contributor Author

Thanks for the great response! Will look into the workaround you mentioned.

Another approach that could work would be to present the library both ways, ie:

 products: [
        .library(
            name: "Datadog",
            type: .dynamic,
            targets: ["Datadog"]),
        .library(
            name: "DatadogObjc",
            type: .dynamic,
            targets: ["DatadogObjc"]),
        .library(
            name: "DatadogStatic",
            targets: ["Datadog"]),
        .library(
            name: "DatadogStaticObjc",
            targets: ["DatadogObjc"]),
    ],

Hoping that a proper solutions comes with Xcode soon 🤞

@buranmert
Copy link
Contributor

@bpollman that's a very good idea! can you please go ahead and make this change? then i will merge the PR ✅

I just want to remind you that if you are shipping your framework, your clients will not be able to use Datadog framework in their apps. They will have "duplication of library code" errors.

@bpollman bpollman force-pushed the 1.4.1-remove-dynamic branch from eadd96f to b451e82 Compare February 11, 2021 22:28
@bpollman
Copy link
Contributor Author

Thanks @buranmert! Changes have been made.

With luck these sorts of workarounds won't be necessary by the time we need to ship a framework.

@bpollman bpollman changed the title Swift Package Manager - Remove dynamic definition to allow use inside frameworks Swift Package Manager - Add static library variants to allow use inside frameworks Feb 11, 2021
Package.swift Outdated
Comment on lines 19 to 26
.library(
name: "DatadogStatic",
targets: ["Datadog"]),
.library(
name: "DatadogStaticObjc",
targets: ["DatadogObjc"]),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if Xcode changes its behavior one day, these may end up being linked dynamically
can we please enforce static linking by adding type: .static explicitly?

Suggested change
.library(
name: "DatadogStatic",
targets: ["Datadog"]),
.library(
name: "DatadogStaticObjc",
targets: ["DatadogObjc"]),
.library(
name: "DatadogStatic",
type: .static,
targets: ["Datadog"]),
.library(
name: "DatadogStaticObjc",
type: .static,
targets: ["DatadogObjc"]),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point. Changes have been made.

@bpollman bpollman force-pushed the 1.4.1-remove-dynamic branch from b451e82 to fc961b7 Compare February 14, 2021 22:09
@buranmert buranmert removed the awaiting response Waiting for response / confirmation from the reporter label Feb 15, 2021
@buranmert buranmert added this to the next-version milestone Feb 15, 2021
@buranmert buranmert merged commit 4e952d3 into DataDog:master Feb 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants