I come to get some advice about package dependency designs. Please forgive the verboseness. I failed to express what exactly I want to ask you for briefly and elegantly because I don't know the terms related to what I ask for. So I wrote the following long, which might be boring, story.
I have a project to develop an app having some features that cooperate. As it gets larger in the codebase size, I decided to separate the features into private Swift packages, with a vague expectation that they might be re-used another day. All seemed reasonable at the moment. Each package has a repository (remote and local). After making some changes, I commit and push them. The changes are automatically applied to my project by clicking "Update to Latest Package Versions" in Xcode. I have tried using local packages once but moved to remote ones since I usually work in many places.
Simply say, there are packages A, B, and C. Package A is a collection of small utility functions. B and C have a dependency on A. All of the packages are imported into the project. I wanted to make some changes to A for experimental purposes, so I created a branch and made the changes. The changes don't affect any features that package B and C depend on. I wanted to replace package A with a version of the test branch. I didn't want to merge the changes into the main branch until I was sure it still worked perfectly after the changes. I changed the version rules in Package Dependencies. Complaints came out from packages B and C since they expected the package to be the main branch version, as written in their package.swift file.
Now I have to go to packages B and C and modify the package.swift file to make them expect the test branch of A and commit & push the changes. This is not good. I didn't expect this at the moment I designed them.
I tried to redesign packages B and C, so they don't require package A explicitly and reveal their features only if package A is available, using
#canImport. It didn't help at all. I learned it doesn't work as expected in the Swift package due to how the compiler works.
The story tells the problem simpler than it is. The actual situation is I ended up with an ugly dependency tree, and if I touch a node, it forces me to touch all the nodes that depend on the node. What a demanding naughty tree!
Now I feel something was deeply wrong from the moment I designed them. Should package A be just some files to be included wherever they are needed, not as a package? Or is there a way to work around this situation?
I have thought about making another package containing only the parts that depend on packages A and B. However, it will be worse because multiple dependencies generate packages corresponding to the intersections from the possible combinations.