I recently stumbled across a small issue how to organize code into packages in SwiftPM.
Simplified example: Let's say I have a local package which contains two targets.
- One target (
Authorization) specifies a protocol
Authorization to be used by other local packages in the app.
- The second target (
AppAuthorization) contains a type conforming to the protocol
Authorization, only to be used in the app itself.
What I would like is to have a package where only the app depends on
AppAuthorization, and other local packages on
What I can do is have a package 'Authorization' with two libraries
Authorization which contain the respective targets. The target
AppAuthorization dependends on the target
If I understand it correctly, I would then get the binary for the
Authorization protocol/module twice: Once through the library
Authorization which the other local packages depend on, once through the library
AppAuthorization which should contain it as well because of the dependency of target
AppAuthorization to the target
While this seems to work with local packages (maybe because of static linking), I assume it would blow up if I move the package to non-local and dynamic linking?
If a target could depend on a library in the same package, I could go for:
AppAuthorization with target
Authorization with target
AppAuthorization depending on library
Authorization in the same package.
Could be there is a reason that is disallowed though. Am I missing something?
For this and a variety of other reasons, the distinction between
target has proven to be mostly counterproductive. Several team members have mused of a hypothetical future where the distinction is removed. Reducing it to a simple
public: Bool parameter on
.target that mirrored the
public ACL token would solve a lot of problems in one fell swoop. Unfortunately the two concepts were designed and implemented separately in the early days of SwiftPM, and thus it would be a massive overhaul to unify their code.
That last italicized sentence is the only reason a target cannot depend on a library. It is not deliberately disallowed; the API for it is just absent. And since the associated implementation would have to stitch together and intertwine what is currently two separate loading phases, the amount of work involved has thus far deterred anyone from attempting it.
Thanks for the insight into how things got the way they are!
I would love to try how far I can get with this. If I can’t put enough time in, it will be a learning opportunity anyway.
As it would be a purely additive change as far as I can tell, would I need to submit an evolution proposal, or would an issue+PR be enough?
Unifying the internal implementation would not have any noticeable effect to users on its own, so it probably does not need to go through evolution, though you should still seek feedback from the team before you start. It will definitely be far more complicated than the sort of thing you tag “good first issue”.
But the final step of actually changing or adding to what the manifest declarations can mean is a user‐facing change to the language and does need to go through evolution.
Thanks @SDGGiesbrecht, I will pitch this in the evolution thread. Code-wise I have an experimental version, the code changes seem to be smaller than expected—or I am missing something which is way more likely ;)