We have a codebase with many local Swift Packages in a single repository. In order to better keep dependency URLs and versions synced across all of the Package.swift files, it would be beneficial if we could do something like this:
// global file
extension Package.Dependency {
static let nuke = .package(name: "Nuke", url: ..., .upToNextMinor(...))
static let apollo = ...
}
// Package.swift A
let package = Package(..., dependencies: [.nuke, .apollo])
// Package.swift B
let package = Package(..., dependencies: [.apollo])
I'm not working on the same project anymore, but in hindsight, I probably could have used a code gen tool like Sorcery ( or just scripted some sed commands!) to get our package definitions in a single place, but in a large team with various levels of experience, I'm not sure it'd be worth the additional level of obscurity.
I am also interested in any ideas on how to share common package code.
Our current use case is this: We'd like to gradually adopt Swift feature flags across all our packages on our road to Swift 6. Right now, each package manifest has this extension:
But it would be much nicer to define this in one global place and use it across packages (which are all in the same repository and also Xcode project/workspace).
A very similar wish would be to have the same platforms value across all our packages, and ideally even tools version, although that's probably even tricker, given it's a special kind of comment instead of Swift code.
fwiw, my experience lead me to just copy the common code.
I believe it will be years before SPM supports shared code in Package.swift, so for non-trivial packages I end up copying common code to the end of the file and writing scripts to synchronize the copies (essentially an #include). It's ugly but I find that I'm constantly adding features to the common code, making me glad I made the jump.
The structure I use is something like
import PackageDescription
let package = P.makePackage()
/// Keep declarations used in package outside of global scope
private enum P {
static func makePackage() -> Package {
... // use PackageSpec here
}
}
// MARK: - tag for copied common code...
private struct PackageSpec {
...
}
Features I'm using:
SwiftSettings, esp for 6
factories for different target and dependency flavors
switch remote dependencies to local
share common package authors (me, apple, vapor...)
platform sets (latest, last 2 years, etc.)
easy declarations for multi-product packages, dependency lists, version ranges
flags, e.g., linker support for info.plist
support for SPM versions (<5.9, 5.9..<6.0, 6.0+)
above all: no more duplicate string literals :)
I decided not to do code generation because the upgrade/migration time varies a lot; forcing all to migrate in lock-step was a productivity drag.