diff --git a/Examples/ElizaCocoaPodsApp/Podfile.lock b/Examples/ElizaCocoaPodsApp/Podfile.lock index 727fcb34..51daf1d2 100644 --- a/Examples/ElizaCocoaPodsApp/Podfile.lock +++ b/Examples/ElizaCocoaPodsApp/Podfile.lock @@ -20,4 +20,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: b598f373a6ab5add976b09c2ac79029bf2200d48 -COCOAPODS: 1.13.0 +COCOAPODS: 1.15.2 diff --git a/Libraries/Connect/Internal/Interceptors/GRPCWebInterceptor.swift b/Libraries/Connect/Internal/Interceptors/GRPCWebInterceptor.swift index 7dff6061..e331187d 100644 --- a/Libraries/Connect/Internal/Interceptors/GRPCWebInterceptor.swift +++ b/Libraries/Connect/Internal/Interceptors/GRPCWebInterceptor.swift @@ -76,7 +76,9 @@ extension GRPCWebInterceptor: UnaryInterceptor { headers: response.headers, message: response.message, trailers: response.trailers, - error: ConnectError.unaryResponseHasNoMessage(), + error: ConnectError( + code: .unimplemented, message: "unary response has no message" + ), tracingInfo: response.tracingInfo )) } @@ -303,7 +305,7 @@ private extension HTTPResponse { headers: self.headers, message: nil, trailers: trailers, - error: ConnectError.unaryResponseHasNoMessage(), + error: ConnectError(code: .unimplemented, message: "unary response has no message"), tracingInfo: self.tracingInfo ) } else { @@ -318,11 +320,3 @@ private extension HTTPResponse { } } } - -private extension ConnectError { - static func unaryResponseHasNoMessage() -> Self { - return ConnectError( - code: .unimplemented, message: "unary response has no message" - ) - } -} diff --git a/Libraries/Connect/Internal/Streaming/BidirectionalAsyncStream.swift b/Libraries/Connect/Internal/Streaming/BidirectionalAsyncStream.swift index 2e3e9d8d..f2213799 100644 --- a/Libraries/Connect/Internal/Streaming/BidirectionalAsyncStream.swift +++ b/Libraries/Connect/Internal/Streaming/BidirectionalAsyncStream.swift @@ -17,6 +17,9 @@ import SwiftProtobuf /// Concrete implementation of `BidirectionalAsyncStreamInterface`. /// Provides the necessary wiring to bridge from closures/callbacks to Swift's `AsyncStream` /// to work with async/await. +/// +/// If the library removes callback support in favor of only supporting async/await in the future, +/// this class can be simplified. @available(iOS 13, *) class BidirectionalAsyncStream< Input: ProtobufMessage, Output: ProtobufMessage diff --git a/Libraries/Connect/Internal/Streaming/ClientOnlyAsyncStream.swift b/Libraries/Connect/Internal/Streaming/ClientOnlyAsyncStream.swift index 1abfa8bd..7a60a671 100644 --- a/Libraries/Connect/Internal/Streaming/ClientOnlyAsyncStream.swift +++ b/Libraries/Connect/Internal/Streaming/ClientOnlyAsyncStream.swift @@ -17,6 +17,9 @@ import Foundation /// Concrete implementation of `ClientOnlyAsyncStreamInterface`. /// Provides the necessary wiring to bridge from closures/callbacks to Swift's `AsyncStream` /// to work with async/await. +/// +/// This subclasses `BidirectionalAsyncStream` since its behavior is purely additive (it overlays +/// some additional validation) and both types are internal to the package, not public. @available(iOS 13, *) final class ClientOnlyAsyncStream< Input: ProtobufMessage, Output: ProtobufMessage @@ -34,6 +37,7 @@ final class ClientOnlyAsyncStream< } } +@available(iOS 13, *) extension ClientOnlyAsyncStream: ClientOnlyAsyncStreamInterface { func closeAndReceive() { self.close() diff --git a/Libraries/Connect/Internal/Streaming/ClientOnlyStream.swift b/Libraries/Connect/Internal/Streaming/ClientOnlyStream.swift index 029ec754..acdf051c 100644 --- a/Libraries/Connect/Internal/Streaming/ClientOnlyStream.swift +++ b/Libraries/Connect/Internal/Streaming/ClientOnlyStream.swift @@ -15,6 +15,9 @@ import SwiftProtobuf /// Concrete implementation of `ClientOnlyStreamInterface`. +/// +/// The complexity around configuring callbacks on this type is an artifact of the library +/// supporting both callbacks and async/await. This is internal to the package, and not public. final class ClientOnlyStream: @unchecked Sendable { private let onResult: @Sendable (StreamResult) -> Void private let receivedMessageCount = Locked(0) diff --git a/Libraries/Connect/Internal/Streaming/StreamResult+ClientOnlyStream.swift b/Libraries/Connect/Internal/Streaming/StreamResult+ClientOnlyStream.swift index 89a120f9..c0f7d2a0 100644 --- a/Libraries/Connect/Internal/Streaming/StreamResult+ClientOnlyStream.swift +++ b/Libraries/Connect/Internal/Streaming/StreamResult+ClientOnlyStream.swift @@ -23,26 +23,26 @@ extension StreamResult { /// - returns: The validated stream result, which may have been transformed into an error. func validatedForClientStream(receivedMessageCount: Int) -> Self { switch self { - case .complete(let code, _, _): - if code == .ok && receivedMessageCount < 1 { + case .headers: + return self + case .message: + if receivedMessageCount > 1 { return .complete( code: .internalError, error: ConnectError( - code: .unimplemented, message: "unary stream has no messages" + code: .unimplemented, message: "unary stream has multiple messages" ), trailers: nil ) } else { return self } - case .headers: - return self - case .message: - if receivedMessageCount > 1 { + case .complete(let code, _, _): + if code == .ok && receivedMessageCount < 1 { return .complete( code: .internalError, error: ConnectError( - code: .unimplemented, message: "unary stream has multiple messages" + code: .unimplemented, message: "unary stream has no messages" ), trailers: nil ) diff --git a/Libraries/ConnectNIO/Internal/GRPCInterceptor.swift b/Libraries/ConnectNIO/Internal/GRPCInterceptor.swift index 9f554f5d..8a534f24 100644 --- a/Libraries/ConnectNIO/Internal/GRPCInterceptor.swift +++ b/Libraries/ConnectNIO/Internal/GRPCInterceptor.swift @@ -125,23 +125,21 @@ extension GRPCInterceptor: UnaryInterceptor { ), tracingInfo: response.tracingInfo )) return + } else if Envelope.containsMultipleMessages(rawData) { + proceed(HTTPResponse( + code: .unimplemented, + headers: response.headers, + message: nil, + trailers: response.trailers, + error: ConnectError( + code: .unimplemented, message: "unary response has multiple messages" + ), + tracingInfo: response.tracingInfo + )) + return } do { - guard Envelope.containsMultipleMessages(rawData) == false else { - proceed(HTTPResponse( - code: .unimplemented, - headers: response.headers, - message: nil, - trailers: response.trailers, - error: ConnectError( - code: .unimplemented, message: "unary response has multiple messages" - ), - tracingInfo: response.tracingInfo - )) - return - } - let messageData = try Envelope.unpackMessage( rawData, compressionPool: compressionPool ).unpacked