SwiftPM and swappable libraries?

I've got a peculiar use-case that I was wondering if anyone has any good solutions for. The Kitura project has a dependency on a network layer... this network layer was originally provided by Kitura-net, but then NIO came along, and the previous maintainers/authors created an API compatible library called Kitura-NIO. However, they both provide the KituraNet product name. So basically right now there are two implementations of the same API, and I'd like an easy way for people adding Kitura to their project to be able to decide which implementation to use.

The previous authors came up with a clever solution using environment variables. Here's a snippet of the Package.swift of the current implementation:

var kituraNetPackage: Package.Dependency

if ProcessInfo.processInfo.environment["KITURA_NIO"] != nil {
    kituraNetPackage = .package(url: "https://github.com/Kitura/Kitura-NIO.git", from: "2.4.200")
} else {
    kituraNetPackage = .package(url: "https://github.com/Kitura/Kitura-net.git", from: "2.4.200")
}

let package = Package(
    ...
    dependencies: [
        kituraNetPackage,
        ... various dependencies ...
    ]
    ...
)

This works really well on the server. However the problem I'm facing is that if wanted to use Kitura in an iOS project with SwiftPM, there is no way to provide the KITURA_NIO environment variable to SwiftPM through Xcode. I've tried placing it in the run scripts and setting the environment variable in the scheme editor to no avail.

I thought I might be able to use SwiftPM's multi-product description, something like this:

    ...
    products: [
        .library(
            name: "Kitura",
            targets: ["Kitura"]
        ),
        .library(
            name: "KituraNIO",
            targets: ["Kitura"]
        )
    ],
    dependencies: [
        .package(url: "https://github.com/Kitura/Kitura-NIO.git", from: "2.4.200"),
        .package(url: "https://github.com/Kitura/Kitura-net.git", from: "2.4.200"),
        ... various dependencies ...
    ]
    ...

However, the problem here is that SwiftPM recognizes Kitura-NIO and Kitura-net both provide the same target and I get this error:

error: multiple products named 'KituraNet' in: Kitura-NIO, Kitura-net
error: multiple targets named 'KituraNet' in: Kitura-NIO, Kitura-net

I could create separate branches, but then I lose the version selection capability that SwiftPM offers.

Does anyone else have any good suggestions? Has anyone seen a similar problem before?

Thanks

1 Like

It's a hack, but if you go the environment variable route, the integrated manifest loader will inherit Xcode's environment, so something like KITURA_NIO=1 open Package.swift -a Xcode should select the appropriate dependency. It's not a very good solution, but I think it's the best one available today and is used by a few of the Swift repos.

In the long term, a better solution will likely involve some sort of mechanism for feature toggles that can be exposed to package clients.

2 Likes

One caveat to keep in mind for the hack, it only works if Xcode isn't running already.

Terms of Service

Privacy Policy

Cookie Policy