Excluding SPM packages from building in specific targets - ios

Say I have some debugging library, and I want it to not be included in the production versions of my iOS app, how can I achieve this with Swift Package Manager? so in short, I want it to be only part of development builds.

You can conditionally include a package in a target, reference.
// swift-tools-version:5.3
import PackageDescription
let package = Package(
name: "BestPackage",
dependencies: [
.package(url: "https://github.com/pureswift/bluetooth", .branch("master")),
.package(url: "https://github.com/pureswift/bluetoothlinux", .branch("master")),
],
targets: [
.target(
name: "BestExecutable",
dependencies: [
.product(name: "Bluetooth", condition: .when(platforms: [.macOS])),
.product(name: "BluetoothLinux", condition: .when(platforms: [.linux])),
.target(name: "DebugHelpers", condition: .when(configuration: .debug)),
]
),
.target(name: "DebugHelpers")
]
)

Related

Is it possible to have conditional dependencies in a Package.swift target depending on the platform?

I am trying to build a Swift package, where one of the targets must depend on a different version of a library depending on whether it is being built for iOS or MacOS.
For iOS, the dependency is packaged as an .xcframework, whereas for MacOS it is packaged as a system library.
So my dependencies package looks like this:
// swift-tools-version:5.6
import PackageDescription
let package = Package(
name: "LibFoo",
products: [
.library(name: "LibFooFramework", targets: ["LibFooFramework"]),
.library(name: "ClibFoo", targets: ["ClibFoo"])
],
targets: [
.binaryTarget(
name: "LibFooFramework",
path: "path/to/LibFooFramework.xcframework"
),
.systemLibrary(
name: "ClibFoo",
pkgConfig: "libFoo"
)
]
)
So then in my client package, I want to use one of these dependencies depending on the target platform.
I have tried this:
// swift-tools-version:5.6
import PackageDescription
#if os(iOS)
let conditionalTarget: Target = .target(
name: "MyLib",
dependencies: [
.product(name: "LibFooFramework", package: "LibFooFramework")
]
)
#else
let CVulkanUtilsTarget: Target = .target(
name: "MyLib",
dependencies: [
.product(name: "ClibFoo", package: "LibFoo")
]
)
#endif
let package = Package(
name: "MyLib",
products: [
.library(name: "MyLib", targets: ["MyLib"]),
],
dependencies: [
.package(url: "https://github.com/spencerkohan/LibFoo", branch:"master")
],
targets: [
.library(
name: "LibFooFramework",
path: "path/to/LibFooFramework.xcframework"
),
]
)
But it seems that the package manager always takes the else branch, even if the package is being built for iOS. I guess this is because the os target of the Package.swift will always be the build environment and not the target.
But is there any way to achieve this type of conditional dependency?

SPM dependencies branch is deprecated on Xcode 13.3 and Swift 5.6

Xcode 13.3 shows warnings for deprecation on the dependencies for external packages.
For example:
// swift-tools-version:5.6
import PackageDescription
let package = Package(
name: "MyPackage",
platforms: [
.iOS(.v15)
],
products: [
.library(
name: "MyPackage",
targets: ["MyTarget"]),
],
dependencies: [
.package(
name: "MyDependency",
url: "https://github.com/someorg/somepackage.git",
.branch("main"))
],
targets: [
.target(
name: "MyTarget",
dependencies: ["MyDependency"])
]
)
This shows the warning:
'package(name:url:_:)' is deprecated: use specific requirement APIs
instead (e.g. use 'branch:' instead of '.branch')
However applying the recommended update makes the dependency not reachable anymore from MyPackage.
It is necessary to set the dependency as a product.
Specifically with .product(name: [PackageName], package: [Name of repository]), example:
// swift-tools-version:5.6
import PackageDescription
let package = Package(
name: "MyPackage",
platforms: [
.iOS(.v15)
],
products: [
.library(
name: "MyPackage",
targets: ["MyTarget"]),
],
dependencies: [
.package(
url: "https://github.com/someorg/somepackage.git",
branch: "main")
],
targets: [
.target(
name: "MyTarget",
dependencies: [
.product(
name: "MyDependency",
package: "somepackage")])
]
)

Import local SPM Library in local SPM package

