How to obtain schemes from Package.swift

Hi folks!

We are working on a script that use xcodebuild to build and test a given Swift package intended for iOS. The question we have is what's a good way to obtain local schemes from the given package?

One way is to use xcodebuild -list -json but that will also include the scheme from the dependent packages, and there doesn't seem to be a way to distinguish them. Is there a way to get around this?

Another way is try to infer the generated the schemes from Package.swift. The only thing we could find is this description from this page:

Xcode creates a scheme for each product in the package manifest. Select a scheme for the package’s build-and-run destination, and build it as you’d build an app target. Each source target usually has at least one corresponding test target. If your package contains multiple products, Xcode creates an additional scheme with the name `` [packageName] -Package to build all targets and run all unit tests.

Xcode creates a scheme for each product in the package manifest

Unfortunately this description is a bit vague. Spefically what will be the name of the generated scheme for each product?

By editing Package.swift, delete .swiftpm and run xcodebuild -list -json we have following observations:

Package.swift

let package = Package(
    name: "Foundation",
    products: [
        .library(
            name: "FoundationLib",
            targets: ["Foundation"]),
    ],
    dependencies: [
    ],
    targets: [
        .target(
            name: "Foundation",
            dependencies: []),
        .testTarget(
            name: "FoundationTests",
            dependencies: ["Foundation"]),
    ]
)

will yield a scheme name which is different from the product name

{
  "workspace" : {
    "name" : "Foundation",
    "schemes" : [
      "Foundation"
    ]
  }
}

For multi-product packages like below:

let package = Package(
    name: "Foundation",
    products: [
        .library(
            name: "FoundationLib",
            targets: ["Foundation"]),
        .library(
            name: "FoundationLib2",
            targets: ["Foundation2"]),
    ],
    dependencies: [
    ],
    targets: [
        .target(
            name: "Foundation",
            dependencies: []),
        .target(
            name: "Foundation2",
            dependencies: []),
        .testTarget(
            name: "FoundationTests",
            dependencies: ["Foundation"]),
    ]
)

it yields schemes which has same name as product name.

{
  "workspace" : {
    "name" : "Foundation",
    "schemes" : [
      "Foundation-Package",
      "FoundationLib",
      "FoundationLib2"
    ]
  }
}

Wonder if it is possible to document this behavior and logic to make inferring possible?

Thanks in advance.

1 Like

What do you mean by "local" here?

That's not the case in my experience. We're using xcodebuild -list -json in the Swift Package Index build system and when running xcodebuild -list on the Swift Package Index project itself (which has many dependencies), I get

❯ xcodebuild -list -json
{
  "workspace" : {
    "name" : "spi-server",
    "schemes" : [
      "DependencyResolution",
      "Run",
      "SPI-Server",
      "SPI-Server-Package",
      "SwiftPM-Perf"
    ]
  }
}

The general rule is that you get only one scheme if there's a single product, otherwise you get the -Package scheme.