Make HTTP API calls easier. Built on top of URLSession.
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:
- SimpleHTTPwhich bring the full framework API described in this README
- SimpleHTTPFoundationwhich only bring a few addition to Foundation API. See this article or API doc to have a glimpse of what is provided.
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" path by sending a UserBody and expecting a UserResponse as response.
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:
- baseURLwill be prepended to all call paths
- You can skip encoder and decoder if you use JSON
- You can provide a custom URLSessioninstance if ever needed
Request support two body types:
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)
  }
}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)
  }
}You can declare constant paths if needed (refer to Path documentation to see more):
extension Path {
  static let login: Path = "login"
}
extension Request {
  static let func login(_ body: UserBody) -> Request<UserResponse> {
    .post(.login, body: body)
  }
}When using Session you can add automatic behavior to your requests/responses using Interceptor like authentication, logging, request retrying, etc...
RequestInterceptor allows to adapt and/or retry a request:
- adaptRequestmethod is called before making a request allowing you to transform it adding headers, changing path, ...
- rescueRequestErroris 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 is dedicated to intercept and server responses:
- adaptResponsechange the server output
- receivedResponsenotify about the server final response (a valid output or error)