如何使用Swift Package Manager那么,让我门开始吧

Swift Package Manager 是苹果推出的用于创建使用swift的库和可执行程序的工具。

SwiftPM有什么作用?

能够通过命令快速创建library或者可执行的swift程序,能够跨平台使用,能够使开发出来的项目能够在不同平台上运行。

SwiftPM有哪些局限?

1、目前只能用来写跨平台的项目,如swift服务端开发,现在的Vapor、Perfect等服务端的web框架均使用SwiftPM来构建和管理依赖。 2、iOS和MacOS目前还未支持,但是后续一定会支持,现在只需要耐心的等待。

注意:本文适用于Swift 4.1.0 版本,后续会有更新,如果里面的demo不能正常运行,请检查版本。

那么,让我门开始吧

  • 创建一个Package

1、创建文件夹,并进入文件夹

$ mkdir Hello
$ cd Hello

2、初始化一个名为Hello的package

$ swift package init

会生成以下的文件结构

Hello项目的目录结构

3、使用命令行编译项目

$ swift build
Compile Swift Module 'Hello' (1 sources)

使用命令行运行test

$ swift test
Compile Swift Module 'HelloTests' (2 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/HelloPackageTests.xctest/Contents/MacOS/HelloPackageTests
Test Suite 'All tests' started at 2018-08-28 11:23:16.755
Test Suite 'HelloPackageTests.xctest' started at 2018-08-28 11:23:16.755
Test Suite 'HelloTests' started at 2018-08-28 11:23:16.755
Test Case '-[HelloTests.HelloTests testExample]' started.
Test Case '-[HelloTests.HelloTests testExample]' passed (0.264 seconds).
Test Suite 'HelloTests' passed at 2018-08-28 11:23:17.019.
     Executed 1 test, with 0 failures (0 unexpected) in 0.264 (0.264) seconds
Test Suite 'HelloPackageTests.xctest' passed at 2018-08-28 11:23:17.019.
     Executed 1 test, with 0 failures (0 unexpected) in 0.264 (0.264) seconds
Test Suite 'All tests' passed at 2018-08-28 11:23:17.019.
     Executed 1 test, with 0 failures (0 unexpected) in 0.264 (0.264) seconds
  • 创建一个可执行项目

1、创建文件夹,并进入文件夹

$ mkdir HelloExcutable
$ cd HelloExcutable/

2、初始化一个名为Hello的可执行文件

$ swift package init --type executable

会生成以下的文件结构

HelloExcutable的目录结构

3、运行HelloExcutable

$ swift run HelloExcutable
Compile Swift Module 'HelloExcutable' (1 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/HelloExcutable
Hello, world!

4、Build HelloExcutable

$ swift build

会在当前目录创建一个.build的文件夹,里面是编译后的内容。

编译后的目录结构

可以看到在x86_64-apple-macosx10.10/debug文件夹里有一个HelloExcutable的可执行文件。

5、直接运行HelloExcutable

$ .build/x86_64-apple-macosx10.10/debug/HelloExcutable

6、多个xxx.swift的情况: 在HelloExcutable(和Main.swift同级)的目录下创建名为Greeter.swift的文件,在里面插入代码:

func sayHello(name: String) {
    print("Hello, \(name)!")
}

修改Main.swift:

if CommandLine.arguments.count != 2 {
    print("Usage: hello NAME")
} else {
    let name = CommandLine.arguments[1]
    sayHello(name: name)
}

7、运行来查看效果

$ swift run HelloExcutable `whoami`
Compile Swift Module 'HelloExcutable' (2 sources)
Linking /Users/leacode/Documents/Swift/HelloExcutable/.build/x86_64-apple-macosx10.10/debug/HelloExcutable
Hello, leacode!

会获得命令行的结果,并打印出来

实际项目种使用

  • 创建项目

执行命令

$ swift package init --help
OVERVIEW: Initialize a new package

OPTIONS:
  --type   empty|library|executable|system-module

可以看到目前SwiftPM支持四种类型,指定不同的type可以创建不同类型的项目。由于swift支持不同的平台,所以创建项目的时候并没有生成xcode文件,如果是在非Mac平台开发,可以使用其他IDE进行开发,如果在Mac上开发就会方便很多,可以使用以下命令创建xcodeproj文件:

$ swift package generate-xcodeproj
  • 编译项目

执行命令

$ swift build --help
OVERVIEW: Build sources into binary products

USAGE: swift build [options]

OPTIONS:
  --build-path            Specify build/cache directory [default: ./.build]
  --build-tests           Build both source and test targets
  --configuration, -c     Build with configuration (debug|release) [default: debug]
  --disable-prefetching   
  --disable-sandbox       Disable using the sandbox when executing subprocesses
  --enable-build-manifest-caching
                          Enable llbuild manifest caching [Experimental]
  --no-static-swift-stdlib
                          Do not link Swift stdlib statically
  --package-path          Change working directory before any other operation
  --product               Build the specified product
  --show-bin-path         Print the binary output path
  --static-swift-stdlib   Link Swift stdlib statically
  --target                Build the specified target
  --verbose, -v           Increase verbosity of informational output
  -Xcc                    Pass flag through to all C compiler invocations
  -Xcxx                   Pass flag through to all C++ compiler invocations
  -Xlinker                Pass flag through to all linker invocations
  -Xswiftc                Pass flag through to all Swift compiler invocations
  --help                  Display available options

可以看到build项目除了直接用build命令之外还可以加上一些额外的选项。这里做一下讲解

USAGE: swift build [options]

OPTIONS:
  --build-path            指定编译文件存放的路径(默认路径是./.build)
  --build-tests           编译源码和测试代码
  --configuration, -c     编译环境(debug|release),默认是debug
  --disable-prefetching   禁止prefetching
  --disable-sandbox       禁用沙盒
  --enable-build-manifest-caching 
                          打开llbuild清单缓存(实验功能,这个就是增量编译了)
  --no-static-swift-stdlib 不要静态link Swift stdlib
  --package-path          当自己指定源代码路径的时候使用此命令
  --product               编译指定的product
  --show-bin-path         打印二进制文件输出路径
  --static-swift-stdlib   静态link Swift stdlib
  --target                编译特定的target
  --verbose, -v           Increase verbosity of informational output
  -Xcc                    将标志传递给所有C编译器调用
  -Xcxx                   将标志传递给所有C++编译器调用
  -Xlinker                将标志传递给所有linker调用
  -Xswiftc                将标志传递给所有Swift编译器调用
  --help                  查看帮助

如果你只是一个swift开发人员,指定路径、指定环境是工作中会最常用到的命令。

  • 运行项目

执行命令

$ swift run --help
OVERVIEW: Build and run an executable product

USAGE: swift run [options] [executable [arguments ...]]

OPTIONS:
  --build-path            Specify build/cache directory [default: ./.build]
  --configuration, -c     Build with configuration (debug|release) [default: debug]
  --disable-prefetching   
  --disable-sandbox       Disable using the sandbox when executing subprocesses
  --enable-build-manifest-caching
                          Enable llbuild manifest caching [Experimental]
  --no-static-swift-stdlib
                          Do not link Swift stdlib statically
  --package-path          Change working directory before any other operation
  --skip-build            Skip building the executable product
  --static-swift-stdlib   Link Swift stdlib statically
  --verbose, -v           Increase verbosity of informational output
  -Xcc                    Pass flag through to all C compiler invocations
  -Xcxx                   Pass flag through to all C++ compiler invocations
  -Xlinker                Pass flag through to all linker invocations
  -Xswiftc                Pass flag through to all Swift compiler invocations
  --help                  Display available options

POSITIONAL ARGUMENTS:
  executable              The executable to run

和上面的build一样,在运行项目的时候,可以配置不同的环境参数来运行不同的target或环境

  • 添加依赖

通过SwiftPM创建项目的时候会在项目的根目录生成一个Package.swift的文件,这个文件就相当于cocoapods的Podfile 或者Carthage 的Cartfile.

首先来看看 HelloExcutable 这个项目的Package.swift文件:

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

import PackageDescription

let package = Package(
    name: "HelloExcutable",
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages which this package depends on.
        .target(
            name: "HelloExcutable",
            dependencies: []),
    ]
)

目前这个项目没有添加任何依赖,添加依赖的步骤如下:

1、引入一个依赖库SwiftNIO

import PackageDescription

let package = Package(
    name: "HelloExcutable",
    dependencies: [
        .package(url: "https://github.com/apple/swift-nio.git", from: "1.9.2")
    ],
    targets: [
        .target(
            name: "HelloExcutable",
            dependencies: ["NIO", "NIOHTTP1", "NIOFoundationCompat"]),
    ]
)

2、执行命令

swift build

这时候会下载依赖的package,下载完后会编译项目。

3、重新生成 xcodeproj文件(仅Mac适用)

swift package generate-xcodeproj

打开生成的 HelloExcutable.xcodeproj 文件就可以看到刚刚添加的依赖已经集成到项目里了:

添加dependencies之后的项目

Package.swift怎么写?

Package.swift是用于管理项目依赖以及项目结构的文件,文件内容就是一个 Package 类的实例。

Package

这里是Package这个Class的init方法:

final public class Package {

...

public init(name: String, //项目的名称
                pkgConfig: String? = default,
                providers: [PackageDescription.SystemPackageProvider]? = default,
                 products: [PackageDescription.Product] = default, // 对外公开的产物
             dependencies: [PackageDescription.Package.Dependency] = default, // 依赖
                  targets: [PackageDescription.Target] = default, // 项目的targets
    swiftLanguageVersions: [Int]? = default, // swift版本
        cLanguageStandard: PackageDescription.CLanguageStandard? = default, // c语言标准
      cxxLanguageStandard: PackageDescription.CXXLanguageStandard? = default) // c++语言标准
      
}

可以根据项目的需要,设置对应的参数。 比如要用SwiftPM做一个framework,那么需要设置products指定Framework的名字和target。

Package.Dependency

当项目中需要添加依赖的时候,需要设置dependencies参数, 是一个Package.Dependency类的集合,下面是Package.Dependency的部分源码:

extension Package.Dependency : Equatable {

    public static func package(url: String, from version: PackageDescription.Version) -> PackageDescription.Package.Dependency

    public static func package(url: String, _ requirement: PackageDescription.Package.Dependency.Requirement) -> PackageDescription.Package.Dependency

    public static func package(url: String, _ range: Range<PackageDescription.Version>) -> PackageDescription.Package.Dependency

    public static func package(url: String, _ range: ClosedRange<PackageDescription.Version>) -> PackageDescription.Package.Dependency
    
    ...
    
}

类似cocoapods 和 Carthage, Package.Dependency有两个参数,第一个参数设置依赖库的url,第二个参数设置依赖库的版本

设置版本的语法:

 .package(url:"", from: "1.0.0")                               (1.0.0 ..< 2.0.0)
 .package(url:"", from: "1.2.0")                               (1.2.0 ..< 2.0.0)
 .package(url:"", from: "1.5.8")                               (1.5.8 ..< 2.0.0)
 .package(url:"", .exactItem(Version(stringLiteral: "1.2.0"))  (==1.2.0)
 .package(url:"", .exactItem(Version(stringLiteral: "1.2.0"))  (==1.2.0)
 .package(url:"", .revisionItem("74663ec"))                    某次提交的revision的值
 .package(url:"", .branchItem("develop"))                      分支名
 .package(url:"", .localPackageItem)                           本地依赖
 .package(url:"", Version(stringLiteral: "1.2.3")...Version(stringLiteral: "1.2.8"))   (>=1.2.3 && <=1.2.8)
 .package(url:"", Version(stringLiteral: "1.2.3")..<Version(stringLiteral: "1.2.8"))   (>=1.2.3 && <1.2.8)

同样上面的Version也可以用以下方式来写:

Version(1, 2, 0) 相当于于 Version(stringLiteral: "1.2.0") Version(1, 0, 0)..<Version(1, .max, .max) 意思是版本大于1.0.0 小于2.0.0

Target

另外一个比较重要的类是Target:

final public class Package {

...

public static func target(name: String, // target的名称
                  dependencies: [PackageDescription.Target.Dependency] = default, // target 的依赖,这里面主要指定Package添加的依赖的module的名字
                          path: String? = default, // target的路径,如果自定义文件夹需要设置此参数
                       exclude: [String] = default,  // target path中不希望被包含的path
                       sources: [String]? = default,  // 资源文件的路径
             publicHeadersPath: String? = default // 公共header文件的路径
             ) -> PackageDescription.Target

...

}

用于定义项目里的target。

本篇主要介绍了SwiftPM的一些基础用法,使用时需要注意以下几点:

1、dependencies里面的链接和版本一定要写对,target里的dependencies对应的总的dependencies中的module,一个依赖可以有多个module。

2、注意多个库依赖时的兼容性,如果出现卡着不动的时候,常常是依赖的版本有问题,可以逐步添加来排查问题。

3、Package.swift中的语法不要写错,注意 [ ]和 ""要成对存在,不要漏了前后的符号。

4、如果你不是在Mac上开发,可以使用Atom等支持高亮的编辑器来编辑Package.swift文件。

5、不要去尝试用它来管理iOS项目的依赖,现在还不支持,到支持的时候我会更新此文章。

希望本文能给你带来一些帮助,有疑问或者需要补充的地方欢迎留言。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

[WCF-Discovery]如何利用”发现代理”实现可用服务的实时维护?

上面的内容大部分是围绕着Ad-Hoc模式展开介绍的。Managed模式和Ad-Hoc不同之处在于可用服务的终结点通过发现代理来统一管理。客户端在进行可用目标服务...

2578
来自专栏hrscy

iOS 多线程--NSThread

比如售票问题,同一张票在某个时间只能卖给一个人,不能把一张票卖给多个人,这样就会出问题。

922
来自专栏大内老A

.NET Core的文件系统[1]:读取并监控文件的变化

ASP.NET Core 具有很多针对文件读取的应用。比如我们倾向于采用JSON文件来定义配置,所以应用就会涉及针对配置文件读取。如果用户发送一个针对物理文件的...

6935
来自专栏程序员与猫

ASP.NET Core File Providers

原文地址:FileProvider By Steve Smith ASP.NET Core通过对File Providers的使用实现了对文件系统访问的抽象。 ...

2417
来自专栏逆向技术

Win3内存管理之私有内存跟共享内存的申请与释放

  通过上一篇文章.我们理解了虚拟内存与物理内存的区别. 那么我们有API事专门申请虚拟内存与物理内存的.

1192
来自专栏葡萄城控件技术团队

Asp.Net MVC4入门指南(4):添加一个模型

在本节中,您将添加一些类,这些类用于管理数据库中的电影。这些类是ASP.NET MVC 应用程序中的"模型(Model)"。 您将使用.NET Framewor...

19810
来自专栏草根专栏

用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识 + 项目准备

REST 是 Representational State Transfer 的缩写. 它是一种架构的风格, 这种风格基于一套预定义的规则, 这些规则描述了网络...

1.1K6
来自专栏数据库

在.NET Core类库中使用EF Core迁移数据库到SQL Server

前言 如果大家刚使用EntityFramework Core作为ORM框架的话,想必都会遇到数据库迁移的一些问题。 起初我是在ASP.NET Core的Web项...

2356
来自专栏开发与安全

linux系统编程之信号(三):信号的阻塞与未决

一、信号在内核中的表示 实际执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(B...

2230
来自专栏大内老A

谈谈你最熟悉的System.DateTime[下篇]

在《上篇》中,我们实现了将保存有消息条目的XML向CodeDOM的转换,即是将XML文件生成一个CodeCompileUnit对象,而该CodeCompileU...

1805

扫码关注云+社区

领取腾讯云代金券