Skip to content

Commit

Permalink
Update README for 0.3.1
Browse files Browse the repository at this point in the history
  • Loading branch information
karwa committed Mar 20, 2022
1 parent c81045f commit 186d0b3
Showing 1 changed file with 87 additions and 46 deletions.
133 changes: 87 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@

A new URL type for Swift.

- 🌍 **Compliant** with the [latest URL Standard](https://url.spec.whatwg.org/). WebURL matches how modern browsers interpret URLs.
- 🌍 **Compliant**. WebURL conforms to the [latest URL Standard](https://url.spec.whatwg.org/), which specifies how modern browsers such as Safari and Chrome interpret URLs.

- ⚡️ **Fast**. Tuned for high performance and low memory use.

- 🍭 **Swifty**. The API makes liberal use of generics, in-place mutation, zero-cost abstractions, and other Swift features. It's a big step up from Foundation's `URL`.
- 🍭 **Delightful**. The API is designed around modern best practices, and liberal use of Swift features such as generics and zero-cost abstractions make it really expressive and powerful. It's not _just_ easier to use than Foundation's `URL` - more importantly, it's easier to use _correctly_.

- 🧳 **Portable**. The core WebURL library has no dependencies other than the Swift standard library, and no platform-specific behavior.

- 🥽 **Memory-safe**. WebURL uses carefully tuned bounds-checking techniques so it can be both fast _and_ safe.
- 🔗 **Interoperable**. Compatibility libraries allow you to use WebURL's modern, standards-compliant parser and API together with `Foundation` and `swift-system`, and our port of `async-http-client` demonstrates how to use WebURL together with `swift-nio`.

_(and of course, it's written in **100% Swift**)_.

📚 Check out the [Documentation](https://karwa.github.io/swift-url/main/documentation/weburl/) to learn more 📚
📚 Check out the [Documentation][weburl-docs] to learn more 📚
<br/>
<br/>

Expand All @@ -30,7 +30,7 @@ To use this package in a SwiftPM project, you need to set it up as a package dep
dependencies: [
.package(
url: "https://github.com/karwa/swift-url",
.upToNextMinor(from: "0.3.0")
.upToNextMinor(from: "0.3.1")
)
]

Expand Down Expand Up @@ -60,13 +60,15 @@ url.pathComponents += ["apple", "swift"]
url // "https://github.com/apple/swift"
```

📚 Check out the [Documentation](https://karwa.github.io/swift-url/main/documentation/weburl/) to learn about WebURL's API 📚
📚 Check out the [Documentation][weburl-docs] to learn about WebURL's API 📚
<br/>
<br/>

## 🔗 Integration with Foundation

WebURL 0.3.0 includes a library called `WebURLFoundationExtras`, which allows you to construct a `WebURL` from a Foundation `URL` value. To use it, add it to your target dependencies and import the module.
The `WebURLFoundationExtras` compatibility library allows you to convert between `WebURL` and Foundation `URL` values, and includes convenience wrappers for many Foundation APIs (such as `URLSession`), allowing them to be used directly with `WebURL` values.

To enable Foundation integration, add the compatibility library to your target dependencies and import it from your code.

```swift
targets: [
Expand All @@ -81,28 +83,46 @@ targets: [
]
```

Now you're able to accept URLs using a `Foundation.URL` value while taking advantage of WebURL's web-compatible normalization and fantastic API. Note that this can fail, because Foundation is quite loose about what it accepts as a "URL" and some ambiguous values aren't considered valid by the latest standard, but the things you expect to work will work :)
Now, you can take advantage of `WebURL`'s modern, standards-compliant parser and faster, more convenient API, all while keeping compatibility with existing clients and using `URLSession` to make requests (as is required on Apple platforms).

```swift
import Foundation
import WebURL
import WebURLFoundationExtras

// ℹ️ Make URLSession requests using WebURL.
func makeRequest(to url: WebURL) -> URLSessionDataTask {
return URLSession.shared.dataTask(with: url) {
data, response, error in
// ...
}
}

// ℹ️ Also supports Swift concurrency.
func processData(from url: WebURL) async throws {
let (data, _) = try await URLSession.shared.data(from: url)
// ...
}

// ℹ️ For libraries: move to WebURL without breaking
// compatibility with clients using Foundation's URL.
public func processURL(_ url: Foundation.URL) throws {
guard let webURL = WebURL(url) else {
throw InvalidURLError()
}
// Continue processing using WebURL.
// Internal code uses WebURL...
}
```

WebURL -> Foundation.URL conversion will be coming in a later version.
For more information about why `WebURL` is a great choice even for applications and libraries using Foundation, and a discussion about how to safely work with multiple URL standards, we **highly recommend** reading: [Using WebURL with Foundation][using-weburl-with-foundation].
<br/>
<br/>

## 🔗 Integration with swift-system

WebURL 0.2.0 includes a library called `WebURLSystemExtras`, which integrates with `swift-system` and Apple's `System.framework` and allows you to create `file:` URLs from `FilePath`s and vice versa. It has excellent support for both POSIX and Windows paths. Again, to use it, add the target dependency and import the module.
The `WebURLSystemExtras` compatibility library allows you to convert between `file:` URLs and `FilePath`s, using the `swift-system` package or Apple's `System` framework. It has excellent support for both POSIX and Windows paths, with features for security and legacy compatibility (e.g. non-Unicode file names) modelled on Chrome's implementation.

To enable `swift-system` integration, add the compatibility library to your target dependencies and import it from your code.

```swift
.target(
Expand All @@ -115,7 +135,7 @@ WebURL 0.2.0 includes a library called `WebURLSystemExtras`, which integrates wi
)
```

And you're good to go!
And that's it - you're good to go!

```swift
import System
Expand All @@ -131,68 +151,76 @@ func openFile(at url: WebURL) throws -> FileDescriptor {

## 🧪 async-http-client Port

We have a prototype port of [async-http-client](https://github.com/karwa/async-http-client) which uses WebURL for _all_ of its internal URL handling. If you're using AHC in your server, check it out to take advantage of the latest URL standard and WebURL's improved API. By default, it takes advantage of WebURL's Foundation integration so you can make requests using either type, but it can also be built without any Foundation dependency at all - meaning smaller binaries and faster startup times. It's also a great demonstration of how to adopt WebURL in your library.
Our prototype port of [async-http-client](https://github.com/karwa/async-http-client) uses WebURL for _all_ of its internal URL handling, including processing HTTP redirects. It's the best library available in Swift to ensure your HTTP requests use web-compatible URL processing, and is also a great demonstration of how to adopt WebURL in a library built upon `swift-nio`.

We'll be updating the port periodically, so if you wish to use it in an application we recommend making a fork and pulling in changes as you need.
By default, the port uses WebURL's Foundation compatibility for ease of integration, so you can make requests using either URL type, but it can also be built without any Foundation dependency at all - meaning smaller binaries and faster startup times.

**Note:** We'll be updating the port periodically, so if you wish to use it in an application we recommend making a fork and pulling in changes as you need.

```swift
import AsyncHTTPClient
import WebURL

let client = HTTPClient(eventLoopGroupProvider: .createNew)

func getTextFile(url: WebURL) throws -> EventLoopFuture<String?> {
let request = try HTTPClient.Request(url: url, method: .GET, headers: [:])
return client.execute(request: request, deadline: .none).map { response in
// ℹ️ Supports the traditional NIO EventLoopFuture API.
do {
let url = WebURL("https://github.com/karwa/swift-url/raw/main/README.md")!

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

let url = WebURL("https://github.com/karwa/swift-url/raw/main/README.md")!
try getTextFile(url: url).wait() // "# WebURL A new URL type for Swift..."
// ℹ️ Also supports Swift concurrency.
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..."
}
```
<br/>

# 📰 Project Status

WebURL is a complete URL library, implementing the latest version of the URL Standard (as of writing, that is commit `f787850`). It currently does not support Internationalized Domain Names (IDNA), but that support is planned.

It is tested against the [shared `web-platform-tests`](https://github.com/web-platform-tests/wpt/) used by major browsers, and passes all constructor and setter tests (other than those which require IDNA). The library includes a comprehensive set of APIs for working with URLs: getting/setting components, percent-encoding/decoding, reading and writing path components, form parameters, file paths, etc. Each has their own extensive sets of tests in addition to the shared web-platform-tests. The project is regularly benchmarked and fuzz-tested. The benchmark and fuzz-testing suite are available in the `Benchmarks` and `Fuzzers` directories respectively.
WebURL is a complete URL library, implementing the latest version of the URL Standard. It currently does not support Internationalized Domain Names (IDNA), but that support is planned.

Being a pre-1.0 package, the interfaces have not had time to stabilize. If there's anything you think could be improved, your feedback is welcome - either open a GitHub issue or post to the [Swift forums](https://forums.swift.org/c/related-projects/weburl/73).
It is tested against the [shared `web-platform-tests`](https://github.com/web-platform-tests/wpt/) used by the major browsers and other libraries, and passes all constructor and setter tests (other than those which require IDNA). We pool our implementation experience to ensure there is no divergence, and the fact that WebURL passes these tests should give you confidence that it interprets URLs just like the latest release of Safari or `rust-url`.

Prior to 1.0, it may be necessary to make source-breaking changes.
I'll do my best to keep these to a minimum, and any such changes will be accompanied by clear documentation explaining how to update your code.
WebURL also includes a comprehensive set of APIs for working with URLs: getting/setting components, percent-encoding and decoding, manipulating path components, query strings, file paths, etc. Each has these come with additional, very comprehensive sets of tests. The project is regularly benchmarked and fuzz-tested using the tools available in the `Benchmarks` and `Fuzzers` directories respectively.

## 🗺 Roadmap
Being a pre-1.0 package, we reserve the right to make source-breaking changes before committing to a stable API. We'll do our best to keep those to a minimum, and of course, any such changes will be accompanied by clear documentation explaining how to update your code.

Aside from stabilizing the API, the other priorities for v1.0 are:
If there's anything you think could be improved, this is a great time to let us know! Either open a GitHub issue or post to the [Swift forums][swift-forums-weburl].

1. `Foundation` interoperability.

Foundation's `URL` type is the primary type used for URLs on Swift today, and Foundation APIs such as `URLSession` are critical for many applications, in particular because of their system integration on Apple platforms.
## 🗺 Roadmap

We will provide a compatibility library which allows these APIs to be used together with `WebURL`.
Aside from stabilizing the API, the other priorities for v1.0 are:

2. More APIs for query parameters.
1. More APIs for query parameters.

A URL's `query` component is often used as a string of key-value pairs. This usage appears to have originated with HTML forms, and WebURL has excellent support for this via its `formParams` view, but popular convention is also to use keys and values that are _not strictly_ form-encoded. This can lead to decoding issues.
A URL's `query` component is often used as a string of key-value pairs. This usage appears to have originated with HTML forms, and WebURL has excellent support for it already via the `formParams` view -- but these days, by popular convention, it is also used with keys and values that are _not strictly_ form-encoded. This can lead to decoding issues, so we should offer a variant of `formParams` that allows for percent-encoding, not just form-encoding.

Additionally, we may want to consider making key lookup Unicode-aware. It makes sense, but AFAIK is unprecedented in other libraries and so may be surprising. But it does make a lot of sense. Feedback is welcome.

Looking beyond v1.0, the other features I'd like to add are:

3. Better APIs for `data:` URLs.
2. Better APIs for `data:` URLs.

WebURL already supports them as generic URLs, but it would be nice to add APIs for extracting the MIME type and decoding base64-encoded data.
WebURL already supports them as generic URLs, but it would be nice to add specialized APIs for extracting the MIME type and decoding base64-encoded data.

4. APIs for relative references.
3. APIs for relative references.

All `WebURL`s are absolute URLs (following the standard), and relative references are currently only supported as strings via the [`WebURL.resolve(_:)` method](https://karwa.github.io/swift-url/main/documentation/weburl/weburl/resolve(_:)).
All `WebURL`s are absolute URLs (following the standard), and relative references are currently only supported as strings via the [`WebURL.resolve(_:)` method][weburl-resolve].

It would be valuable to a lot of applications (e.g. server frameworks) to add a richer API for reading and manipulating relative references, instead of using only strings. We may also want to calculate the difference between 2 URLs and return the result as a relative reference.
It could be valuable to some applications to add richer APIs for reading and manipulating relative references, instead of using only strings. We may also want to calculate the difference between 2 URLs and return the result as a relative reference. It depends on what people actually need, so please do leave feedback.

5. Support Internationalized Domain Names (IDNA).
4. Support Internationalized Domain Names (IDNA).

This is part of the URL Standard, and its position on this list shouldn't be read as downplaying its importance. It is a high-priority item, but is currently blocked by other things.

Expand All @@ -202,27 +230,31 @@ Looking beyond v1.0, the other features I'd like to add are:

# 💝 Sponsorship

I'm creating this library because I think that Swift is a great language, and it deserves a high-quality, modern library for handling URLs. It has taken a lot of time to get things to this stage, and there is an exciting roadmap ahead. so if you (or the company you work for) benefit from this project, do consider sponsoring it to show your support and encourage future development. Maybe it saves you some time on your server instances, or saves you time chasing down weird bugs in your URL code.
I'm creating this library because I think that Swift is a great language, and it deserves a high-quality, modern library for handling URLs. I think it's a really good, production-quality implementation, it has taken a lot of time to get to this stage, and there is still an exciting roadmap ahead. So if you (or the company you work for) benefit from this project, consider donating a coffee to show your support. You don't have to; it's mostly about letting me know that people appreciate it. That's ultimately what motivates me.
<br/>
<br/>

# ℹ️ FAQ

## How do I leave feedback?

Either open a GitHub issue or post to the [Swift forums](https://forums.swift.org/c/related-projects/weburl/73).
Either open a GitHub issue or post to the [Swift forums][swift-forums-weburl].

## Are pull requests/code reviews/comments/questions welcome?
## Are pull requests/review comments/questions welcome?

Most definitely!

## Is this production-ready?

Yes, it is being used in production.
Yes. With the caveat that the API might see some minor adjustments between now and 1.0.

The implementation is extensively tested, including against the shared `web-platform-tests` used by the major browsers and other libraries, and which we've made _a lot_ of contributions to. As mentioned above, having that shared test suite across the various implementations is a really valuable resource and should give you confidence that WebURL will actually interpret URLs according to the standard.

We also verify a lot of things by regular fuzz-testing (e.g. that parsing and serialization are idempotent, that Foundation conversions are safe, etc), so we have confidence that the behavior is well understood.

The implementation is extensively tested (including against the shared `web-platform-tests` used by the major browsers, which we have also made contributions to, and by fuzz-testing), so we have confidence that the behavior is reliable.
Additionally, the benchmarks package available in this repository helps ensure we deliver consistent, excellent performance across a variety of devices.

Additionally, the benchmarks package available in this repository helps ensure the performance is well-understood, and that operations maintain a consistent performance profile. Benchmarks are run on a variety of devices, from high-end modern x64 computers to the raspberry pi.
We've taken testing and reliability extremely seriously from the very beginning, which is why we have confidence in claiming that this is the best-tested URL library available for Swift. To be quite frank, Foundation does not have anything even close to this.

## Why the name `WebURL`?

Expand All @@ -246,4 +278,13 @@ So having all these incompatible standards is a problem. Clearly, there was only

This is where the WHATWG comes in to it. The WHATWG is an industry association led by the major browser developers (currently, the steering committee consists of representatives from Apple, Google, Mozilla, and Microsoft), and there is high-level approval for their browsers to align with the standards developed by the group. The latest WebKit (Safari 15) is already in compliance. The WHATWG URL Living Standard defines how **actors on the web platform** should understand and manipulate URLs - how browsers process them, how code such as JavaScript's `URL` class interprets them, etc. And this applies at all levels, from URLs in HTML documents to HTTP redirect requests. This is the web's URL standard.

By aligning to the URL Living Standard, this project aims to provide the behavior you expect, with better reliability and interoperability, sharing a standard and test-suite with your browser, and engaging with the web standards process. And by doing so, we hope to make Swift an even more attractive language for both servers and client applications.
By aligning to the URL Living Standard, this project aims to provide the behavior you expect, with better reliability and interoperability, sharing a standard and test-suite with your browser, and engaging with the web standards process. And by doing so, we hope to make Swift an even more attractive language for both servers and client applications.





[swift-forums-weburl]: https://forums.swift.org/c/related-projects/weburl/73
[weburl-docs]: https://karwa.github.io/swift-url/main/documentation/weburl/
[weburl-resolve]: https://karwa.github.io/swift-url/main/documentation/weburl/weburl/resolve(_:)
[using-weburl-with-foundation]: https://karwa.github.io/swift-url/main/documentation/weburl/foundationinterop

0 comments on commit 186d0b3

Please sign in to comment.