diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index de4dd4a..184ea45 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -2,20 +2,31 @@ name: Swift on: push: - branches: [ main ] + branches: [ "main" ] pull_request: - branches: [ main ] -env: - DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer + branches: [ "main" ] jobs: build: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [macos-latest] + swift: ["6.0"] + + steps: + - uses: actions/checkout@v4 + + - name: Setup Swift + uses: swift-actions/setup-swift@v2 + with: + swift-version: ${{ matrix.swift }} + + - run: swift --version - runs-on: macos-latest + - name: Build + run: swift build -v - steps: - - uses: actions/checkout@v2 - - name: Build - run: swift build -v - - name: Run tests - run: swift test -v + - name: Run tests + run: swift test -v 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