Intruction
SPM is similar Gradle or maven in the java ecosystem,designed for efficiently organizing and sharing codebases.You can use SPM to integrate third library or to modularize your own project.
Popular swift packages:
-
SwiftyJSON :github.com/SwiftyJSON/…
-
Alamofire : github.com/Alamofire/A…
The anatomy of a swift package
To understand its structured,we first need how to create a standard swift package project.
How to create a swift package
creating via CLI
Once your development environment is configured, the swift CLI is available to help you scaffold,build,and run test for your own projects.
The command to create an executable template project is as fllows:
swift package init --type executable
Build and run:
swift run
The command to create an library template is as fllows(defaule):
#The Follow command functionality equivalty to swift package init
swift package init --type library
Build and test:
# test command triggers a build automatically, so you can skip 'swift build'
swift build && swift test
Creating via Xcode
Navigation File->New->Project within Xcode to create a new Library.
If the Library is intented for specific project ,select your target project in Dropdown;otherwise,you can leave it. Note: Avoid having the library and target project open in separate Xcode windows at the same time. This can lead to selection conflicts or display glitches.
The Manifest File
The intial project structure is shown below:
Package.swiftManifest file : The central configuration file that define the products of the package. A product refers to the output of the package,such as Library binary or an executable.Each product configuration(include its name and dependents)is declared here,and single package can host multiple products. Source directorySources/spm_1,Sources/calcSource Directories : source/spm_1 and Source/calc are source code directories specified in the manifest . if a path is not explicitly assigned , the system defaults to theSources/[TargetName]conventionTests/spm_1Tests,Tests/calcTestsTest Directories:Tests/spm_1TestsandTests/calcTestsare test suit directories specified in the manifest. if a path is not explicitly assigned, the system defaults to theTests/[TargetName]convetion.
Let's take a look at the Package.swift manifest:
The following manifest defines two products:an executable named spm_1 and a library name calc
// swift-tools-version: 6.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "spm_1",
products: [
.executable(name: "spm_1", targets: ["spm_1"]),
.library(name: "calc", targets: ["calc"])
],
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.10.0"))
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "spm_1",
dependencies: [.product(name: "Alamofire", package: "Alamofire")],
path: "Sources/spm_1"
),
.testTarget(
name: "spm_1Tests",
dependencies: ["spm_1"],
path: "Tests/spm_1Tests"
),
.target(
name: "calc",
dependencies: [],
path: "Sources/calc"
),
.testTarget(
name: "calcTests",
dependencies: ["calc"],
path: "Tests/calcTests"
),
]
)
// swift-tools-version: 6.2
It specifies the minimum required swift version. If the installed Swift toolchain is older than 6.2,the compilation will fail.
Checking our local swift version:
To verify the installed Swift version on your machine,run the following command in the terminalswift -version
Let's update the swift-tools-version in Package.swift to 6.5 and run the build command:
swift build
//...
Package(
name: "spm_1"
)
//...
This define the name of the package. Note that the package name is allowed to be identiacal to any of the product names defined within it.
products: [
.executable(name: "spm_1", targets: ["spm_1"]),
.library(name: "calc", targets: ["calc"])
],
This defines all products within the package. In this project, two products are specified: an executable and a libray. For the library product,name: "calc" represents the product's name, while targets:["calc"] links it to a specefic target definition. target are the build blocks that manage source files and their respective dependencies.
dependencies: [.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.10.0")) ]
This section declare the external dependencies required by the package. Note that this only makes the dependency available to the package; It is not yet in use. To actually leverage a dependency,it must be explicitly referenced within the specific targes that require it.
...
targets: [
.executableTarget(
name: "spm_1",
dependencies: [.product(name: "Alamofire", package: "Alamofire")],
path: "Sources/spm_1"
),
.testTarget(
name: "spm_1Tests",
dependencies: ["spm_1"],
path: "Tests/spm_1Tests"
),
...
]
executableTarget define a target of binary type, with its source code locald at path:Sources/spm_1. this target requires an external dependency specified as .product(name: "Alamofire", package: "Alamofire").
Here ,name: "Alamofire" refers to a specific product whith the package we previously declared in the dependencies section. The package parameter corrsponds to the dependency's identity, similar to how we defined our own Package(name: "spm_1", ...) earlier.
Let's examine the Alamofire Manifest to get a clearer picture of these definitions.
If other projects wish to integrate our calc libray, the can refer to the following configuration:
// swift-tools-version: 6.2
import PackageDescription
let package = Package(
name: "MyNewLibrary",
products: [
.library(
name: "MyNewLibrary",
targets: ["MyNewLibrary"]
),
],
dependencies: [.package(path: "/Users/jack/Desktop/iosLear/spm_1")],
targets: [
.target(
name: "MyNewLibrary",
dependencies: [.product(name: "calc", package: "spm_1")]
),
]
)
Platform Compatibility
Suppose your project utilize API that are only available on specific OS versions.In such case,you can declare the support platforms and their minimum version in the manifest file. For instance,the Logger API is only available on macOS11 or later. If you don't explicity declare a minium version in your project,attempting to compile will result in the following error:
To clarify the minimum supported version for your library, you should declare the supported platforms and their respective versions in the manifest file. this is considered a best practice in swift development.
let package = Package(
name: "MyNewLibrary",
platforms: [
.macOS(.v13) // 限制最低支持 macOS 13 (Ventura)
.iOS(.v13)
]),
Package.resolved
Sometimes,a project declare a dependency with a flexible version range-for instance,accepting any version within amajor release,such as 5.10.1 up to 5.99.0. However,this flexibility can lead to inconsistencies across different development enviroments.> To address this,when Swift first performs dependency resolution(which can be manually triggered via swift package resolve),it generates a 'Package.resolved' file. This file record the exact version of each dependency,ensuring that all developers are working with the same codebase.
For example:
let package = Package(
//...omitted
dependencies: [
//upToNextMajor indicates that any version from 5.10.0 up to (but excluding) 6.0.0 is acceptable
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.10.0"))
]
after execution,a Package.resolved file will be generate as follows:
执行后我们会生成的如下
{
"originHash" : "bb9545b2cfa4ba84d0fc626cc6ab4c63e425a448966bf59ae5e32e9e73297b50",
"pins" : [
{
"identity" : "alamofire",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Alamofire/Alamofire.git",
"state" : {
"revision" : "7be73f6c2b5cd90e40798b06ebd5da8f9f79cf88",
"version" : "5.11.0"
}
}
],
"version" : 3
}
Key commands for managing dependencies:
-
swift package resolve(Resolve & Download):Use this when you have just cloned project or modified dependnecies and want to download them without performing a full build. -
swift package update(Update & Download):Use this to update your third-part libraries to the latest available version within the permitted range defined in your manifest.