Swift Package dependency managment

HI team
I have a question with the SPM dependency management.
Lets say my SDK named ASDK. In the ASDK, I use BSDK(=> GSDK, HSDK, ISDK), CSDK, DSDK as its dependency.
For an application FAPP, the dev adds B SDK as its own dependency.
The graph likes:

When I build the application for the first time, it shows the errors like "Target has copy command from '....' '' but then now the error has gone...

Please help us with:

  1. How the SPM manage the deps graph for our case? Will it cause any conflicts?
  2. Does the dependency graph above cause any potential performance concerns? As after we implemented that, the Package.resolved has been added with all the dependencies of the BSDK. We are concerning it will cause issues or not.
  3. We want to actively control how many dependencies will be pulled by conditions. Is there anyway to do that? Like: for BSDK, we just want to load some of its dependencies and exclude the others as we dont use that feature.
  1. How the SPM manage the deps graph for our case? Will it cause any conflicts?
  2. Does the dependency graph above cause any potential performance concerns? As after we implemented that, the Package.resolved has been added with all the dependencies of the BSDK. We are concerning it will cause issues or not.

If different versions of the same dependency occur in the dependency graph then during the resolution process only one version of that dependency will be picked (by default the most recent one). However, the resolution process will fail if the graph has conflicting version requirements, like incompatible exact or major versions.
For your case, it means that only one version of GSDK, HSDK, and KSDK will be picked during the resolution process, avoiding this way any sort of code duplication in the resulting bundle/binary. Does it answer your question about "performance concerns"?

Hint: To have a more detailed look at the resolved graph run from your package root folder: swift package show-dependencies.

  1. We want to actively control how many dependencies will be pulled by conditions. Is there anyway to do that?

It depends on what you mean by "be pulled"? Each package dependency has to be fully checked out (therefore downloaded) before it can be used (along with all its transitive dependencies). However, if there are some products within the package that you don't depend on then they won't go into the resulting bundle/binary. Therefore you do not need to "actively control" anything to "exclude" the dependencies that your package doesn't depend on. Does this answer your question?

1 Like

Thank you @dmhts
Your response is clear and solved my all concerns.

Just one concern left for the mismatch version: when this happen, I believe the host app might need to solve them manually or do we have any better solution for it?

Lets say ASDK is using a specific version of the BSDK(v1.0). The FAPP is expecting the BSDK(v1.3) due to the app requirement(because of bug fixes, new feature,...).
My first thought is Is there any dependency modularization technique which scoping the dependencies of FAPP and A SDK?

Lets say ASDK is using a specific version of the BSDK(v1.0). The FAPP is expecting the BSDK(v1.3) due to the app requirement(because of bug fixes, new feature,...)

It depends on how strictly you specify your version requirements, e.g. the next configuration would fail to resolve because the requirements are too strict:

// ASDK
.package(url: "https://BSDK", exact: "1.0.0")

// FAPP
.package(url: "https://BSDK", exact: "1.3.0")

// Failed to resolve dependencies because 'FAPP' depends on 'BSK' 1.3.0
// and 'ASDK' depends on 'BSDK' 1.0.0.

On the other hand, if you relax the ASDK's requirement so it accepts any version up to the next major one it would work out:

// ASDK
.package(url: "https://BSDK", from: "1.0.0")

// FAPP
.package(url: "https://BSDK", exact: "1.3.0")

Normally, if you're dealing with source code dependencies (unlike binary ones) being not so strict about your version requirements would be the recommended way, because with semantic versioning the backward compatibility must be preserved up to the next major version. If your dependencies are binary it gets more complicated and it's already another story.

More on version requirements in the documentation.

Thank you @dmhts
By defining a version-based requirement, the SPM will resolve the graph automatically.
What about my last concern about a namespace/scope for the dependency? Dont we have it in SPM or its just not? Im understanding that the SPM will pull all dependencies then when bundling the result the SPM picks up what it needs from that big local storage. Is my statement correct?

I'm unsure what exactly you mean by "a namespace/scope for the dependency". If you're referring to the dependency scope concept used in Maven, there is no direct equivalent in SwiftPM. Given that, I think you can roughly say that there is one "big local storage" of all package dependencies that are present on your local machine as a result of the resolution process.
You can find this "local storage" under ~/Library/Developer/Xcode/DerivedData/YourProject/SourcePackages and ~/Library/Caches/org.swift.swiftpm.

@dmhts Thank you very much.
I apologize for the lack of information on that. I mean Im concerning a way that we can make the dependencies will be scoped by its parent package. Ex: the dependencies of FAPP will be separated with the ASDK's dependencies.

And please help me with one more concern: As you presented before, can we also affirm that no conflict if having two versions of the same package (1 in your library and 1 in the client app), even when that package uses the @objc annotation? We are seeing that seems impossible since it requires the Objective-C runtime to register the symbols on loading.

In case of any conflicts, I really appreciate your advice on the resolution for this specific case.

no conflict if having two versions of the same package (1 in your library and 1 in the client app), even when that package uses the @objc annotation?

Yes, only one version of the package will be picked. Whether dynamic dispatching is used or not it's no longer a concern of SwiftPM - I don't see how it can be related here.

Thank @dmhts
What about the objc in dynamic loading?
Lets say

  • ASDK pulls BSDK version 2.3.4
  • FApp pulls BSDK version 3.0.1.

It builds fine, but what version is actually loaded at runtime is undefined. Since the versions aren't compatible (3.0.1 has breaking changes over 2.3.4), something ought to break at runtime.
The app or the library get a version that may not be compatible with what they want.