Skip to content

Commit

Permalink
Allow dynamic WebSocket URLs (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
atdrendel authored Aug 28, 2023
1 parent 919b04e commit dc57d71
Show file tree
Hide file tree
Showing 9 changed files with 44 additions and 40 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ name: CI
on: push

jobs:
library:
runs-on: macos-latest
test:
runs-on: macos-13

steps:
- uses: actions/checkout@v3
- name: Select Xcode 14
run: sudo xcode-select -s /Applications/Xcode_14.3.app
run: sudo xcode-select -s /Applications/Xcode_14.3.1.app
- name: Test
run: swift test
8 changes: 4 additions & 4 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/shareup/async-extensions.git",
"state" : {
"revision" : "59504194f84b8c66a27503b5fd0640ac9b01f42a",
"version" : "4.1.0"
"revision" : "f8dec7a227bbbe15fd4df90c787d4c73e91451ba",
"version" : "4.2.1"
}
},
{
Expand Down Expand Up @@ -50,8 +50,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/shareup/websocket-apple.git",
"state" : {
"revision" : "2fa14629e689ac73e76b949b2216a6ae3f5545c5",
"version" : "4.0.1"
"revision" : "f409dca92eb61be6dd1183941a8590f9c4459f07",
"version" : "4.0.2"
}
}
],
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
),
.package(
url: "https://github.com/shareup/websocket-apple.git",
from: "4.0.1"
from: "4.0.2"
),
],
targets: [
Expand Down
4 changes: 2 additions & 2 deletions Sources/Phoenix/PhoenixChannel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import JSON
import os.log
import Synchronized

private typealias JoinFuture = AsyncExtensions.Future<(Ref, JSON)>
private typealias LeaveFuture = AsyncExtensions.Future<Void>
private typealias JoinFuture = AsyncThrowingFuture<(Ref, JSON)>
private typealias LeaveFuture = AsyncThrowingFuture<Void>

private typealias MessageSubject = PassthroughSubject<Message, Never>

Expand Down
34 changes: 19 additions & 15 deletions Sources/Phoenix/PhoenixSocket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ final actor PhoenixSocket {
}
}

nonisolated let url: URL
nonisolated let url: @Sendable () -> URL
nonisolated let timeout: UInt64
nonisolated let heartbeatInterval: UInt64

