Search code examples
swiftswift-package-manager

Swift packages: sharing internal code between libraries but not library users


How should I share code between modules (targets in Swift package terms) defined in Package.swift while still exposing APIs from the shared code to library users? Another way to phrase this question is: How to share internal API in module with specific other modules (other libraries) but not library users.

For example, say I have 2 libraries, Human and Dog, and users only need 1 of them (or use both if they want). I came up with 2 approaches:

  • I could make another library, Core, which contains public APIs that should be shared to library users (of Human and Dog), and use a separate internal library Internal.
    • This prevents setting access control for entities (methods, enums, classes, etc) in each file separately, to avoid overexposing things in Core which might not be able to go into Internal. This is because I always need to set the entities to public for access in Human or Dog, meaning they'll be public to library users too.
    • Library users who want to use library Human will also have to know to import both Human and Core to their Xcode project/ source files.
  • Explicitly add the shared code directories to both targets instead of 1, using target(sources:) documented here. This actually throws an error if 1 source file is shared between 2 targets: target 'Library Human' has sources overlapping sources.

I think the 2nd option is better but it doesn't work. Perhaps there is a better option I don't know about?

The reason why they need to be in separate modules is because one (Human) depends on a rather large dependency which should not be required for Dog.


I originally posted this on Swift forums here, and will update here with a solution if I found one.


Solution

  • I solved this by doing the first method I listed in the question (4 modules):

    • Core: Code shared by library A and B, which should be importable by the user.
    • Internal: Code that library users should not be able to import. Depends on Core. Also depends on other libraries which shouldn't be exposed to the library user.
    • Library A (e.g. Human): One library the user may choose to use.
    • Library B (e.g. Dog): Another library the user may choose to use.

    I then in Library A and Library B, I have a file called Exports.swift which has @_exported import Core.