diff --git a/mobile/examples/swift/hello_world/AppDelegate.swift b/mobile/examples/swift/hello_world/AppDelegate.swift index 6b0f9da8a90c..14c5ec6a10b4 100644 --- a/mobile/examples/swift/hello_world/AppDelegate.swift +++ b/mobile/examples/swift/hello_world/AppDelegate.swift @@ -1,7 +1,7 @@ import Envoy import UIKit -private enum ConfigLoadError: Error { +private enum ConfigLoadError: Swift.Error { case noFileAtPath } diff --git a/mobile/library/swift/src/BUILD b/mobile/library/swift/src/BUILD index 6d9f7655fe54..573233318d01 100644 --- a/mobile/library/swift/src/BUILD +++ b/mobile/library/swift/src/BUILD @@ -5,12 +5,16 @@ load("//bazel:swift_static_framework.bzl", "swift_static_framework") swift_static_framework( name = "ios_framework", srcs = [ + "Client.swift", "Envoy.swift", + "Error.swift", "LogLevel.swift", "Request.swift", "RequestBuilder.swift", "RequestMethod.swift", + "ResponseHandler.swift", "RetryPolicy.swift", + "StreamEmitter.swift", ], module_name = "Envoy", objc_includes = ["//library/objective-c:EnvoyEngine.h"], diff --git a/mobile/library/swift/src/Client.swift b/mobile/library/swift/src/Client.swift new file mode 100644 index 000000000000..65e4e986fb50 --- /dev/null +++ b/mobile/library/swift/src/Client.swift @@ -0,0 +1,12 @@ +/// Client that is able to send and receive requests through Envoy. +@objc +public protocol Client { + /// Start a new stream. + /// + /// - parameter request: The request for opening a stream. + /// - parameter handler: Handler for receiving stream events. + /// + /// - returns: Emitter for sending streaming data outward. + func startStream(request: Request, handler: ResponseHandler) + -> StreamEmitter +} diff --git a/mobile/library/swift/src/Error.swift b/mobile/library/swift/src/Error.swift new file mode 100644 index 000000000000..d56f2c9e1541 --- /dev/null +++ b/mobile/library/swift/src/Error.swift @@ -0,0 +1,17 @@ +import Foundation + +@objcMembers +public final class Error: NSObject, Swift.Error { + /// Error code associated with the exception that occurred. + public let errorCode: Int + /// A description of what exception that occurred. + public let message: String + /// Optional cause for the error. + public let cause: Swift.Error? + + init(errorCode: Int, message: String, cause: Swift.Error?) { + self.errorCode = errorCode + self.message = message + self.cause = cause + } +} diff --git a/mobile/library/swift/src/ResponseHandler.swift b/mobile/library/swift/src/ResponseHandler.swift new file mode 100644 index 000000000000..bd3a8a01d7ec --- /dev/null +++ b/mobile/library/swift/src/ResponseHandler.swift @@ -0,0 +1,39 @@ +import Foundation + +/// Callback interface for receiving stream events. +@objc +public protocol ResponseHandler { + /// Called when response headers are received by the stream. + /// + /// - parameter headers: The headers of the response. + /// - parameter statusCode: The status code of the response. + func onHeaders(_ headers: [String: [String]], statusCode: Int) + + /// Called when a data frame is received by the stream. + /// + /// - parameter data: Bytes in the response. + /// - parameter endStream: True if the stream is complete. + func onData(_ data: Data, endStream: Bool) + + /// Called when response metadata is received by the stream. + /// + /// - parameter metadata: The metadata of the response. + /// - parameter endStream: True if the stream is complete. + func onMetadata(_ metadata: [String: [String]], endStream: Bool) + + /// Called when response trailers are received by the stream. + /// + /// - parameter trailers: The trailers of the response. + func onTrailers(_ trailers: [String: [String]]) + + /// Called when an internal Envoy exception occurs with the stream. + /// + /// - parameter error: The error that occurred with the stream. + func onError(_ error: Error) + + /// Called when the stream is canceled. + func onCanceled() + + /// Called when the stream has been completed. + func onCompletion() +} diff --git a/mobile/library/swift/src/StreamEmitter.swift b/mobile/library/swift/src/StreamEmitter.swift new file mode 100644 index 000000000000..0f99baf9a00d --- /dev/null +++ b/mobile/library/swift/src/StreamEmitter.swift @@ -0,0 +1,50 @@ +import Foundation + +/// Interface allowing for sending/emitting data on an Envoy stream. +@objc +public protocol StreamEmitter { + /// Returns whether the emitter is still active and can + /// perform communications with the associated stream. + /// + /// - returns: True if the stream is active. + func isActive() -> Bool + + /// Send data over the associated stream. + /// + /// - parameter data: Data to send over the stream. + /// + /// - throws: `Envoy.Error` when the stream is inactive or data can't be sent. + /// + /// - returns: The stream emitter, for chaining syntax. + func sendData(_ data: Data) throws -> StreamEmitter + + /// Sent metadata over the associated stream. + /// + /// - parameter metadata: Metadata to send over the stream. + /// + /// - throws: `Envoy.Error` when the stream is inactive or data can't be sent. + /// + /// - returns: The stream emitter, for chaining syntax. + func sendMetadata(_ metadata: [String: [String]]) throws -> StreamEmitter + + /// End the stream after sending any provided trailers. + /// + /// - parameter trailers: Trailers to send over the stream. + /// + /// - throws: `Envoy.Error` when the stream is inactive or data can't be sent. + func close(trailers: [String: [String]]) throws + + /// Cancel and end the associated stream. + /// + /// - throws: `Envoy.Error` when the stream is inactive or data can't be sent. + func cancel() throws +} + +extension StreamEmitter { + /// Convenience function for ending the stream without sending any trailers. + /// + /// - throws: `Envoy.Error` when the stream is inactive or data can't be sent. + public func close() throws { + try self.close(trailers: [:]) + } +}