ARTICLE AD BOX
Undefined Symbol in Test Target When Using SPM Umbrella Libraries
I'm migrating a large project from CocoaPods to Swift Package Manager. The project has a main app target and a test target in Xcode. My approach was to create two "umbrella" SPM products — AppModules (linked to the app target) and AppTestModules (linked to the test target) — that re-export all dependencies transitively.
Here's a minimal reproduction of the package structure:
// swift-tools-version: 5.10 import PackageDescription let dynamicProducts: Set<String> = ["AppModules", "AppTestModules"] let targets: [Target] = [ // Feature module with production code .target(name: "FeatureA"), // Umbrella linked to the main app target under Frameworks & Libraries .target(name: "AppModules", dependencies: ["FeatureA"]), // Test support module: mocks, helpers, fixtures for FeatureA .target(name: "FeatureATestSupport", dependencies: ["FeatureA"]), // Umbrella linked to the test target under Frameworks & Libraries .target(name: "AppTestModules", dependencies: ["FeatureATestSupport"]), ] let products: [Product] = targets.map { target in if dynamicProducts.contains(target.name) { return .library(name: target.name, type: .dynamic, targets: [target.name]) } else { return .library(name: target.name, targets: [target.name]) } } let package = Package( name: "MyModules", platforms: [.iOS(.v16)], products: products, targets: targets )Xcode target linkage:
MyApp → links AppModules.framework MyAppTests → links AppTestModules.frameworkProblem 1 — Undefined symbol at link time:
The app target builds and runs fine. However, building the test target produces:
Undefined symbols for architecture arm64: "_$s8FeatureA9SomeClassCN", referenced from: _$s20FeatureATestSupport14SomeTestHelperCN in AppTestModules.frameworkWhat's I think is happening, can someone confirm?:
FeatureA is imported in a test file. FeatureA is a static library (no type: specified). When SPM builds AppModules.framework (dynamic), it merges FeatureA's object code directly into it — so FeatureA's symbols live inside AppModules.framework's binary.
The test target only links AppTestModules.framework which depends on FeatureATestSupport.Our initial assumption was that since the test target already had @testable import MyApp, it would have access to all symbols that MyApp links — including everything merged into AppModules.framework. The thinking was:
MyApp links AppModules.framework, which contains FeatureA. If the test target imports MyApp, it should be able to see FeatureA's symbols transitively.
However the linker error persists regardless of @testable import.
What fixes it:
Adding FeatureA as an explicit dependency of AppTestModules resolves the linker error. The test target can now link FeatureA directly. The unit tests run, however now I get a large list of these errors and simulator eventually crashes.
objc[XXXX]: Class GADOMIDWindowProcessor is implemented in both /path/to/DerivedData/MyApp/Build/Products/Debug-iphonesimulator/PackageFrameworks/AppModules.framework/AppModules (0x...) and /path/to/CoreSimulator/Devices/.../MyApp.app/PlugIns/MyAppTests.xctest/MyAppTests (0x...). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed. failed to demangle superclass of SomeViewSpec from mangled name '\377"\247': subject type x does not conform to protocol SomeProtocol CoreSimulator XXXX.XX.X - Device: iPhone XX (DEVICE-UUID) - Runtime: iOS XX.X (XXXXX) - DeviceType: iPhone XXIt looks like I have two sets of symbols now and at runtime it doesn't know how to resolve it and causes a crash.
My question:
Is there a cleaner structural approach for this pattern? The two problems together mean:
Any static library reachable from the app but not from the test umbrella causes a linker error Any static library shared between two or more dynamic frameworks causes a runtime type identity crashWill making everything dynamic solve the problem?. Is there an idiomatic way to structure umbrella products so the test target can see all symbols?
