WebURL

一个用于 Swift 的全新 URL 类型。

🌐  符合标准

WebURL 完全支持最新的 URL 标准,该标准规定了 Safari 和 Chrome 等现代浏览器如何解释 URL。它包括对 Unicode 域名 (IDNA) 的支持。

Foundation 的 URLURLComponents 各自符合不同的标准(大约有 20 年的历史),并且它们都不匹配浏览器和其他现代软件(如 NodeJS)处理 URL 的方式。WebURL 可以。

🍭  使用愉快

我们充分利用 Swift,提供丰富、富有表现力的 API,鼓励现代最佳实践和使用 URL 的方式。读取或修改 URL 的路径或查询等常见任务变得更容易、更高效,并帮助您避免细微的错误。

即使询问两个 URL 是否 ==,在使用 Foundation 的 URL 时也充满了令人惊讶的边缘情况。WebURL 大大简化了语义,这有助于使您的应用程序更加健壮,并符合您可能对 URL 的理解。

🔗  🧳  可移植、可互操作

核心 WebURL 库没有外部依赖或平台特定的行为。一切在任何地方都以相同的方式工作,并且 一切都完全向后兼容

得益于此包附带的集成库,WebURL 仍然可以与 Foundationswift-system 无缝协作。我们还移植了 async-http-client 以使用 WebURL,这表明基于 swift-nio 的项目采用它有多么容易。

⚡️  快速

尽管提供了非常高级的 API,WebURL 提供了出色的性能和低内存使用率。我们花费了额外的精力来优化常见操作,例如将 URL 转换为字符串(例如来自 JSON)或进行高效的就地修改。

该 API 使用直写视图的概念,以在单独的范围内公开其较低级别的通用实现,而不会污染 API 的其余部分。这允许大容量工作流程实现最小的开销(例如,解析 URL 列表或扫描数据的应用程序可以直接从文件的字节中进行)。

(并且它是用 100% Swift 编写的).


📚 查看文档以了解更多信息 📚


在您的项目中使用 WebURL

要在 SwiftPM 项目中使用此包,您需要将其设置为包依赖项

// Add the package as a dependency.
dependencies: [
  .package(url: "https://github.com/karwa/swift-url", .upToNextMinor(from: "0.4.0"))
]

// Then add the WebURL library as a target dependency.
targets: [
  .target(
    name: "<Your target>",
    // 👇 Add this line 👇
    dependencies: [ .product(name: "WebURL", package: "swift-url") ]
  )
]

这样,您就可以开始使用 WebURL

import WebURL

var url = WebURL("https://github.com/karwa/swift-url")!
url.scheme   // "https"
url.hostname // "github.com"
url.path     // "/karwa/swift-url"

url.pathComponents.removeLast(2)
// "https://github.com/"

url.pathComponents += ["apple", "swift"]
// "https://github.com/apple/swift"

📚 查看文档以了解有关 WebURL API 的信息 📚

🔗 与 Foundation 集成

WebURLFoundationExtras 兼容性库允许您在 WebURL 和 Foundation URL 值之间进行转换,包括使用 WebURL 发出 URLSession 请求。

要使 WebURLFoundationExtras 可用,请将其添加到您的目标依赖项中并从您的代码中导入它。

targets: [
  .target(
    name: "<Your target>",
    dependencies: [
      .product(name: "WebURL", package: "swift-url"),
      // 👇 Add this line 👇
      .product(name: "WebURLFoundationExtras", package: "swift-url")
    ]
  )
]

这样,您就可以充分利用 WebURL,同时保持与现有客户端的兼容性。同样,由于 WebURL 是一个完全可移植的包解决方案,您甚至可以利用 Unicode 域名 (IDNA) 等功能,并且 一切都毫不费力地向后兼容

import Foundation
import WebURL
import WebURLFoundationExtras

// Make URLSession requests using WebURL, including IDNA support.
let task = 
  URLSession.shared.dataTask(with: WebURL("https://😀.example.com/")!) {
    data, response, error in
    // Works!
  }

// Also supports Swift concurrency.
let (data, _) = 
  try await URLSession.shared.data(from: WebURL("https://数据.example.com/")!)

