SimpleHTTP

0.4.0

Lightweight HTTP framework adding functionalites to URLSession.
pjechris/SimpleHTTP

What's New

0.4.0

2022-09-24T14:23:15Z

What's Changed

  • tech(package): Introduce a foundation package by @pjechris in #10
  • feat(request): [#11] Introduce Endpoint instead of Path by @pjechris in #13
  • feat(request): Add new APIs to Request by @pjechris in #14

Full Changelog: 0.3.0...0.4.0

SimpleHTTP

swift platforms tests

twitter doc

Make HTTP API calls easier. Built on top of URLSession.

Installation

Use Swift Package Manager to install the library:

dependencies: [
  .package(url: "https://github.com/pjechris/SimpleHTTP", from: "0.4.0"),
]

The package come with 2 modules:

  • SimpleHTTP which bring the full framework API described in this README
  • SimpleHTTPFoundation which only bring a few addition to Foundation API. See this article or API doc to have a glimpse of what is provided.

Basic Usage

Building a request

You make requests by creating Request objects. You can either create them manually or provide static definition by extending Request:

extension Request {
  static let func login(_ body: UserBody) -> Request<UserResponse> {
    .post("login", body: body)
  }
}

This defines a Request.login(_:) method which create a request targeting "login" endpoint by sending a UserBody and expecting a UserResponse as response.

Sending a request

You can use your request along URLSession by converting it into a URLRequest by calling request.toURLRequest(encoder:relativeTo:accepting).

You can also use a Session object. Session is somewhat similar to URLSession but providing additional functionalities:

  • encoder/decoder for all requests
  • error handling
  • ability to intercept requests
let session = Session(
  baseURL: URL(string: "https://github.com")!,
  encoder: JSONEncoder(),
  decoder: JSONDecoder()
)

try await session.response(for: .login(UserBody(username: "pjechris", password: "MyPassword")))

A few words about Session:

  • baseURL will be prepended to all call endpoints
  • You can skip encoder and decoder if you use JSON
  • You can provide a custom URLSession instance if ever needed

Send a body

Request support two body types:

Encodable

To send an Encodable object just set it as your Request body:

struct UserBody: Encodable {}

extension Request {
  static func login(_ body: UserBody) -> Request<LoginResponse> {
    .post("login", body: body)
  }
}

Multipart

You can create multipart content from two kind of content

  • From a disk file (using a URL)
  • From raw content (using Data)

First example show how to create a request sending an audio file as request body:

extension Request {
  static func send(audioFile: URL) throws -> Request<SendAudioResponse> {
    var multipart = MultipartFormData()

    try multipart.add(url: audioFile, name: "define_your_name")

    return .post("v1/sendAudio", body: multipart)
  }
}

Second example show same request but this time audio file is just some raw unknown data:

  static func send(audioFile: Data) throws -> Request<SendAudioResponse> {
    var multipart = MultipartFormData()

    try multipart.add(data: audioFile, name: "your_name", mimeType: "audioFile_mimeType")

    return .post("v1/sendAudio", body: multipart)
  }
}

Note you can add multiple contents inside a MultipartFormData. For instance here we send both a audio file and an image:

extension Request {
  static func send(audio: URL, image: Data) throws -> Request<SendAudioImageResponse> {
    var multipart = MultipartFormData()

    try multipart.add(url: audio, name: "define_your_name")
    try multipart.add(data: image, name: "your_name", mimeType: "image_mimeType")

    return .post("v1/send", body: multipart)
  }
}

Constant endpoints

You can declare constant endpoints if needed (refer to Endpoint documentation to see more):

extension Endpoint {
  static let login: Endpoint = "login"
}

extension Request {
  static let func login(_ body: UserBody) -> Request<UserResponse> {
    .post(.login, body: body)
  }
}

Interceptor

When using Session you can add automatic behavior to your requests/responses using Interceptor like authentication, logging, request retrying, etc...

RequestInterceptor

RequestInterceptor allows to adapt and/or retry a request:

  • adaptRequest method is called before making a request allowing you to transform it adding headers, changing path, ...
  • rescueRequestError is called whenever the request fail. You'll have a chance to retry the request. This can be used to re-authenticate the user for instance

ResponseInterceptor

ResponseInterceptor is dedicated to intercept and server responses:

  • adaptResponse change the server output
  • receivedResponse notify about the server final response (a valid output or error)

Description

  • Swift Tools 5.5.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sat Nov 26 2022 21:42:55 GMT-0500 (GMT-05:00)