Swift 包的 REPL 支持

swift run 命令有一个新的 --repl 选项,该选项启动 Swift REPL,并支持导入包的库目标。

Swift 发行版带有一个 Swift 语言的 REPL。Swift REPL 是一个用于试验 Swift 代码的绝佳工具,无需创建临时的 Swift 包或 Xcode 项目。可以通过运行不带任何参数的 swift 命令来启动 REPL。

Swift REPL 允许您导入核心库(如 FoundationDispatch)和系统模块(如 macOS 上的 Darwin 和 Linux 上的 Glibc)。实际上,REPL 允许您导入任何 Swift 模块,只要它可以使用启动 REPL 时提供的编译器参数正确查找和加载它们。Swift Package Manager 利用此功能,并使用导入包的库目标所需的编译器参数启动 REPL。

示例

让我们使用一些示例来探索新功能

Yams

Yams 是一个用于处理 YAML 的 Swift 包。

克隆该包并使用 swift run --repl 启动 REPL

$ git clone https://github.com/jpsim/Yams
$ cd Yams
$ swift run --repl

这应该编译该包并启动 Swift REPL。让我们尝试使用 dump 方法,该方法将对象转换为 YAML

  1> import Yams

  2> let yaml = try Yams.dump(object: ["foo": [1, 2, 3, 4], "bar": 3])
yaml: String = "bar: 3\nfoo:\n- 1\n- 2\n- 3\n- 4\n"

  3> print(yaml)
bar: 3
foo:
- 1
- 2
- 3
- 4

同样,我们可以使用 load 方法将字符串转换回对象

  4> let object = try Yams.load(yaml: yaml)
object: Any? = 2 key/value pairs {
  ...
}

  5> print(object)
Optional([AnyHashable("bar"): 3, AnyHashable("foo"): [1, 2, 3, 4]])

Vapor 的 HTTP

Vapor 项目有一个基于 SwiftNIO 包构建的 HTTP 包。

克隆该包并使用 swift run --repl 启动 REPL

$ git clone https://github.com/vapor/http
$ cd http
$ swift run --repl

让我们使用 HTTPClient 类型发出 GET 请求

  1> import HTTP
  2> let worker = MultiThreadedEventLoopGroup(numberOfThreads: 1)
  3> let client = HTTPClient.connect(hostname: "httpbin.org", on: worker).wait()
  4> let httpReq = HTTPRequest(method: .GET, url: "/json")
  5> let httpRes = try client.send(httpReq).wait()

  6> print(httpRes)
HTTP/1.1 200 OK
Connection: keep-alive
Server: gunicorn/19.9.0
Date: Sun, 30 Sep 2018 21:30:41 GMT
Content-Type: application/json
Content-Length: 429
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Via: 1.1 vegur
{
  "slideshow": {
    "author": "Yours Truly",
    "date": "date of publication",
    "slides": [
      {
        "title": "Wake up to WonderWidgets!",
        "type": "all"
      },
      {
        "items": [
          "Why <em>WonderWidgets</em> are great",
          "Who <em>buys</em> WonderWidgets"
        ],
        "title": "Overview",
        "type": "all"
      }
    ],
    "title": "Sample Slide Show"
  }
}

我们可以使用 Foundation 的 JSONSerialization 来解析响应

  7> let result = try JSONSerialization.jsonObject(with: httpRes.body.data!) as! NSDictionary
result: NSDictionary = 1 key/value pair {
  [0] = {
    key = "slideshow"
    value = 4 key/value pairs {
      [0] = {
        key = "slides"
        value = 2 elements
      }
      [1] = {
        key = "author"
        value = "Yours Truly"
      }
      [2] = {
        key = "title"
        value = "Sample Slide Show"
      }
      [3] = {
        key = "date"
        value = "date of publication"
      }
    }
  }
}

实现细节

将 REPL 与 Swift 包一起使用需要两条信息才能构建 REPL 参数。第一条信息是为库目标及其依赖项提供头文件搜索路径。对于 Swift 目标,这意味着提供模块的 .swiftmodule 文件的路径;对于 C 目标,我们需要包含目标模块映射文件的目录路径。第二条信息是构建包含所有库目标的共享动态库。这将允许 REPL 在运行时加载所需的符号。SwiftPM 通过合成一个包含根包的所有库目标的特殊产品来实现这一点。此特殊产品仅在使用 --repl 选项时构建,并且不影响其他包管理器操作。

查看实现了此功能的 pull request,了解完整的实现细节!

结论

Swift 包的 REPL 支持将进一步增强 REPL 环境,并为库包作者和使用者提供更轻松的实验体验。该功能可在最新的 trunk snapshot 中试用。如果您发现错误或有增强功能请求,请提交 JIRA

有问题?

如果您有疑问并有兴趣了解更多信息,请查看 Swift 论坛中的相关讨论主题