Skip to content

Commit b3a0aaf

Browse files
authored
Make URLSessionWebSocketTransport init synchronous (#5)
* Make URLSessionWebSocketTransport init synchronous We want to access nonisolated self, which is only possible since swift-tools-version 5.10 and https://github.com/apple/swift-evolution/blob/main/proposals/0327-actor-initializers.md * Update workflow
1 parent c8d3ead commit b3a0aaf

File tree

5 files changed

+18
-45
lines changed

5 files changed

+18
-45
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ jobs:
2424
- 'watchOS Simulator'
2525
include:
2626
- platform: 'macOS'
27-
destination: 'arch=x86_64'
27+
destination: 'arch=arm64'
2828
- platform: 'iOS Simulator'
2929
destination: 'OS=latest,name=iPhone 15 Pro'
3030
- platform: 'tvOS Simulator'
3131
destination: 'OS=latest,name=Apple TV 4K (3rd generation)'
3232
- platform: 'watchOS Simulator'
3333
destination: 'OS=latest,name=Apple Watch Series 9 (45mm)'
3434
name: ${{ matrix.platform }} Tests
35-
runs-on: macos-13
35+
runs-on: macos-14
3636
steps:
3737
- name: Select latest available Xcode
3838
uses: maxim-lobanov/setup-xcode@v1
@@ -95,40 +95,3 @@ jobs:
9595
# with:
9696
# cc_token: ${{ secrets.CODECOV_TOKEN }}
9797
# cc_verbose: true
98-
99-
codeql:
100-
if: ${{ !(github.event.pull_request.draft || false) }}
101-
name: CodeQL Analysis
102-
runs-on: ubuntu-latest
103-
container:
104-
image: swift:5.9-jammy
105-
permissions: { actions: write, contents: read, security-events: write }
106-
steps:
107-
- name: Checkout code
108-
uses: actions/checkout@v4
109-
- name: Mark repo safe
110-
run: |
111-
git config --global --add safe.directory "${GITHUB_WORKSPACE}"
112-
- name: Initialize CodeQL
113-
uses: github/codeql-action/init@v3
114-
with: { languages: swift }
115-
- name: Perform build
116-
run: swift build
117-
- name: Run CodeQL analyze
118-
uses: github/codeql-action/analyze@v3
119-
120-
dependency-graph:
121-
if: ${{ github.event_name == 'push' }}
122-
runs-on: ubuntu-latest
123-
container: swift:jammy
124-
permissions:
125-
contents: write
126-
steps:
127-
- name: Check out code
128-
uses: actions/checkout@v4
129-
- name: Set up dependencies
130-
run: |
131-
git config --global --add safe.directory "${GITHUB_WORKSPACE}"
132-
apt-get update && apt-get install -y curl
133-
- name: Submit dependency graph
134-
uses: vapor-community/[email protected]

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version:5.9
1+
// swift-tools-version:5.10
22
//===----------------------------------------------------------------------===//
33
//
44
// This source file is part of the StructuredWebSocketClient open source project

Sources/StructuredWebSocketClient/URLSession+DelegateHelpers.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,20 @@ extension SimpleURLSessionTaskDelegate {
5050
/// which is a reference type, to ensure that an explicitly weak reference may be taken.
5151
final class URLSessionDelegateAdapter<D: SimpleURLSessionTaskDelegate & AnyObject>: NSObject, URLSessionWebSocketDelegate {
5252
private weak var realDelegate: D?
53-
53+
5454
init(adapting realDelegate: D) {
5555
self.realDelegate = realDelegate
5656
}
57-
57+
58+
/// Allows initializing without providing the real delegate immediately
59+
///
60+
/// This is helpful in non-isolated actor initializers, where self is not available yet.
61+
override init() {}
62+
63+
func setDelegate(_ realDelegate: D) {
64+
self.realDelegate = realDelegate
65+
}
66+
5867
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: (any Error)?) {
5968
self.realDelegate?.urlSession(session, task: task, didCompleteWithError: error)
6069
}

Sources/StructuredWebSocketClient/URLSession+MessageTransport.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,14 @@ public actor URLSessionWebSocketTransport: MessageTransport, SimpleURLSessionTas
7373
private let events: AsyncChannel<WebSocketEvent> = .init()
7474
private var isAlreadyClosed = false
7575

76-
public init(request: URLRequest, urlSession: URLSession = .shared, logger: Logger? = nil) async {
76+
public init(request: URLRequest, urlSession: URLSession = .shared, logger: Logger? = nil) {
7777
self.logger = logger ?? .init(label: "URLSessionWebSocketTransport")
7878

7979
self.delegateQueue = urlSession.delegateQueue
80-
self.delegateHandler = URLSessionDelegateAdapter(adapting: self)
80+
self.delegateHandler = URLSessionDelegateAdapter() // self is not yet available in non-isolated init
8181
let urlSession = URLSession(configuration: urlSession.configuration, delegate: self.delegateHandler, delegateQueue: urlSession.delegateQueue)
8282
self.wsTask.task = urlSession.webSocketTask(with: request)
83+
self.delegateHandler?.setDelegate(self) // so we have to set the delegate here
8384
}
8485

8586
public func send(_ message: URLSessionWebSocketTask.Message) async throws {

Tests/StructuredWebSocketClientTests/WebSocketClientTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ final class WebSocketClientTests: XCTestCase {
6868

6969
// Postman's echo server
7070
let request = URLRequest(url: .init(string: "wss://ws.postman-echo.com/raw")!)
71-
let client = await WebSocketClient(inboundMiddleware: nil, outboundMiddleware: nil, transport: URLSessionWebSocketTransport(request: request), logger: logger)
71+
let client = WebSocketClient(inboundMiddleware: nil, outboundMiddleware: nil, transport: URLSessionWebSocketTransport(request: request), logger: logger)
7272
let outMsg = "Hi there"
7373
let expectation = XCTestExpectation(description: "message received")
7474

0 commit comments

Comments
 (0)