I have a local SPM package that contains 2 libraries, and I want to import one of those libraries in another local SPM package :
File containing the libraries:
let package = Package(
name: "LocationService",
platforms: [.iOS(.v13)],
products: [
.library(
name: "LocationService",
type: .dynamic,
targets: ["LocationService"]),
.library(
name: "LocationLiveClient",
type: .dynamic,
targets: ["LocationLiveClient"]),
],
targets: [
.target(
name: "LocationService",
dependencies: []),
.target(
name: "LocationLiveClient",
dependencies: ["LocationService"],
path: "Sources/LocationLiveClient"),
]
)
File importing the libraries:
let package = Package(
name: "HomePage",
products: [
.library(
name: "HomePage",
type: .dynamic,
targets: ["HomePage"])
],
dependencies: [
.package(path: "../RouterService"),
.package(path: "../LocationService/Sources/LocationLiveClient"),
],
targets: [
.target(
name: "HomePage",
dependencies: ["RouterService", "LocationLiveClient"])
]
)
There are a couple of issues to resolve here.
(If a requirement of your design is to use 'dynamic' linking, then this approach may not work for you.)
type: .dynamic:
Unless you absolutely need to guarantee how library linking is achieved, it is recommended that you leave this as the default value of nil (just remove the line). This allows the swift package manager to determine how to best link the libraries (the default being 'static').
.package(path: "../LocationService/Sources/LocationLiveClient"),
LocationLiveClient is a product & target of the LocationService package. In the dependencies here, a reference to the package as a whole should be made. So change this to .package(path: "../LocationService"),
dependencies: ["RouterService", "LocationLiveClient"])
Once the change to depend on the whole location service package, the compiler needs a little extra information. You can update your target dependencies to specifically use the LocationLiveClient library in the LocationService package: .product(name: "LocationLiveClient", package: "LocationService").
With those changes in place you end up with a Package definition like this:
let package = Package(
name: "HomePage",
products: [
.library(
name: "HomePage",
targets: ["HomePage"]),
],
dependencies: [
.package(path: "../RouterService"),
.package(path: "../LocationService"),
],
targets: [
.target(
name: "HomePage",
dependencies: [
"RouterService",
.product(name: "LocationLiveClient", package: "LocationService")
]
),
]
)
You should then be able to import LocationLiveClient as expected.
Side note: Assuming your 'LocationService' package has the following folder structure, then you can safely remove path: "Sources/LocationLiveClient" from your LocationLiveClient target definition.
LocationService
-> Sources
-> LocationService
-> LocationLiveClient

Same version of swift package in all local packages

I'm organizing my code using local swift packages, but many of them are using the same framework (Swinject in my case). So, almost all of my packages contain the same line:
let package = Package(
name: "MyPackage",
products: [
.library(
name: "MyPackage",
targets: ["MyPackage"]),
],
dependencies: [
.package(name: "Swinject", url: "https://github.com/Swinject/Swinject.git", from: "2.7.0")
],
targets: [
.target(
name: "MyPackage",
dependencies: ["Swinject"]),
.testTarget(
name: "MyPackageTests",
dependencies: ["MyPackage"]),
]
)
If I wanted to change the version of this package, I would need to change the dependency descriptions in all local packages. Is it possible to declare this dependency once and then use it from all packages?

How do I declare the Siesta Swift package as a dependency of another Swift package?

I'm trying to use the Swift package Siesta as a dependency for the package I'm building and reference it in my package code. I've identified how to import the package into my project in my Package.swift file which is simple enough:
dependencies: [
.package(url: "https://github.com/bustoutsolutions/siesta", from: "1.5.1")
],
This causes the package to be copied into my package just fine. The problem I'm having is actually linking it up to my package so I can import it and reference it in code. I know I need to actually link it up to my target
I've read some other package files and because the package name for Siesta is like this
let package = Package(
name: "Siesta",
And the products it declares are like this
products: [
.library(name: "Siesta", targets: ["Siesta"]),
.library(name: "SiestaUI", targets: ["SiestaUI"]),
.library(name: "Siesta_Alamofire", targets: ["Siesta_Alamofire"]),
],
I should be able to just do this in my package file's target to use it
.target(
name: "MyTarget",
dependencies: [.product(name: "Siesta", package: "Siesta")]),
But when I try to build my package, I get an error:
/Users/blahblah/Desktop/MyPackage/Package.swift: unknown package 'Siesta' in dependencies of target 'MyTarget'
And not only that, all the targets for my single run scheme on my package go missing and I can't build again without discarding all my local version control changes. What's going on here?
With Swift tools version 5.2 you have to provide a name argument when declaring your package dependency.
.package(name: "Siesta", url: "https://github.com/bustoutsolutions/siesta", from: "1.5.1")
A working example of a 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: "MyPackage",
products: [
.library(
name: "MyLibrary",
targets: ["MyTarget"]),
],
dependencies: [
// make sure to provide a `name` argument here
.package(name: "Siesta", url: "https://github.com/bustoutsolutions/siesta", from: "1.5.1")
],
targets: [
.target(
name: "MyTarget",
dependencies: [
.product(name: "Siesta", package: "Siesta")
]),
]
)
Source: https://forums.swift.org/t/package-names-in-swift-5-2/34886/6

Resources