Expand Down Expand Up @@ -68,14 +68,14 @@ final actor PhoenixSocket {
private nonisolated let tasks = TaskStore()

init(
url: URL,
url: @escaping @Sendable () -> URL,
timeout: TimeInterval = 10,
heartbeatInterval: TimeInterval = 30,
pushEncoder: @escaping PushEncoder = Push.encode,
messageDecoder: @escaping MessageDecoder = Message.decode,
makeWebSocket: @escaping MakeWebSocket
) {
self.url = url.webSocketURLV2
self.url = { url().webSocketURLV2 }
self.timeout = timeout.nanoseconds
self.heartbeatInterval = heartbeatInterval.nanoseconds
self.pushEncoder = pushEncoder
Expand Down Expand Up @@ -221,17 +221,19 @@ extension PhoenixSocket {

private func listen() {
let task = Task { [weak self] in
guard let self, !Task.isCancelled,
let ws = await self.webSocket
guard !Task.isCancelled,
let ws = await self?.webSocket,
let decoder = self?.messageDecoder
else { return }

let decoder = self.messageDecoder

for await msg in ws.messages {
guard let self else { return }

do {

let message = try decoder(msg)

_ = self.pushes.didReceive(message)
_ = pushes.didReceive(message)

// NOTE: In the case a channel receives
// `Event.close`, it will remove itself from
Expand Down Expand Up @@ -284,14 +286,16 @@ extension PhoenixSocket {

let didSucceed = await withThrowingTaskGroup(of: Bool.self) { group in
group.addTask { [weak self] in
guard !Task.isCancelled, let self
else { throw TimeoutError() }
guard !Task.isCancelled else {
throw TimeoutError()
}

let push = Push(topic: "phoenix", event: .heartbeat)
let message: Message = try await self.request(push)
let message: Message? = try await self?.request(push)

guard !Task.isCancelled
else { throw TimeoutError() }
guard !Task.isCancelled, let message else {
throw TimeoutError()
}

return message.payload["status"] == "ok"
}
Expand Down Expand Up @@ -489,13 +493,13 @@ private extension PhoenixSocket {

return try await makeWebSocket(
id, // id
url, // url
url(), // url
.init(), // options
{}, // onOpen
{ [id] close in
Task { [weak self] in
guard let self, !Task.isCancelled else { return }
await self.doCloseFromServer(
await doCloseFromServer(
id: id,
error: WebSocketError.closeCodeAndReason(
close.code, close.reason
Expand Down
2 changes: 1 addition & 1 deletion Sources/Phoenix/Socket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public struct Socket: Identifiable, Sendable {

public extension Socket {
static func system(
url: URL,
url: @escaping @Sendable () -> URL,
decoder: @escaping MessageDecoder = Message.decode,
encoder: @escaping PushEncoder = Push.encode,
maxMessageSize: Int = 5 * 1024 * 1024
Expand Down
10 changes: 5 additions & 5 deletions Tests/PhoenixTests/PhoenixChannelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import XCTest
// do not apply to our socket or overlap other tests. We skip those tests.

final class PhoenixChannelTests: XCTestCase {
private let url = URL(string: "ws://0.0.0.0:4003/socket")!
private let url = { @Sendable in URL(string: "ws://0.0.0.0:4003/socket")! }

private var sendSubject: PassthroughSubject<WebSocketMessage, Never>!
private var receiveSubject: PassthroughSubject<WebSocketMessage, Never>!
Expand Down Expand Up @@ -237,7 +237,7 @@ final class PhoenixChannelTests: XCTestCase {
}

func testJoinsAfterSocketOpenAndJoinDelays() async throws {
let openFuture = AsyncExtensions.Future<Void>()
let openFuture = AsyncThrowingFuture<Void>()
let canJoin = Locked(false)
let didJoin = Locked(false)

Expand Down Expand Up @@ -299,7 +299,7 @@ final class PhoenixChannelTests: XCTestCase {
}

func testOpensAfterSocketOpenDelay() async throws {
let openFuture = AsyncExtensions.Future<Void>()
let openFuture = AsyncThrowingFuture<Void>()
let canJoin = Locked(false)
let didJoin = Locked(false)

Expand Down Expand Up @@ -377,8 +377,8 @@ final class PhoenixChannelTests: XCTestCase {
}
}

let didJoin1 = AsyncExtensions.Future<Void>()
let didJoin2 = AsyncExtensions.Future<Void>()
let didJoin1 = AsyncThrowingFuture<Void>()
let didJoin2 = AsyncThrowingFuture<Void>()

let channel = await self.makeChannel(socket)

Expand Down
16 changes: 8 additions & 8 deletions Tests/PhoenixTests/PhoenixSocketTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import XCTest
// do not apply to our socket or overlap other tests. We skip those tests.

final class PhoenixSocketTests: XCTestCase {
private let url = URL(string: "ws://0.0.0.0:4003/socket")!
private let url = { @Sendable in URL(string: "ws://0.0.0.0:4003/socket")! }

private var sendSubject: PassthroughSubject<WebSocketMessage, Never>!
private var receiveSubject: PassthroughSubject<WebSocketMessage, Never>!
Expand All @@ -42,8 +42,8 @@ final class PhoenixSocketTests: XCTestCase {
)

let url = socket.url
XCTAssertEqual(url.path, "/socket/websocket")
XCTAssertEqual(url.query, "vsn=2.0.0")
XCTAssertEqual(url().path, "/socket/websocket")
XCTAssertEqual(url().query, "vsn=2.0.0")

XCTAssertEqual(10 * NSEC_PER_SEC, socket.timeout)
XCTAssertEqual(30 * NSEC_PER_SEC, socket.heartbeatInterval)
Expand All @@ -58,8 +58,8 @@ final class PhoenixSocketTests: XCTestCase {
)

let url = socket.url
XCTAssertEqual(url.path, "/socket/websocket")
XCTAssertEqual(url.query, "vsn=2.0.0")
XCTAssertEqual(url().path, "/socket/websocket")
XCTAssertEqual(url().query, "vsn=2.0.0")

XCTAssertEqual(12 * NSEC_PER_SEC, socket.timeout)
XCTAssertEqual(29 * NSEC_PER_SEC, socket.heartbeatInterval)
Expand Down Expand Up @@ -587,7 +587,7 @@ final class PhoenixSocketTests: XCTestCase {
await socket.connect()
let channel = await socket.channel("abc", rejoinDelay: [0, 0.01])

let isErrored = AsyncExtensions.Future<Void>()
let isErrored = AsyncThrowingFuture<Void>()

try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask {
Expand Down Expand Up @@ -726,7 +726,7 @@ private extension PhoenixSocketTests {

private func future(
timeout: TimeInterval
) -> AsyncExtensions.Future<Void> {
) -> AsyncThrowingFuture<Void> {
.init(timeout: timeout)
}

Expand All @@ -735,7 +735,7 @@ private extension PhoenixSocketTests {
onOpen: @escaping WebSocketOnOpen = {},
onClose: @escaping WebSocketOnClose = { _ in }
) async throws -> WebSocket {
try await .system(url: url, onOpen: onOpen, onClose: onClose)
try await .system(url: url(), onOpen: onOpen, onClose: onClose)
}

func fake(
Expand Down
2 changes: 1 addition & 1 deletion Tests/PhoenixTests/PushBufferTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ final class PushBufferTests: XCTestCase {

func testEarlierTimeoutOverridesLaterTimeout() async throws {
let didTimeout = Locked(false)
let replyFut = Future<Message>()
let replyFut = AsyncThrowingFuture<Message>()

let buffer = PushBuffer()
buffer.resume()
Expand Down

0 comments on commit dc57d71

Please sign in to comment.