Hive Hive
Sign in

[SPM/xcframework] Transitive dependencies of binary targets are not resolved when using local SPM package

GitHub issue · Open

Metadata
Source
tuist/tuist #8056
Updated
Jun 11, 2026
Domains
Generated projects
Details

What happened?

Problem description:

We use a local SPM package added via .package(path:) and .package(product:) that exposes multiple products, each wrapping a binary target (xcframework). In its Package.swift, several targets (wrappers) depend on these binary targets, and also declare dependencies on other packages from our internal registry:

// swift-tools-version:5.8
import PackageDescription
let package = Package(
name: "ExamplePackage",
platforms: [.iOS(.v13)],
products: [
.library(name: "FeatureA", targets: ["FeatureAWrapper"]),
.library(name: "FeatureB", targets: ["FeatureBWrapper"]),
.library(name: "CoreUtils", targets: ["CoreUtils"]),
],
dependencies: [
.package(id: "MyCompany.TransitiveDepA", exact: "1.2.3"),
.package(id: "MyCompany.TransitiveDepB", exact: "4.5.6"),
],
targets: [
// Wrapper depending on a binary xcframework and a registry product
.target(
name: "FeatureAWrapper",
dependencies: [
"FeatureA", // binary
.product(name: "TransitiveDepA", package: "MyCompany.TransitiveDepA")
]
),
.binaryTarget(
name: "FeatureA",
path: "Frameworks/FeatureA.xcframework"
),
// Another wrapper that depends on its own binary and (optionally) the first wrapper and another registry product
.target(
name: "FeatureBWrapper",
dependencies: [
"FeatureB", // binary
"FeatureAWrapper", // intra-package dependency
.product(name: "TransitiveDepB", package: "MyCompany.TransitiveDepB")
]
),
.binaryTarget(
name: "FeatureB",
path: "Frameworks/FeatureB.xcframework"
),
// A pure (non-binary) target also exposed as a product
.target(
name: "CoreUtils",
dependencies: []
)
]
)

When building the app with Tuist, the transitive dependencies of these binary targets are not resolved: the dependencies listed in Package.swift are not propagated to the final Xcode project and are not linked with xcframework wrappers.

Some of these transitive dependencies are used directly by the app target as well, but the linker log clearly shows a linking error for symbols that should come from the transitive dependency of the xcframework. This happens for static xcframeworks (symbols not linked into the .o), and at runtime for dynamic xcframeworks (dependency not found via @rpath).

Where it happens:

According to my research of Tuist’s source code, this happens because in resolveExternalDependencies (PackageInfoMapper.swift), only the direct product.targets are mapped for SPM products, and their dependencies are not traversed recursively. As a result, if a binary target depends on other packages, those dependencies are ignored.

How do we reproduce it?

  1. Create a local SPM package with multiple products in Package.swift. Each product wraps a binary target (xcframework), and some wrapper targets declare dependencies on internal registry packages.
  2. Add this package to your Tuist project via .project.
  3. Build the project with Tuist. Observe that the transitive dependencies of the binary targets are not linked in the final Xcode project, and you get linking/runtime errors when the app uses the xcframework wrappers (even if some dependencies are used by the app target).

Error log

There is no explicit error message from Tuist, but Xcode linker output shows missing symbols for types/functions defined in the transitive dependency of the xcframework (for static). For dynamic xcframeworks, you get a runtime error about missing dependency by @rpath.

Example linker log:

ld: warning: Could not find or use auto-linked library '[NDA_MODULE]_core.a': library '[NDA_MODULE]_core.a' not found
ld: warning: Could not find or use auto-linked library '[NDA_MODULE]_chat.a': library '[NDA_MODULE]_chat.a' not found
ld: warning: Could not find or use auto-linked framework 'SwiftProtobuf': framework 'SwiftProtobuf' not found
ld: warning: Could not find or use auto-linked framework 'Testing': framework 'Testing' not found
ld: warning: Could not find or use auto-linked framework 'XCTest': framework 'XCTest' not found
Undefined symbols for architecture arm64:
"method descriptor for static SwiftProtobuf._ProtoNameProviding._protobuf_nameMap.getter : SwiftProtobuf._NameMap", referenced from:
...
"protocol descriptor for Testing.SuiteTrait", referenced from:
...
"XCTest.XCTFail(_: Swift.String, file: Swift.StaticString, line: Swift.UInt) -> ()", referenced from:
...
"_OBJC_CLASS_$_XCTestContext", referenced from:
...
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1

macOS version

15.5

Tuist version

4.55.6

Xcode version

16.4.0

Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

Comments
D
dosubot[bot] Aug 19, 2025

Hi @GLinnik21! I’m Dosu and I’m helping the tuist team.

This is a known limitation in Tuist: when you add a local SPM package via .project that wraps binary targets (like xcframeworks), Tuist only maps the direct dependencies of each product target and does not recursively resolve or link transitive dependencies for those binaries. This means dependencies declared in your Package.swift that are needed by the binary targets are not automatically linked in the generated Xcode project, which leads to the linker/runtime errors you’re seeing. Fixing this would require changes to Tuist’s dependency mapping logic to traverse and include all transitive dependencies, not just the immediate ones [source].

There are a couple of workarounds:

  • Explicitly declare all transitive dependencies in your Tuist target’s dependencies, even if they are only used transitively by a binary target. You can use tuist inspect implicit-imports to help identify which dependencies are missing and need to be added [source].
  • If you need to add a binary target as a dependency, consider creating a local “umbrella” SPM package that exposes the binary and its dependencies, then add that package as a local dependency in your Tuist project and explicitly declare the external dependency as well [source].

Tuist does not support setups that rely on implicit imports—every dependency must be explicitly declared to avoid linker errors. There is currently no native fix or recent PR addressing this in Tuist, so keeping an eye on future releases or discussions is recommended if this is critical for your workflow.

To reply, just mention @dosu.


Docs are dead. Just use Dosu.

Leave Feedback Ask Dosu about tuist Share Dosu with your team

G
GLinnik21 Aug 19, 2025

Thank you, mr robot

S
shgew Apr 7, 2026

Hitting this with ad SDK xcframeworks. The Tude SPM package declares a dependency on GoogleMobileAds (from swift-package-manager-google-mobile-ads), which wraps a static xcframework. Setting "Tude": .framework in PackageSettings.productTypes produces undefined symbols for all GoogleMobileAds types.

The generated _Tude target has GoogleMobileAdsTarget.framework in its Frameworks build phase but an empty dependencies array and no cross-project reference to GoogleMobileAds.xcodeproj.

Workaround: a local umbrella module that statically links all ad SDKs into one dynamic framework with -ObjC.

Tuist 4.174.2