This text is a whole Swift Package deal Supervisor cheatsheet for the bundle manifest file, utilizing the most recent Swift 5.2 instruments model.
Swift
If you wish to be taught how to use the Swift Package Manager it’s best to learn my different article, as a result of that’s extra like an introduction for many who have by no means labored with SPM but.
Package deal varieties
There are a number of bundle varieties that you may create with the swift bundle init
command. You may specify the --type
flag with the next values: empty, library, executable, system-module, manifest. You may also outline a customized bundle identify by means of the --name
flag.
- The empty bundle will create the default file construction with out the pattern code information.
- The library kind will create a reusable library product template.
- The executable kind will create a Swift software with an executable product definition within the bundle and a
foremost.swift
file as a place to begin. - The system-module kind will create a wrapper round a system offered bundle, comparable to libxml, we’ll discuss this afterward.
- The manifest kind will solely create a
Package deal.swift
file with out anything.
The Package deal manifest file
Each single SPM venture has this particular file inside it known as Package deal.swift
. I already wrote a put up about how the package manager and the Swift toolchain works behind the scenes, this time we’ll focus solely on the manifest file itself. Let’s get began. 📦
Each single Package deal.swift file begins with a particular remark line the place you need to outline the model of the used Swift instruments. The most recent model is sort of completely different from the older ones.
Subsequent you need to import the PackageDescription framework with a purpose to outline your Swift bundle. This framework incorporates the bundle manifest construction as Swift objects.
import PackageDescription
That is it now you're prepared to explain the bundle itself. Oh by the best way you'll be able to change the model of the used instruments, you'll be able to learn extra about this within the Package deal Supervisor usage readme.
Package deal
A bundle is only a bunch of Swift (or different) information. The manifest file is the outline of what and how you can construct from these sources. Each single bundle ought to have a reputation, however this isn't enought to truly generate one thing from it. You may solely have precisely one bundle definition contained in the file. That is the shortest and most ineffective one that you may create. 🙈
let bundle = Package deal(identify: "myPackage")
The bundle identify goes for use when you're importing packages as dependencies, so identify your pacages fastidiously. For those who select a reserved identify by a system framework there could be points with linking. If there is a battle you need to use static linking as a substitute of dynamic. For those who generate a venture through the swift bundle generate-xcodeproj
command that venture will attempt to hyperlink every little thing dynamically, however when you open the Package deal.swift
file utilizing Xcode 11, the dependencies will likely be linked statically if this was not set explicitly within the product definition part.
Platform
A platform is mainly an working system with a given model that you may assist.
let bundle = Package deal(
identify: "myPackage",
platforms: [
.iOS(.v13),
.macOS(.v10_15),
.tvOS(.v13),
.watchOS(.v6),
]
)
If you add a platform you're placing a constraint on it through the required model. Each single dependency ought to match the requirement of the primary bundle platforms. Lengthy story quick if it's essential add assist for Apple platforms, it's best to specify a platform flag with a supported model, in any other case SPM will use the oldest deployment goal based mostly on the put in SDK, apart from macOS, that is going to be v10_10. Each bundle has Linux assist by default, you'll be able to't add such restrictions but, however perhaps it will change within the close to future, additionally Home windows is coming.
Product
A bundle can have a number of ultimate merchandise (construct artifacts). Presently there are two varieties of construct merchandise: executables and libraries. The executable is a binary that may be executed, for instance this is usually a command line software. A library is one thing that others can use, it's mainly the general public API product illustration in your targets.
import PackageDescription
let bundle = Package deal(identify: "myPackage", merchandise: [
.library(name: "myPackageLib", targets: ["myPackageLib"]),
.library(identify: "myPackageStaticLib", kind: .static, targets: ["myPackageLib"]),
.library(identify: "myPackageDynLib", kind: .dynamic, targets: ["myPackageLib"]),
.executable(identify: "myPackageCli", targets: ["myPackage"])
], targets: [
.target(name: "myPackageLib"),
.target(name: "myPackageCli"),
])
If the library kind is unspecified, the Package deal Supervisor will mechanically select it based mostly on the shopper's desire. As I discussed this earlier generated Xcode tasks favor dynamic linking, however when you merely open the manifest file the app will likely be statically linked.
Dependency
Packages can depend on different packages. You may outline your dependencies by specifying an area path or a repository url with a given model tag. Including a dependency into this part is just not sufficient to make use of it in your targets. You even have so as to add the product offered by the bundle on the goal stage.
let bundle = Package deal(
identify: "myPackage",
dependencies: [
.package(path: "/local/path/to/myOtherPackage"),
.package(url: "<git-repository-url>", from: "1.0.0"),
.package(url: "<git-repository-url>", .branch("dev")),
.package(url: "<git-repository-url>", .exact("1.3.2")),
.package(url: "<git-repository-url>", .revision("<hash>")),
.package(url: "<git-repository-url>", .upToNextMajor(from: "1.0.0")),
.package(url: "<git-repository-url>", .upToNextMinor(from: "1.0.0")),
.package(url: "<git-repository-url>", "1.0.0"..<"1.3.0"),
]
)
The url is usually a GitHub url, luckily you'll be able to add non-public repositories as nicely through the use of an ssh key based mostly authentication. Simply use the [email protected]:BinaryBirds/viper-kit.git
url format, as a substitute of the HTTP based mostly, if you wish to add non-public packages. 🤫
Goal
A goal is one thing that you may construct, in different phrases it is a construct goal that may end up in a library or an executable. You need to have a minimum of one goal in your venture file in any other case you'll be able to't construct something. A goal ought to all the time have a reputation, each different settings is elective.
Settings
There are various settings that you should use to configure your target. Targets can rely upon different targets or merchandise outlined in exterior packages. A goal can have a customized location, you'll be able to specify this by setting the trail attribute. Additionally you'll be able to exclude supply information from the goal or explicitly outline the sources you need to use. Targets can have their very own public headers path and you may present construct settings each for the C, C++ and the Swift language, and compiler flags.
.goal(identify: "myPackage",
dependencies: [
.target(name: "other"),
.product(name: "package", package: "package-kit")
],
path: "./Sources/myPackage",
exclude: ["foo.swift"],
sources: ["main.swift"],
publicHeadersPath: "./Sources/myPackage/headers",
cSettings: [
.define("DEBUG"),
.define("DEBUG", .when(platforms: [.iOS, .macOS, .tvOS, .watchOS], configuration: .debug)),
.outline("DEBUG", to: "yes-please", .when(platforms: [.iOS], configuration: .debug)),
.headerSearchPath(""),
.headerSearchPath("", .when(platforms: [.android, .linux, .windows], configuration: .launch)),
.unsafeFlags(["-D EXAMPLE"]),
.unsafeFlags(["-D EXAMPLE"], .when(platforms: [.iOS], configuration: .debug)),
],
cxxSettings: [
],
swiftSettings: [
.define("DEBUG"),
.define("DEBUG", .when(platforms: [.iOS, .macOS, .tvOS, .watchOS], configuration: .debug)),
.unsafeFlags(["-D EXAMPLE"]),
.unsafeFlags(["-D EXAMPLE"], .when(platforms: [.iOS], configuration: .debug)),
],
linkerSettings: [
.linkedFramework("framework"),
.linkedLibrary("framework", .when(platforms: [.iOS], configuration: .debug)),
.linkedLibrary("library"),
.linkedLibrary("library", .when(platforms: [.macOS], configuration: .launch)),
.unsafeFlags(["-L example"]),
.unsafeFlags(["-L example"], .when(platforms: [.linux], configuration: .launch)),
]),
As you'll be able to see you'll be able to outline preprocessor macros for each single language. You should use the secure circumstances for primary stuff, however there's an unsafeFlags case for the reckless ones. The good factor is that you may assist a platform situation filter together with construct configuration to each single settings because the final param.
Accessible platforms are: .iOS
, .macOS
, .watchOS
, .tvOS
, .android
, .linux
, .home windows
The construct configuration could be .debug
or .launch
Check targets
Check targets are used to outline take a look at suites. They can be utilized to unit test different targets utilizing the XCTest framework. They appear to be precisely the identical as common targets.
.testTarget(identify: String,
dependencies: [Target.Dependency],
path: String?,
exclude: [String],
sources: [String]?,
cSettings: [CSetting]?,
cxxSettings: [CXXSetting]?,
swiftSettings: [SwiftSetting]?,
linkerSettings: [LinkerSetting]?)
I believe the one distinction between a goal and a take a look at goal is that you may run a take a look at goal utilizing the swift take a look at
command, however from a structural standpoint, they're mainly the identical.
Package deal configs and system libraries
You may wrap an present system library utilizing Swift, the great thing about that is that you should use packages written in C, CPP or different languages. I am going to present you a fast instance by means of the wonderful Kanna(鉋) - XML/HTML parser repository. I am utilizing this software quite a bit, thanks for making it Atsushi Kiwaki. 🙏
#if swift(>=5.2) && !os(Linux)
let pkgConfig: String? = nil
#else
let pkgConfig = "libxml-2.0"
#endif
#if swift(>=5.2)
let suppliers: [SystemPackageProvider] = [
.apt(["libxml2-dev"])
]
#else
let suppliers: [SystemPackageProvider] = [
.apt(["libxml2-dev"]),
.brew(["libxml2"])
]
#endif
let bundle = Package deal(identify: "Kanna",
pkgConfig: "",
suppliers: [
.apt(["libsqlite-dev"]),
.brew(["sqlite3"])
],
merchandise: [
.library(name: "Kanna", targets: ["Kanna"])
],
targets: [
.target(name: "myPackage"),
.systemLibrary(name: "libxml2",
path: "Modules",
pkgConfig: pkgConfig,
providers: providers)
])
There's a module definition file on the Modules listing. You will want a module.modulemap
file to export a given library, you'll be able to learn extra about Modules on the LLVM web site.
module libxml2 [system]
hyperlink "xml2"
umbrella header "libxml2-kanna.h"
export *
module * export *
You may outline your personal umbrella header and thell the system what to import.
I barely use system libraries, however this can be a good reference level. Anyhow, if it's essential wrap a system library I assume that you will have the required data to make it occur. 😅
Language settings
You may also specify the listing of Swift verisons that the bundle is appropriate with. In case you are making a bundle that incorporates C or C++ code you'll be able to inform the compiler to make use of a selected language normal through the construct course of.
swiftLanguageVersions: [.v4, .v4_2, .v5, .version("5.1")],
cLanguageStandard: .c11,
cxxLanguageStandard: .gnucxx11)
You may see all of the at the moment out there choices within the feedback. I do not know what number of of you employ these directives, however personally I by no means needed to work with them. I am not writing an excessive amount of code from the C language household these days, nevertheless it's nonetheless good that SPM has this selection built-in. 👍
Abstract
The Swift Package deal Supervisor is just not the proper software simply but, nevertheless it's on an excellent observe to turn into the de facto normal by slowly changing CocoaPods and Carthage. There are nonetheless some lacking options which might be necessities for a lot of the builders. Don't fret, SPM will enhance quite a bit within the close to future. For instance the binary dependency and useful resource assist is coming alongside Swift 5.3. You may observe the package evolution process on the official Swift Evolution dashboard.
You may learn extra in regards to the Package Manager on the official Swift web site, nevertheless it's fairly obsolate. The documentation on Apple's web site can also be very previous, however nonetheless helpful. There's a good readme file on GitHub in regards to the usage of the Swift Package Manager, however nothing is up to date incessantly. 😢