From d1a470301fdd54304aaf9b855d83843a13edeca2 Mon Sep 17 00:00:00 2001 From: Henry Javier Serrano Echeverria Date: Sat, 25 Jan 2025 14:19:52 +0900 Subject: [PATCH 01/10] Updated conformance to strict concurrency --- FormatScript.sh | 6 -- Package.swift | 74 +++++++++---------- README.md | 10 +-- .../AsyncWebSocketClientProtocol.swift | 2 +- .../AsyncWebSocketData.swift | 2 +- .../AsyncWebSocketEvent.swift | 2 +- .../StreamGenerator/StreamGenerator.swift | 2 +- .../URLSessionWebSocketTaskWrapper.swift | 15 ++-- .../AsyncWebSocketClientMock.swift | 5 +- SwiftFormatConfiguration.json | 53 ------------- .../MockURLSessionWebSocketTaskWrapper.swift | 3 +- 11 files changed, 59 insertions(+), 115 deletions(-) delete mode 100755 FormatScript.sh delete mode 100644 SwiftFormatConfiguration.json diff --git a/FormatScript.sh b/FormatScript.sh deleted file mode 100755 index 32104c0..0000000 --- a/FormatScript.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -swift-format format --in-place --recursive Sources --configuration SwiftFormatConfiguration.json - -swift-format format --in-place --recursive Tests --configuration SwiftFormatConfiguration.json - diff --git a/Package.swift b/Package.swift index e9e40c1..3dd6a23 100644 --- a/Package.swift +++ b/Package.swift @@ -1,43 +1,43 @@ -// swift-tools-version:5.5 +// swift-tools-version:6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( - name: "AsyncWebSocketClient", - platforms: [.iOS(.v13), .macOS(.v12), .watchOS(.v6), .tvOS(.v13)], - products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. - .library( - name: "AsyncWebSocketClient", - targets: ["AsyncWebSocketClient"]), - .library( - name: "AsyncWebSocketClientMocks", - targets: ["AsyncWebSocketClientMocks"]), - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - .package(url: "https://github.com/Henryforce/AsyncTimeSequences", from: "0.0.9") - ], - targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. - .target( - name: "AsyncWebSocketClient", - dependencies: [ - "AsyncTimeSequences", - ]), - .target( - name: "AsyncWebSocketClientMocks", - dependencies: [ - "AsyncWebSocketClient", - ]), - .testTarget( - name: "AsyncWebSocketClientTests", - dependencies: [ - "AsyncWebSocketClient", - "AsyncTimeSequences", - .product(name: "AsyncTimeSequencesSupport", package: "AsyncTimeSequences"), - ]), - ] + name: "AsyncWebSocketClient", + platforms: [.iOS(.v13), .macOS(.v12), .watchOS(.v6), .tvOS(.v13)], + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "AsyncWebSocketClient", + targets: ["AsyncWebSocketClient"]), + .library( + name: "AsyncWebSocketClientMocks", + targets: ["AsyncWebSocketClientMocks"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + .package(url: "https://github.com/Henryforce/AsyncTimeSequences", from: "0.0.9") + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "AsyncWebSocketClient", + dependencies: [ + "AsyncTimeSequences" + ]), + .target( + name: "AsyncWebSocketClientMocks", + dependencies: [ + "AsyncWebSocketClient" + ]), + .testTarget( + name: "AsyncWebSocketClientTests", + dependencies: [ + "AsyncWebSocketClient", + "AsyncTimeSequences", + .product(name: "AsyncTimeSequencesSupport", package: "AsyncTimeSequences"), + ]), + ] ) diff --git a/README.md b/README.md index b3e8dd5..3d67ea2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![badge-platforms][] [![badge-spm][]][spm] -This is a package that contains a client behaving as a wrapper for the URLSessionWebSocketTask provided by Apple. +This is a package that contains a client behaving as an async wrapper for the URLSessionWebSocketTask provided by Apple. ## Usage @@ -16,7 +16,7 @@ try await client.send(.string("Hello world!")) // Either raw data or strings can let stream = await client.listenStream() // Returns an AsyncStream of events for await event in stream { - print(event) // Print events such as data received, connection opened, connection closed + print(event) // Print events such as data received, connection opened, connection closed } ``` @@ -25,11 +25,11 @@ for await event in stream { You can also send encodable objects to the client: ```swift -struct MyEncodableClass: Encodable { - let title: String +struct MyEncodableObject: Encodable, Sendable { + let title: String } -let objectToEncode = MyEncodableClass(title: "Title") +let objectToEncode = MyEncodableObject(title: "Title") try await client.sendJSONData(objectToEncode) ``` diff --git a/Sources/AsyncWebSocketClient/AsyncWebSocketClientProtocol.swift b/Sources/AsyncWebSocketClient/AsyncWebSocketClientProtocol.swift index 68fdd42..dce57f4 100644 --- a/Sources/AsyncWebSocketClient/AsyncWebSocketClientProtocol.swift +++ b/Sources/AsyncWebSocketClient/AsyncWebSocketClientProtocol.swift @@ -7,7 +7,7 @@ import Foundation -public protocol AsyncWebSocketClientProtocol { +public protocol AsyncWebSocketClientProtocol: Sendable { func connect() async throws func disconnect() async throws func send(_ data: AsyncWebSocketData) async throws diff --git a/Sources/AsyncWebSocketClient/AsyncWebSocketData.swift b/Sources/AsyncWebSocketClient/AsyncWebSocketData.swift index 0f794f6..ce1a3d4 100644 --- a/Sources/AsyncWebSocketClient/AsyncWebSocketData.swift +++ b/Sources/AsyncWebSocketClient/AsyncWebSocketData.swift @@ -7,7 +7,7 @@ import Foundation -public enum AsyncWebSocketData { +public enum AsyncWebSocketData: Sendable { case data(Data) case string(String) } diff --git a/Sources/AsyncWebSocketClient/AsyncWebSocketEvent.swift b/Sources/AsyncWebSocketClient/AsyncWebSocketEvent.swift index 85b875c..1c3ff89 100644 --- a/Sources/AsyncWebSocketClient/AsyncWebSocketEvent.swift +++ b/Sources/AsyncWebSocketClient/AsyncWebSocketEvent.swift @@ -7,7 +7,7 @@ import Foundation -public enum AsyncWebSocketEvent { +public enum AsyncWebSocketEvent: Sendable { case socketOpened case socketClosed(Error?) case dataReceived(AsyncWebSocketData) diff --git a/Sources/AsyncWebSocketClient/StreamGenerator/StreamGenerator.swift b/Sources/AsyncWebSocketClient/StreamGenerator/StreamGenerator.swift index 69bf753..baf1237 100644 --- a/Sources/AsyncWebSocketClient/StreamGenerator/StreamGenerator.swift +++ b/Sources/AsyncWebSocketClient/StreamGenerator/StreamGenerator.swift @@ -7,7 +7,7 @@ import Foundation -actor StreamGenerator { +actor StreamGenerator { var subscribers = [UUID: AsyncStream.Continuation]() var value: T { _value } diff --git a/Sources/AsyncWebSocketClient/URLSessionWebSocketTaskWrapper.swift b/Sources/AsyncWebSocketClient/URLSessionWebSocketTaskWrapper.swift index 26961f8..c02063b 100644 --- a/Sources/AsyncWebSocketClient/URLSessionWebSocketTaskWrapper.swift +++ b/Sources/AsyncWebSocketClient/URLSessionWebSocketTaskWrapper.swift @@ -11,10 +11,12 @@ protocol URLSessionWebSocketTaskWrapper { func wrappedResume() func wrappedCancel(with closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) func wrappedSend( - _ message: URLSessionWebSocketTask.Message, completionHandler: @escaping (Error?) -> Void) + _ message: URLSessionWebSocketTask.Message, + completionHandler: @Sendable @escaping (Error?) -> Void + ) func wrappedReceive( - completionHandler: @escaping (Result) -> Void) - func wrappedSendPing(pongReceiveHandler: @escaping (Error?) -> Void) + completionHandler: @Sendable @escaping (Result) -> Void) + func wrappedSendPing(pongReceiveHandler: @Sendable @escaping (Error?) -> Void) } extension URLSessionWebSocketTask: URLSessionWebSocketTaskWrapper { @@ -26,16 +28,15 @@ extension URLSessionWebSocketTask: URLSessionWebSocketTaskWrapper { cancel(with: closeCode, reason: reason) } - func wrappedSend(_ message: Message, completionHandler: @escaping (Error?) -> Void) { + func wrappedSend(_ message: Message, completionHandler: @Sendable @escaping (Error?) -> Void) { send(message, completionHandler: completionHandler) } - func wrappedReceive(completionHandler: @escaping (Result) -> Void) { + func wrappedReceive(completionHandler: @Sendable @escaping (Result) -> Void) { receive(completionHandler: completionHandler) } - func wrappedSendPing(pongReceiveHandler: @escaping (Error?) -> Void) { - + func wrappedSendPing(pongReceiveHandler: @Sendable @escaping (Error?) -> Void) { sendPing { error in pongReceiveHandler(error) } diff --git a/Sources/AsyncWebSocketClientMocks/AsyncWebSocketClientMock.swift b/Sources/AsyncWebSocketClientMocks/AsyncWebSocketClientMock.swift index 377f4b5..73a25f6 100644 --- a/Sources/AsyncWebSocketClientMocks/AsyncWebSocketClientMock.swift +++ b/Sources/AsyncWebSocketClientMocks/AsyncWebSocketClientMock.swift @@ -6,9 +6,10 @@ // import AsyncWebSocketClient +@preconcurrency import Combine import Foundation -open class AsyncWebSocketClientMock: AsyncWebSocketClientProtocol { +open class AsyncWebSocketClientMock: AsyncWebSocketClientProtocol, @unchecked Sendable { public var connectWasCalledCount = 0 public func connect() async throws { connectWasCalledCount += 1 @@ -31,7 +32,7 @@ open class AsyncWebSocketClientMock: AsyncWebSocketClientProtocol { let stream = $streamSocketEvent.values return AsyncStream { continuation in - let cancellableTask = Task { + let cancellableTask = Task { @Sendable in for await event in stream { try Task.checkCancellation() continuation.yield(event) diff --git a/SwiftFormatConfiguration.json b/SwiftFormatConfiguration.json deleted file mode 100644 index d6854ba..0000000 --- a/SwiftFormatConfiguration.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "blankLineBetweenMembers" : { - "ignoreSingleLineProperties" : true - }, - "indentation" : { - "spaces" : 2 - }, - "indentConditionalCompilationBlocks" : true, - "lineBreakBeforeControlFlowKeywords" : false, - "lineBreakBeforeEachArgument" : false, - "lineLength" : 100, - "maximumBlankLines" : 1, - "respectsExistingLineBreaks" : true, - "rules" : { - "AllPublicDeclarationsHaveDocumentation" : true, - "AlwaysUseLowerCamelCase" : true, - "AmbiguousTrailingClosureOverload" : true, - "BeginDocumentationCommentWithOneLineSummary" : true, - "BlankLineBetweenMembers" : true, - "CaseIndentLevelEqualsSwitch" : true, - "DoNotUseSemicolons" : true, - "DontRepeatTypeInStaticProperties" : true, - "FullyIndirectEnum" : true, - "GroupNumericLiterals" : true, - "IdentifiersMustBeASCII" : true, - "MultiLineTrailingCommas" : true, - "NeverForceUnwrap" : true, - "NeverUseForceTry" : true, - "NeverUseImplicitlyUnwrappedOptionals" : true, - "NoAccessLevelOnExtensionDeclaration" : true, - "NoBlockComments" : true, - "NoCasesWithOnlyFallthrough" : true, - "NoEmptyTrailingClosureParentheses" : true, - "NoLabelsInCasePatterns" : true, - "NoLeadingUnderscores" : true, - "NoParensAroundConditions" : true, - "NoVoidReturnOnFunctionSignature" : true, - "OneCasePerLine" : true, - "OneVariableDeclarationPerLine" : true, - "OnlyOneTrailingClosureArgument" : true, - "OrderedImports" : true, - "ReturnVoidInsteadOfEmptyTuple" : true, - "UseEnumForNamespacing" : true, - "UseLetInEveryBoundCaseVariable" : true, - "UseShorthandTypeNames" : true, - "UseSingleLinePropertyGetter" : true, - "UseSynthesizedInitializer" : true, - "UseTripleSlashForDocumentationComments" : true, - "ValidateDocumentationComments" : true - }, - "tabWidth" : 8, - "version" : 1 -} diff --git a/Tests/AsyncWebSocketClientTests/Mocks/MockURLSessionWebSocketTaskWrapper.swift b/Tests/AsyncWebSocketClientTests/Mocks/MockURLSessionWebSocketTaskWrapper.swift index fe47579..a3e52b6 100644 --- a/Tests/AsyncWebSocketClientTests/Mocks/MockURLSessionWebSocketTaskWrapper.swift +++ b/Tests/AsyncWebSocketClientTests/Mocks/MockURLSessionWebSocketTaskWrapper.swift @@ -9,7 +9,8 @@ import Foundation @testable import AsyncWebSocketClient -final class MockURLSessionWebSocketTaskWrapper: URLSessionWebSocketTaskWrapper { +final class MockURLSessionWebSocketTaskWrapper: URLSessionWebSocketTaskWrapper, @unchecked Sendable +{ init() {} var resumeWasCalledCount = 0 From daefcabc33f860bf70e16f78a3bb83079f700556 Mon Sep 17 00:00:00 2001 From: Henry Javier Serrano Echeverria Date: Sat, 25 Jan 2025 16:58:01 +0900 Subject: [PATCH 02/10] Updated workflow setup --- .github/workflows/swift.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index de4dd4a..071ad1f 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -2,11 +2,9 @@ name: Swift on: push: - branches: [ main ] + branches: [ "master" ] pull_request: - branches: [ main ] -env: - DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer + branches: [ "master" ] jobs: build: @@ -14,7 +12,7 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Build run: swift build -v - name: Run tests From 055721ba5c9dfe8a3e984b3f71418572a925add2 Mon Sep 17 00:00:00 2001 From: Henry Javier Serrano Echeverria Date: Sat, 25 Jan 2025 17:02:21 +0900 Subject: [PATCH 03/10] Updated workflow setup --- .github/workflows/swift.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 071ad1f..8c586f4 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -2,9 +2,9 @@ name: Swift on: push: - branches: [ "master" ] + branches: [ "main" ] pull_request: - branches: [ "master" ] + branches: [ "main" ] jobs: build: From 26cabcc602b4ce8908783a905d6fac1b0bbcbaff Mon Sep 17 00:00:00 2001 From: Henry Javier Serrano Echeverria Date: Sat, 25 Jan 2025 17:17:15 +0900 Subject: [PATCH 04/10] Updated workflow setup --- .github/workflows/swift.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 8c586f4..d4ac900 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -9,11 +9,17 @@ on: jobs: build: - runs-on: macos-latest + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Build - run: swift build -v - - name: Run tests - run: swift test -v + - uses: actions/checkout@v3 + + - name: Set up Xcode + uses: actions/setup-xcode@v3 + with: + xcode-version: 'latest' + + - name: Build and Test + run: | + swift build + swift test From ff2b6860c50dc00a93f1d271239a03f5df7ef425 Mon Sep 17 00:00:00 2001 From: Henry Javier Serrano Echeverria Date: Sat, 25 Jan 2025 17:21:34 +0900 Subject: [PATCH 05/10] Updated workflow setup --- .github/workflows/swift.yml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index d4ac900..2eb126b 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -8,18 +8,19 @@ on: jobs: build: - - runs-on: ubuntu-latest + name: Swift ${{ matrix.swift }} on ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + swift: ["6.0"] + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - - name: Set up Xcode - uses: actions/setup-xcode@v3 - with: - xcode-version: 'latest' - - - name: Build and Test - run: | - swift build - swift test + - uses: actions/checkout@v4 + - uses: swift-actions/setup-swift@65540b95f51493d65f5e59e97dcef9629ddf11bf + with: + swift-version: "5.3.3" + - name: Build + run: swift build -v + - name: Run tests + run: swift test -v From 0099439173e38ebf4e930b68d67a92a816cd268f Mon Sep 17 00:00:00 2001 From: Henry Javier Serrano Echeverria Date: Sat, 25 Jan 2025 17:22:49 +0900 Subject: [PATCH 06/10] Updated workflow setup --- .github/workflows/swift.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 2eb126b..88da530 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v4 - uses: swift-actions/setup-swift@65540b95f51493d65f5e59e97dcef9629ddf11bf with: - swift-version: "5.3.3" + swift-version: "6.0" - name: Build run: swift build -v - name: Run tests From 37ad613009cd86608597f1544a2b7c4ff2f5b4cb Mon Sep 17 00:00:00 2001 From: Henry Javier Serrano Echeverria Date: Sat, 25 Jan 2025 17:29:50 +0900 Subject: [PATCH 07/10] Updated workflow setup --- .github/workflows/swift.yml | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 88da530..e9e82a9 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -8,19 +8,11 @@ on: jobs: build: - name: Swift ${{ matrix.swift }} on ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest] - swift: ["6.0"] - runs-on: ${{ matrix.os }} - + runs-on: macos-latest + steps: - - uses: actions/checkout@v4 - - uses: swift-actions/setup-swift@65540b95f51493d65f5e59e97dcef9629ddf11bf - with: - swift-version: "6.0" - - name: Build - run: swift build -v - - name: Run tests - run: swift test -v + - uses: swift-actions/setup-swift@v2 + - name: Build + run: swift build -v + - name: Run tests + run: swift test -v From 80e3ad62d4d233e3f39092188de213944b3d1cfd Mon Sep 17 00:00:00 2001 From: Henry Javier Serrano Echeverria Date: Sat, 25 Jan 2025 17:37:05 +0900 Subject: [PATCH 08/10] Updated workflow setup --- .github/workflows/swift.yml | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index e9e82a9..d538059 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -8,11 +8,25 @@ on: jobs: build: - runs-on: macos-latest + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-24.04-arm, macos-latest-xlarge] + swift: ["6.0"] steps: - - uses: swift-actions/setup-swift@v2 - - name: Build - run: swift build -v - - name: Run tests - run: swift test -v + - uses: actions/checkout@v4 + + - name: Setup Swift + uses: swift-actions/setup-swift@v2 + with: + swift-version: ${{ matrix.swift }} + + - run: swift --version + + - name: Build + run: swift build -v + + - name: Run tests + run: swift test -v From d936c90d263b51ed2c5fef1aca4c332af256372f Mon Sep 17 00:00:00 2001 From: Henry Javier Serrano Echeverria Date: Sat, 25 Jan 2025 17:39:16 +0900 Subject: [PATCH 09/10] Updated workflow setup --- .github/workflows/swift.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index d538059..2a932f4 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - os: [ubuntu-24.04-arm, macos-latest-xlarge] + os: [ubuntu-24.04-arm, macos-latest] swift: ["6.0"] steps: From f95a979f62347bf4254ed1df3ee552729ac944f1 Mon Sep 17 00:00:00 2001 From: Henry Javier Serrano Echeverria Date: Sat, 25 Jan 2025 17:41:25 +0900 Subject: [PATCH 10/10] Updated workflow setup --- .github/workflows/swift.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 2a932f4..184ea45 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - os: [ubuntu-24.04-arm, macos-latest] + os: [macos-latest] swift: ["6.0"] steps: