Search code examples
iosswifttestingbuildswift-package-manager

Swift Package referencing another Swift Package locally, not recognized by test target


I've been trying to refactor an existing swift package I made into multiple swift packages.

I started by moving code from the original package ("Parent") into a second package ("Child") and they're in the same xcworkspace. After making the code I needed public and using import Child on the files where those method signatures from Child are called, the main target builds.

The issue arises when I try to run tests. The tests inside the Child package run fine, but when the Parent's tests try to build it fails to link with these errors...

Undefined symbol: protocol descriptor for Child.SomeProtocol

Undefined symbol: (extension in Child):Child.SomeProtocol.method(param: CoreGraphics.CGFloat) -> CoreGraphics.CGFloat

Undefined symbol: static (extension in Child):Swift.Double.computedProperty.getter : Swift.Double

...with another indicator pointing to the import statement.

First thing I did was add the Child target to the Parent's build scheme, with "Test" and "Run" checked off. Then I tried adjusting the Parent's package file by adding the Child to the test target's dependencies...

targets: [
    .target(
        name: "Parent",
        dependencies: [],
        resources: [.process("Resources")]),
    .testTarget(
        name: "ParentTests",
        dependencies: ["Parent"/*, "Child"*/]),  // <-- Part inside /* comments */ is what I added
]

Then my errors change to...

product 'Child' required by package 'Parent' target 'ParentTests' not found.

Although the project acts like it's built successfully, I only see the error when I try to run tests and get a pop up that says:

There are no test bundles available to test.

Ensure that all package dependencies have resolved and that there are no missing test bundles in the active scheme or test plan.

I found instructions for hosting the package on github, but that introduced a whole other set of problems. To keep it simple, how do I properly link the Child package to the Parent as a local dependency in my workspace?

Also, I noticed that the issue is not just with tests, but when I try to build the host App that owns the Parent package I get similar errors.

Below are the actual packages for my Parent ("Trenches_Engine") and Child ("Math") packages...

// swift-tools-version:5.5.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Trenches_Engine",
    platforms: [
        .iOS(.v14)
    ],
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "Trenches_Engine",
            targets: ["Trenches_Engine"]),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "Trenches_Engine",
            dependencies: [],
            resources: [.process("Resources")]),
        .testTarget(
            name: "Trenches_EngineTests",
            dependencies: ["Trenches_Engine", "Math"]),
    ]
)
// swift-tools-version: 5.6
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Math",
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "Math",
            targets: ["Math"]),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "Math",
            dependencies: []),
        .testTarget(
            name: "MathTests",
            dependencies: ["Math"]),
    ]
)

And here's the protocol/extension not being recognized (and the only one currently in the package)...

import CoreGraphics

/// This protocol provides `random` and `random:min:max` methods that return CGFloat.
public protocol CanRandom { }

// TODO: Replace this with new random methods...
public extension CanRandom {
    
    /// This method returns a random CGFloat without any setup parameters.
    ///
    /// - Returns: CGFloat(Float(arc4random()) / 0xFFFFFFFF)
    func random() -> CGFloat { CGFloat(Float(arc4random()) / 0xFFFFFFFF) }
    
    /// This method returns a random CGFloat, within the range defined by parameters.
    ///
    /// - Parameter min: The floor of acceptable range of random numbers.
    /// - Parameter max: The ceiling of acceptable range of random numbers.
    ///
    /// - Returns: random() * (max - min) + min
    func random(min: CGFloat, max: CGFloat) -> CGFloat {
        random() * (max - min) + min
    }
}

Solution

  • It requires a different package method in Package's dependencies...

        dependencies: [
            // Dependencies declare other packages that this package depends on.
            .package(name: "Child", path: "../Child")
        ]
    

    Along with this in the "targets" array:

        .target(
            name: "Parent",
            dependencies: [.product(name: "Child", package: "Child")],
            resources: [.process("Resources")]),