// URL <-> WebURL conversion allows incremental adoption.
public func connect(to url: Foundation.URL) throws {
  guard let webURL = WebURL(url) else {
    throw InvalidURLError()
  }
  // Internal code uses WebURL...
}

有关为什么即使对于使用 Foundation 的应用程序和库来说,WebURL 也是一个不错的选择,以及关于如何安全地使用多个 URL 标准的讨论,请阅读:将 WebURL 与 Foundation 一起使用

🔗 与 swift-system 集成

WebURLSystemExtras 库允许您在 file: URL 和 FilePath 之间进行转换。这实际上是一个非常复杂的操作,因为它从未被标准化,并且有很多遗留问题需要理解,但是我们花了一些时间并创建了一个很棒的实现。最棘手的平台始终是 Windows,因此我们的实现基于 Chromium 而不是 Foundation,并受到了 rust-url 和我们自己的研究的启发,并构建了一个全面的测试数据库,以确保我们处理所有已知的边缘情况。

这意味着 WebURL 对 POSIX (Apple/Linux/etc) 和 Windows 路径都有出色的支持,包括旧版、预 Unicode 文件 URL。很多文档的名称都使用预 Unicode 文本编码(例如,日本的 SHIFT-JIS 或韩国的 EUC-KR),它们仍然需要其 URL 才能工作。这很棘手(Unicode 绝对更好),但我们应该像 Chrome 一样支持它们。我们正在努力成为在 Swift 支持的所有平台上使用 file: URL 的最佳方式。

要使 WebURLSystemExtras 可用,请将其添加到您的目标依赖项中并从您的代码中导入它。

.target(
  name: "<Your target>",
  dependencies: [
    .product(name: "WebURL", package: "swift-url"),
    // 👇 Add this line 👇
    .product(name: "WebURLSystemExtras", package: "swift-url")
  ]
)

就是这样 - 您可以开始了!它做了很多,但是超级容易使用!

import System
import WebURL
import WebURLSystemExtras

func openFile(at url: WebURL) throws -> FileDescriptor {
  // WebURL -> FilePath
  let path = try FilePath(url: url)
  return try FileDescriptor.open(path, .readOnly)
}

🧪 async-http-client 移植

我们对 async-http-client 的移植使用 WebURL 来处理所有的内部 URL。它是最好的 Swift 库,可确保您的 HTTP 请求使用 Web 兼容的 URL 处理,并且很好地演示了如何在基于 swift-nio 的库中采用 WebURL。它保留了所有功能,甚至(主要)与现有的 Foundation.URL API 保持兼容性。

该端口还具有一种实验模式,允许在没有任何 Foundation 依赖项的情况下构建它。这依赖于一个非公开的标准库 API 来替换我们从 Foundation 使用的一个调用,因此目前它是一个选择加入功能,但是一切正常,并且它具有一些显着的性能优势。许多开发人员一直在尝试寻找超越 Foundation 的下一代纯 Swift 库,但直到现在,URL 一直是一个障碍;它们并非微不足道,并且由于它们作为货币类型被大量传递,因此任何替代品都需要满足或超过 URLURLComponents 和相关 API 的功能和人体工程学,例如百分比编码。WebURL 做到了一切。

注意:我们将定期更新此端口,因此如果您希望在应用程序中使用它,我们建议您创建一个分支并在需要时引入更改。

import AsyncHTTPClient
import WebURL

let client = HTTPClient(eventLoopGroupProvider: .createNew)

// The async API uses WebURL behind the scenes (in our port).
do {
  let request = HTTPClientRequest(url: "https://github.com/karwa/swift-url/raw/main/README.md")
  let response = try await client.execute(request, timeout: .seconds(30)).body.collect()
  print(String(decoding: response.readableBytesView, as: UTF8.self))
  // "# WebURL A new URL type for Swift..."
}

// Also supports the traditional NIO EventLoopFuture API.
do {
  let url = WebURL("https://github.com/karwa/swift-url/raw/main/README.md")!

  let response = try client.execute(request: try HTTPClient.Request(url: url))
    .map { response in
      response.body.map { String(decoding: $0.readableBytesView, as: UTF8.self) }
    }
    .wait()
  print(response)
  // "# WebURL A new URL type for Swift..."
}

🗺 项目状态和路线图

标准合规性

