Skip to content

Commit

Permalink
Use NWConnection instead of URLSessionWebSocketTask (#10)
Browse files Browse the repository at this point in the history
- Replace URLSessionWebSocketTask with NWConnection
- Conform to published behavior of web browsers' WebSocket
- Add support for Swift Concurrency
  • Loading branch information
atdrendel authored Apr 18, 2022
1 parent 4532e16 commit bf9ff2b
Show file tree
Hide file tree
Showing 20 changed files with 1,507 additions and 946 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
on: push

jobs:
test:
runs-on: macos-12

steps:
- uses: actions/checkout@v2
# Available environments: https://github.com/actions/virtual-environments/blob/main/images/macos/macos-12-Readme.md#xcode
- run: xcversion select 13.3
- run: swift package resolve
- run: swift test --skip-update
20 changes: 0 additions & 20 deletions .github/workflows/swift.yml

This file was deleted.

4 changes: 3 additions & 1 deletion .swiftformat
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
--funcattributes prev-line
--minversion 0.47.2
--maxwidth 100
--maxwidth 96
--typeattributes prev-line
--wraparguments before-first
--wrapparameters before-first
--wrapcollections before-first
--xcodeindentation enabled
34 changes: 0 additions & 34 deletions Package.resolved

This file was deleted.

27 changes: 7 additions & 20 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,23 @@ import PackageDescription
let package = Package(
name: "WebSocket",
platforms: [
.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6),
.macOS(.v11), .iOS(.v14), .tvOS(.v14), .watchOS(.v7),
],
products: [
.library(
name: "WebSocket",
targets: ["WebSocket"]
)],
dependencies: [
.package(
name: "Synchronized",
url: "https://github.com/shareup/synchronized.git",
from: "3.0.0"
),
.package(
name: "WebSocketProtocol",
url: "https://github.com/shareup/websocket-protocol.git",
from: "2.3.2"
),
.package(name: "swift-nio", url: "https://github.com/apple/swift-nio.git", from: "2.0.0")],
],
dependencies: [],
targets: [
.target(
name: "WebSocket",
dependencies: ["Synchronized", "WebSocketProtocol"]),
dependencies: []
),
.testTarget(
name: "WebSocketTests",
dependencies: [
.product(name: "NIO", package: "swift-nio"),
.product(name: "NIOHTTP1", package: "swift-nio"),
.product(name: "NIOWebSocket", package: "swift-nio"),
"WebSocket",
])
dependencies: ["WebSocket"]
),
]
)
43 changes: 22 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,32 @@

## _(macOS, iOS, iPadOS, tvOS, and watchOS)_

A concrete implementation of a WebSocket client implemented by wrapping Apple's `URLSessionWebSocketTask` and conforming to [`WebSocketProtocol`](https://github.com/shareup/websocket-protocol). `WebSocket` exposes a simple API and conforms to Apple's Combine [`Publisher`](https://developer.apple.com/documentation/combine/publisher).
A concrete implementation of a WebSocket client implemented by wrapping Apple's [`NWConnection`](https://developer.apple.com/documentation/network/nwconnection).

The public "interface" of `WebSocket` is a simple struct whose public "methods" are exposed as closures. The reason for this design is to make it easy to inject fake `WebSocket`s into your code for testing purposes.

The actual implementation is `SystemWebSocket`, but this type is not publicly accessible. Instead, you can access it via `WebSocket.system(url:)`. `SystemWebSocket` tries its best to mirror the documented behavior of web browsers' [`WebSocket`](http://developer.mozilla.org/en-US/docs/Web/API/WebSocket). Please report any deviations as bugs.

`WebSocket` exposes a simple API, makes heavy use of [Swift Concurrency](https://developer.apple.com/documentation/swift/swift_standard_library/concurrency), and conforms to Apple's Combine [`Publisher`](https://developer.apple.com/documentation/combine/publisher).

## Usage

```swift
let socket = WebSocket(url: url(49999))

let sub = socket.sink(
receiveCompletion: { print("Socket closed: \(String(describing: $0))") },
receiveValue: { (result) in
switch result {
case .success(.open):
socket.send("First message")
case .success(.string(let incoming)):
print("Received \(incoming)")
case .failure:
socket.close()
default:
break
}
}
)
defer { sub.cancel() }

socket.connect()
// `WebSocket` starts connecting to the specified `URL` immediately.
let socket = WebSocket.system(url: url(49999))

// Wait for `WebSocket` to be ready to send and receive messages.
try await socket.open()

// Send a message to the server
try await socket.send(.text("hello"))

// Receive messages from the server
for await message in socket.messages {
print(message)
}

try await socket.close()
```

## Tests
Expand Down
Loading

0 comments on commit bf9ff2b

Please sign in to comment.