Marvin.Hansen
Marvin.Hansen

Reputation: 1638

Swift Package Manager: How to add local shared library as dependency to multiple executables?

I have a simple gRPC app, a client and a server. Both rely on on generated swift-grpc bindings. For simplicity, when I copy the shared files once into the client folder and a second time into the server folder, swift builds and runs both. For obvious reasons, I want to separate the shared files into, well, a shared lib, a server lib, and a client lib with the goal to import only shared and client specific libs into the client.

Beginning with my shared lib, called DmxLib, I can't get my head around the Package.swift file. Essentially, the docs say, make a lib, add it as dependency and here you go. When I do that, Swift build fails saying the imported files aren't there. When I add the local files from the DmXLib folder to the product dependencies, it tells me the file cannot be found.

Editor is VSCode and platform is Ubuntu 20.04 with Swift 5.3, no XCode.

How exactly do I correctly declare a shared local library that can be imported and actually be used in both, the client and the server?

My source tree looks like so

enter image description here

And my Package.swift file

        // swift-tools-version:5.2
        // The swift-tools-version declares the minimum version of Swift required to build this package.

        import PackageDescription

        let package = Package(
            name: "dmx-db",
            products: [
                .executable(name: "DmxServer", targets: ["DmxServer"]),
                .executable(name: "DmxClient", targets: ["DmxClient"]),
                // This lib will be imported into both, client and server. 
                .library(name: "DmxLib", targets: ["DmxLib"]),
            ],  
            dependencies: [
                .package(url: "https://github.com/grpc/grpc-swift.git", from: "1.0.0-alpha.21"),
                .package(url: "https://github.com/codewinsdotcom/PostgresClientKit", from: "1.0.0"),
                .package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "1.0.0-alpha.5"),
                // Why do I have to declare local files as dependencies???
                .package(path: "../Sources/DmxLib/datasource.grpc.swift")
                ],
            //
            targets: [
                .target(name: "DmxLib", dependencies: [
                    "datasource.grpc.swift" 
                ] ),

                .target(name: "DmxServer", dependencies: [
                    "DmxLib", 
                    .product(name: "GRPC", package: "grpc-swift"),
                    .product(name: "PostgresClientKit", package: "PostgresClientKit"), 
                    .product(name: "Lifecycle", package: "swift-service-lifecycle"), 
                    ]),

                .target(name: "DmxClient", dependencies: [
                    "DmxLib",
                    .product(name: "GRPC", package: "grpc-swift"),
                ]),
            ] 
        )

Upvotes: 0

Views: 2702

Answers (2)

ingconti
ingconti

Reputation: 11646

for LOCAL we suffer for a bad Xcode UI/UX..

(in my sample I have a SPM, "LocalSPMLib" and a consumer App, "LocalSPMConsumerApp")

You can do in this way:

  1. add SPM as usual adding via SPM menus or dragging into project.

it will add to project, but will not work... Should appear under binaries but NOT ;(

  1. RE-add as a Binary Lib (awful... but works..)

enter image description here

Now You can invoke:

import XCTest
import LocalSPMLib

@testable import LocalSPMConsumerApp

class LocalSPMConsumerAppTests: XCTestCase {

    override func tearDownWithError() throws {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
    }

    func testExample() throws {
        XCTAssertEqual(LocalSPMLib().version, 1)

    }

}

Upvotes: 2

Sam
Sam

Reputation: 2456

This should do it. You don't need to add a "dependency" to a target where all the sources are on your local computer.

// swift-tools-version:5.3

import PackageDescription

let package = Package(
    name: "dmx-db",
    products: [
        .executable(name: "DmxServer", targets: ["DmxServer"]),
        .executable(name: "DmxClient", targets: ["DmxClient"]),
                // This lib will be imported into both, client and server. 
        .library(name: "DmxLib", targets: ["DmxLib"]),
    ],
    dependencies: [
        .package(url: "https://github.com/grpc/grpc-swift.git", from: "1.0.0-alpha.21"),
        .package(url: "https://github.com/codewinsdotcom/PostgresClientKit", from: "1.0.0"),
        .package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "1.0.0-alpha.5"),
    ],
    targets: [
        .target(name: "DmxServer", dependencies: [
            "DmxLib", 
            .product(name: "GRPC", package: "grpc-swift"),
            .product(name: "PostgresClientKit", package: "PostgresClientKit"), 
            .product(name: "Lifecycle", package: "swift-service-lifecycle"), 
        ]),
        .target(name: "DmxClient", dependencies: [
            "DmxLib",
            .product(name: "GRPC", package: "grpc-swift"),
        ]),
        .target(name: "DmxLib", dependencies: []),
    ]
)

This does require that you have a Sources directory in the root directory of your package, and inside it you have three folders, named DmxLib, DmxClient, and DmxServer, respectively.

Note: I've actually updated the swift-tools-version to 5.3, because I copied and modified this from one of my projects, but I think it should work with a swift-tools-version of 5.2

Upvotes: 1

Related Questions