WebURL 完全实现了最新版本的 URL 标准。

我们使用主要浏览器和其他库使用的共享的 web-platform-tests 来验证一致性。所有构造函数、setter 和 IDNA 测试都通过了(代码),并且我们的 IDNA 实现通过这些和 Unicode 的 UTS46 一致性测试套件进行了验证(代码)。

如果您发现 WebURL 未产生正确结果的任何情况,请打开一个 GitHub 问题。

API 稳定性和路线图

虽然该包仍处于 1.0 之前的版本,但 API 稳定性保证有限。

重要:

0.x.x 版本将次要版本号视为主要版本号。

这意味着 0.3.x 和 0.3.y 之间不会有源代码破坏性更改,但如果需要,我们希望能够在 0.3.x 到 0.4.0 之间进行源代码破坏性更改。

为了稳定性,请使用 .upToNextMinor(from: ...) 版本约束。
对于最新版本,请使用 .upToNextMajor(from: ...)

1.0 版本即将推出。我认为当前的 API 具有正确的形状和范围,并且其行为应该相对没有争议。

我不太确定的一个 API 是 formParams(用于处理 URL 的查询)。它有一些不错的想法,但它的行为来自 Javascript 的 URLSearchParams 类。该类有一些即使 JS 开发人员也不是很喜欢的怪癖,因此在锁定 API 之前,我们应该重新思考我们希望如何处理查询参数。

在某些领域,该语言也存在不足。URL 标准是一个实时标准,因此我们需要小心锁定可能实际更改的细节。一个问题是他们有一天可能会支持新型主机(目前只有域名/ipv4/ipv6),我们真的希望将此数据公开为 Swift 枚举 - WebURL.Host。但是 Swift 中的枚举是详尽无遗的,因此如果在更新中向该枚举添加一个新案例,那将是一个源代码破坏性更改。结果是,我们只能支持与主要版本增量一起使用的新型主机(例如 1.x -> 2.0)。

Swift 有非详尽枚举,强制 switch 语句处理 @unknown default: 模式,并允许库稍后添加新案例。您在 SDK 库中看到了这一点,但不幸的是,它不适用于源包。但是,此 API 真的需要是一个枚举才能使用;因此,尽管很痛苦,但我们正在接受这一限制。我们认为 URL 标准很快就会添加新型主机,这不是一个常见的现象。

除此之外,我对事情的发展感到相对满意。

如果您认为可以改进任何内容,那么现在是让我知道的好时机!打开一个 GitHub 问题或发布到 Swift 论坛

💝 赞助

我创建此库是因为我认为 Swift 是一种很棒的语言,但它缺少一个用于处理 URL 的高质量、现代库。将 WebURL 发展到这个阶段花费了大量时间,并且仍然有很多工作要做。如果您想表达对该项目的支持,请考虑捐赠一杯咖啡或其他东西。

ℹ️ 常见问题解答

如何留下反馈?

打开一个 GitHub 问题或发布到 Swift 论坛

欢迎提交拉取请求/审查意见/问题吗?

绝对欢迎!

这是否已准备好用于生产环境?

我认为是的。但要注意的是,API 在次要版本之间尚未稳定,仅在补丁版本之间稳定。

从一开始就非常重视测试和可靠性。该实现经过了广泛的测试,包括针对主要浏览器和其他库使用的共享 web-platform-tests 和 Unicode 一致性测试。拥有这些共享测试套件是一个非常有价值的资源,并且应该让您确信 WebURL 实际上像 WebKit 等其他项目一样解释该标准。

其他库功能也经过了广泛的测试,覆盖率约为 90%,并且许多难以测试的断言都使用模糊测试进行检查(例如,解析和序列化是幂等的,Foundation/WebURL 转换是安全的等等),因此行为得到了很好的理解。

此存储库中提供的基准测试包有助于确保我们在各种设备上具有一致的性能,并且可以衡量任何回归。我们还注意代码大小,并在实施整个标准的同时尽我们所能来减少它。

为什么命名为 WebURL

  1. WebURL 很短,但仍然与 Foundation 的 URL 足够区分。

  2. WHATWG 致力于为 Web 平台开发技术。通过遵循 WHATWG URL 标准,WebURL 可以被视为一种“Web 平台 URL”。