ComposableRequest

public struct ComposableRequest<RequestBody, ResponseBody, ResponseError> : Requestable where ResponseError : Error

A ComposableRequest is a generic type used to execute HTTP requests without needing to create request-specific Requestable types.

Basic usage might look like this:

cancellable = ComposableRequest<AnyJSON, AnyJSON, HTTPError>()
    .path("/users")
    .method(.post)
    .headers([.contentType: .contentType(.json)])
    .body(
        [
            "name": "morpheus",
            "job": "leader"
        ]
    )
    .send(on: "https://reqres.in/api")
    .sink { result in
        // handle completion
    } receiveValue: { response in
        // handle response
    }

You can specialize a ComposableRequest with body types, as well as an error model of your choosing. If you use body types String, Data, Codable, RequestParameters or AnyJSON, default encoder and decoders are provided for you. If you use an error type HTTPError, a response validator is used for you. If you provide your own error model, rember to provide a validator, as the default validator automatically allows all responses to continue

Initializers

  • Create a ComposableRequest

    Declaration

    Swift

    public init()

API

  • Set the request path

    let request = ComposableRequest<Any, Any, Error>()
        .path("/posts/create")
    

    Declaration

    Swift

    public func path(_ path: String) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    path

    The path

    Return Value

    The request

  • Set the request path

    let request = ComposableRequest<Any, Any, Error>()
        .path {
            // ... logic to determine the request path ...
            return path
        }
    

    Declaration

    Swift

    public func path(_ pathBuilder: @escaping () -> String) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    pathBuilder

    The closure that builds the request path

    Return Value

    The request

  • Add a URL query

    let request = ComposableRequest<Any, Any, Error>()
        .query(URLQueryItem(name: "key", value: "value"))
    

    Declaration

    Swift

    public func query(_ query: URLQueryItem) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    query

    The query parameter

    Return Value

    The request

  • Add a URL query

    let request = ComposableRequest<Any, Any, Error>()
        .query {
            // ... logic to determine the request query ...
            return URLQueryItem(name: "key", value: "value")
        }
    

    Declaration

    Swift

    public func query(_ queryBuilder: @escaping () -> URLQueryItem) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    queryBuilder

    The closure that returns the URL query

    Return Value

    The request

  • Add URL queries

    let request = ComposableRequest<Any, Any, Error>()
        .query(URLQueryItem(name: "key1", value: "value1"),
               URLQueryItem(name: "key2", value: "value2"))
    

    Declaration

    Swift

    public func query(_ queries: URLQueryItem...) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    queries

    Query items

    Return Value

    The request

  • Replace the exsting URL queries with new ones

    let request = ComposableRequest<Any, Any, Error>()
        .queries([URLQueryItem(name: "key1", value: "value1")
                  URLQueryItem(name: "key2", value: "value2")])
    

    Declaration

    Swift

    public func queries(_ query: [URLQueryItem]) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    queries

    The queries

    Return Value

    The request

  • Replace the existing URL queries with new ones

    let request = ComposableRequest<Any, Any, Error>()
        .queries {
            // ... logic to determine the request queries ...
            return [URLQueryItem(name: "key1", value: "value1")
                    URLQueryItem(name: "key2", value: "value2")]
        }
    

    Declaration

    Swift

    public func queries(_ queryBuilder: @escaping () -> [URLQueryItem]) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    queryBuilder

    The closure that builds the queries

    Return Value

    The request

  • Set the HTTP method

    let request = ComposableRequest<Any, Any, Error>()
        .method(.post)
    

    Declaration

    Swift

    public func method(_ method: RequestMethod) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    method

    The method to use

    Return Value

    The request

  • Set the HTTP method

    let request = ComposableRequest<Any, Any, Error>()
        .method {
            ... logic to determine the request method ...
            return .post
        }
    

    Declaration

    Swift

    public func method(_ methodBuilder: @escaping () -> RequestMethod) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    methodBuilder

    Closure that builds the request method

    Return Value

    The request

  • Add a header

    let request = ComposableRequest<Any, Any, Error>()
        .header(key: "User-Agent", value: "MyUserAgentValue")
    

    Declaration

    Swift

    public func header(key: RequestHeaders.Key, value: RequestHeaders.Value?) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    key

    The header field

    value

    The header value

    Return Value

    The request

  • Add a header

    let request = ComposableRequest<Any, Any, Error>()
        .header {
            /// ... logic to determine header ...
            return (key: "User-Agent", value: "MyUserAgentValue")
        }
    

    Declaration

    Swift

    public func header(_ headerBuilder: @escaping () -> (RequestHeaders.Key, RequestHeaders.Value?)) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    headerBuilder

    The closure that builds the header

    Return Value

    The request

  • Add headers

    let request = ComposableRequest<Any, Any, Error>()
        .headers(pair1, pair2, pair3)
    

    Declaration

    Swift

    public func headers(_ pairs: (key: RequestHeaders.Key, value: RequestHeaders.Value)...) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    pairs

    The pairs of headers to add

    Return Value

    The request

  • Add headers

    let request = ComposableRequest<Any, Any, Error>()
        .headers([pair1, pair2, pair3])
    

    Declaration

    Swift

    public func headers(_ pairs: [(key: RequestHeaders.Key, value: RequestHeaders.Value)]) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    pairs

    The pairs of headers to add

    Return Value

    The request

  • Replace the existing headers with new ones

    let request = ComposableRequest<Any, Any, Error>()
        .headers(["User-Agent" : "MyAgent",
                  "ContentType": "application/json"])
    

    Declaration

    Swift

    public func headers(_ headers: RequestHeaders) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    headers

    The new headers

    Return Value

    The request

  • Replace the existing headers with new ones

    let request = ComposableRequest<Any, Any, Error>()
        .headers {
            /// ... logic to determine header ...
            return ["User-Agent" : "MyAgent",
                    "ContentType": "application/json"]
        }
    

    Declaration

    Swift

    public func headers(_ headersBuilder: @escaping () -> RequestHeaders) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    headersBuilder

    The closure that builds the headers

    Return Value

    The request

  • Set the request body

    let request = ComposableRequest<Any, Any, Error>()
        .body(myBody)
    

    Declaration

    Swift

    public func body(_ body: RequestBody?) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    body

    The body

    Return Value

    The request

  • Set the request body

    let request = ComposableRequest<Any, Any, Error>()
        .body {
            // ... logic to determine body ...
            return myBody
        }
    

    Declaration

    Swift

    public func body(_ bodyBuilder: @escaping () -> RequestBody?) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    bodyBuilder

    The closure that builds the body

    Return Value

    The request

  • Set the request authentication

    let request = ComposableRequest<Any, Any, Error>()
        .authentication(.token(.bearer, "myToken"))
    

    Declaration

    Swift

    public func authenticate(with authentication: RequestAuthentication?) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    authentication

    The authentication

    Return Value

    The request

  • Set the request authentication

    let request = ComposableRequest<Any, Any, Error>()
        .authentication {
            /// ... logic to determine authentication ...
            return .token(.bearer, "myToken"))
        }
    

    Declaration

    Swift

    public func authenticate(with authenticationBuilder: @escaping () -> RequestAuthentication?) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    authenticationBuilder

    The closure that builds the authentication

    Return Value

    The request

  • Set the request authentication using basic username and password credentials

    let request = ComposableRequest<Any, Any, Error>
        .authentication(withUsername: "myUserName", password: "myPassword")
    

    Declaration

    Swift

    public func authenticate(withUsername username: String, password: String) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    username

    Username

    password

    Password

    Return Value

    The request

  • Set the request authentication using an authorization token

    let request = ComposableRequest<Any, Any, Error>
        .authentication(withToken: "myToken", type: .bearer)
    

    Declaration

    Swift

    public func authenticate(withToken token: String, type: RequestAuthentication.TokenType) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    token

    The token

    type

    The type of token

    Return Value

    the request

  • Add a fallback response to the request

    Declaration

    Swift

    public func fallbackResponse(_ fallbackResponse: RequestResponse<ResponseBody>?) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    fallbackResponse

    The response to use if the request fails

    Return Value

    The request

  • Add a fallback response to the request

    Declaration

    Swift

    public func fallbackResponse(_ fallbackResponseBuilder: @escaping () -> RequestResponse<ResponseBody>?) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    fallbackResponse

    Closure that builds the fallback response, used if the request fails

    Return Value

    The request

  • Add a request body encoder

    Declaration

    Swift

    public func encodeBody(with handler: @escaping (RequestBody?) throws -> Data?) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    handler

    Closure used to transform RequestBody into Data

    Return Value

    The request

  • Add a request body encoder

    Declaration

    Swift

    public func encodeBody(with encoder: BodyEncoder<RequestBody>) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    encoder

    Encoder used to handle request

    Return Value

    The request

  • Add a response body decoder

    Declaration

    Swift

    public func decodeBody(with handler: @escaping (Data?) throws -> ResponseBody?) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    handler

    Closure used to transform ResponseBody into Data

    Return Value

    The request

  • Add a response body decoder

    Declaration

    Swift

    public func decodeBody(with decoder: BodyDecoder<ResponseBody>) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    decoder

    Decoder used to handle response

    Return Value

    The request

  • Add a response validator

    Declaration

    Swift

    public func validateResponse(with handler: @escaping (Response) -> Result<Response, ResponseError>) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    handler

    Closure used to validate a Response

    Return Value

    The request

  • Add a response validator

    Declaration

    Swift

    public func validateResponse(with responseValidator: ResponseValidator<ResponseBody, ResponseError>) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    responseValidator

    Response validator

    Return Value

    The request

  • Add a timeout interval

    Declaration

    Swift

    public func timeoutInterval(_ interval: TimeInterval) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    interval

    The interval for requests to timeout

    Return Value

    The request

  • Add a timeout interval

    Declaration

    Swift

    public func timeoutInterval(_ intervalBuilder: @escaping () -> TimeInterval) -> ComposableRequest<RequestBody, ResponseBody, ResponseError>

    Parameters

    intervalBuilder

    Closure to build the timeout interval

    Return Value

    The request

  • Send this request on the main thread

    Declaration

    Swift

    public func send(on host: String,
                     retries: Int = 0,
                     sla: TimeInterval = 120) -> AnyPublisher<Response, Failure>

    Parameters

    host

    The host

    retries

    The number of retries

    sla

    The SLA to use before timing out

    Return Value

    A publisher to observe request responses

  • Send this request

    Declaration

    Swift

    public func send<S>(on host: String,
                        retries: Int = 0,
                        sla: S.SchedulerTimeType.Stride = .seconds(120),
                        using scheduler: S) -> AnyPublisher<Response, Failure> where S: Scheduler

    Parameters

    host

    The host

    retries

    The number of retries

    sla

    The SLA to use before timing out

    scheduler

    The scheduler to use

    Return Value

    A publisher to observe request responses

Requestable