The Problem
Currently SPM only lets you have Debug and Release configurations.
The problem is, in Xcode, you can have any number of configurations and you can name them whatever you want. For example one app that I worked on had the following three configurations: "Production", "Staging", and "Dev". The current app I work on has "Debug", "Release", and "Testing."
So it's really problematic that Swift packages only support "Debug" and "Release."
Example From Our App
In our app we have an "AllUnitTests" scheme that incorporates all the unit test targets in the workspace, including some that are Swift packages.
However, because AllUnitTests scheme builds with an Xcode configuration called "Testing," this causes the Swift packages to all get built in their "Release" configuration. I've been told this happens because some heuristics code maps configurations called "Testing" to SPM's "Release" configuration. I don't know whether that's intended or just a fallback behavior, but the end result is that when Swift packages' test targets are built under our "Testing" configuration, @testable import doesn't work on any of the tests for that package, because SPM doesn't enable testability for Release configurations.
The Swift packages get built without testability enabled even though our AllUnitTests.xcscheme itself has:
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "MySwiftPackage"
BuildableName = "MySwiftPackage"
BlueprintName = "MySwiftPackage"
ReferencedContainer = "container:Packages/MySwiftPackage">
</BuildableReference>
</BuildActionEntry>
One would think that, regardless of the name of the configuration, buildForTesting = "YES"
ought to be enough to clue Xcode to, y'know, enable testability on the target being built. But I realize that "build for testing" and "enable testability" are two different concepts that just sound similar, and there might be times when you want to build a release configuration for testing.
Pitched Solution(s)
We need a solution where Xcode configurations with custom names will play nice with Swift packages. Ideally it would allow a Swift package to inherit build settings from a shared .xcconfig file.
Firstly we'd need a way to create a BuildConfiguration
with a custom name, e.g.:
BuildConfiguration.custom("Testing")
... so at a bare minimum you could do:
targets: [
.target(
name: "MyPackage",
dependencies: ["SomePackage", "SomeOtherPackage"],
swiftSettings: [
.define("ENABLE_TESTABILITY",
.when(configuration: .custom("Testing"))
)
)
]
This would be good for one-offs but it wouldn't scale very well in a large project with 50+ modules like our application. What we really need before we can feasibly transition away from all our modules existing as Xcode frameworks, is a way to share configurations across multiple Swift packages using xcconfig files or something equivalent to them, e.g.:
targets: [
.target(
name: "MyPackage",
dependencies: ["SomePackage", "SomeOtherPackage"],
xcconfigs: [
XCConfig(
path: "../../SharedConfigs/Testability",
config: .custom("Testing")
)
]
)
]
This would be ideal for our circumstance because we currently keep all our build settings defined in a centralized place so that individual teams making new modules don't have to reinvent any wheels and it's easy to adjust the settings used by all modules in a single place rather than having to edit a bunch of separate files and hope you didn't miss one.
We sorely want to use Swift Packages for all of our monorepo's modules because Xcode project files (with their random GUIDs for everything) are a common source of git merge conflicts, and often become corrupt due to people not resolving those conflicts correctly. To never have to deal with Xcode project files ever again would make everyone's lives 1000% better, but the lack of flexibility in how SPM deals with build configurations is currently a blocker to us making this transition because we can't convert all modules to packages in a single PR and it's also impractical to change our custom configuration names.
Counter-arguments and Counter-counter-arguments
The proponents of the current design have said supporting custom configuration names would be bad because, if some public package uses some weird custom configuration name, then you might not be able to use it in your app without also adopting that custom name.
However I think it should be up to the author of a Swift package whether to use the "standard" names or whether to support custom configuration names, because not all Swift packages are public—many of them are private packages used merely as the easiest, simplest way to modularize code within an Xcode project. And maybe there are configurations we'd like consumers of a package not to use, like ones geared towards package development and testing, as opposed to debugging and release.
Also, I don't think that library authors would abuse custom configuration names, because authors of public libraries are smart enough to know not to do that, as evidenced by the fact that we haven't had this problem up to now with libraries that were published as Xcode framework projects. In 10 years I've never seen a public library that was incompatible with a project I've worked on due to using a weird config name.
So I don't think it's a rational fear to say that supporting more than just two configurations would cause problems.
Closing
Please let me know how we can get this pitch into a proposal and move forwards. Thanks!