How to use a local Swift Package which provides two libraries?

951 Views Asked by At

I have a local swift package Foo which provides two different libraries Foo and FooB. Now I would like to use them in another local package Bar. I am only able to get the whole package by path declaration. Is there a way to name/specify which library should be used? I want to use FooB in my Bar Package.

let package = Package(
    name: "Foo",
    products: [
        .library(name: "Foo", targets: ["Foo"]),
        .library(name: "FooB", targets: ["FooB"])
    ]

...)
let package = Package(
    name: "Bar",
   dependencies: [
        .package(path: "Foo"),
        .package(path: "FooB") // this one does not work
    ],

...)
// inside package Bar

import Foo
import FooB // this is throwing "no such module 'FooB'"
2

There are 2 best solutions below

0
On

Here's how you can achieve it: specify the target that depends on the specific library from your local Swift package if you want to use it in another package.

☼ For proper local packaging of Foo, it is essential that each library is represented in separate targets. In order to achieve this, amend your Package.swift for Foo in the following manner:

let package = Package(
    name: "Foo",
    products: [
        .library(name: "Foo", targets: ["Foo"]),
        .library(name: "FooB", targets: ["FooB"])
    ],
    targets: [
        .target(name: "Foo", dependencies: []),
        .target(name: "FooB", dependencies: ["Foo"])
    ]
)

Foo and FooB are the two targets defined, with FooB being dependent on Foo.

☼ Then you can specify the dependency on FooB in your Bar package by rearranging the text.

let package = Package(
    name: "Bar",
    dependencies: [
        .package(path: "../Foo")
    ],
    targets: [
        .target(
            name: "Bar",
            dependencies: [
                .product(name: "FooB", package: "Foo")
            ]
        )
    ]
)

The Bar target has a dependency on the FooB library, specified by using the product function to point to the package "Foo" and its library "FooB".

☼ Your Bar package can now make use of the FooB library by simply importing it.

import FooB

Correct import of the FooB library from the Foo package can be accomplished by making specific changes to your Bar package.

According to your project's directory structure and package names, make sure you adjust the paths and package names accordingly. :)

0
On

Here's a concrete example:

Foo

Playing Foo, IGListKit Package.swift

excerpts:

products: [
    .library(name: "IGListDiffKit",
             type: .static,
             targets: ["IGListDiffKit"]),
    .library(name: "IGListKit",
             type: .static,
             targets: ["IGListKit"]),

targets: [
    .target(
        name: "IGListDiffKit",
        path: "spm/Sources/IGListDiffKit"
    ),
    .target(
        name: "IGListKit",
        dependencies: ["IGListDiffKit"],
        path: "spm/Sources/IGListKit"
    ),

Bar

In the role of Bar, in its entirety:

import PackageDescription

let package = Package(
    name: "HistoryList",
    platforms: [.iOS(.v13)],
    products: [
        .library(
            name: "HistoryList",
            targets: ["HistoryList"]),
    ],
    dependencies: [
        .package(name: "IGList", url: "https://github.com/Instagram/IGListKit", from: "4.0.0"),
    ],
    targets: [
        .target(
            name: "HistoryList",
            dependencies: [
                .product(name: "IGListDiffKit", package: "IGList"),
                .product(name: "IGListKit", package: "IGList")
            ]
        ),
    ]
)

Pruned Package

after importing, the Package.swift of Foo in the Package Dependencies section states only:

let package = Package(
    name: "IGListKit",
    products: [
        .library(name: "IGListKit", targets: ["IGListKit"])
    ],
    targets: [
        .target(
            name: "IGListKit",
            path: "Source"
        )
    ]
)

It seems that Xcode / SPM is pruning the imported package to contain only one of the three libraries in this package. (Initially, the second library's code was missing, one my my syntaxes caused it to be pulled, but the first was not seen. They are now both there, but only one is seen.)

I couldn't determine a syntax to make it bring down both.

Current Error

product 'IGListDiffKit' required by package 'ios-malauzai-history-list' target 'HistoryList' not found in package 'IGList'.

Xcode 15.0 beta (15A5160n)

Additional Thoughts

package(name:url:_:) (and the name property of Package.Dependency) has been deprecated, but you need to use the name of the package dependency to use in the specific target dependency.

This leads me to believe there is a newer syntax that this that should be used.

Resolution

Instagram doesn't fully support Swift Package Manager for IGListKit

(There are better solutions than IGListKit, but this is in legacy code. Currently, there are 191 commits between the 4.0.0 tag and the HEAD of the repository; it's been 3+ years since a release was made.)

The problem in this case was that the latest tag (as of Jun 2023) is the 4.0.0 tag, and the version of the Package.swift file is literally the abbreviated version I was receiving.

Everything worked when I updated, and simplified the description to look at the commit that added the support. (Could have used main branch, but that will keep changing.)

…
    dependencies: [
        .package(url: "https://github.com/Instagram/IGListKit", revision: "c9e045c9425a160871a7915260d07a6c273b548d")
    ],
    targets: [
        .target(
            name: "HistoryList",
            dependencies: [
                .product(name: "IGListKit", package: "IGListKit"),
                .product(name: "IGListDiffKit", package: "IGListKit")
            ]
        ),
…