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 READMESimpleHTTPFoundationwhich 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")!) try await session.response(for: .login(UserBody(username: "pjechris", password: "MyPassword")))
A few words about Session:
baseURLwill be prepended to all call paths- By default JSON is used for both encoding and decoding, so you don't have to configure anything for JSON APIs
- You can provide a custom
URLSessioninstance if ever needed
Encoders and decoders are configured per content type through a SessionConfiguration. Pass a ContentDataCodersConfiguration to register the coders you need:
let session = Session( baseURL: URL(string: "https://github.com")!, configuration: SessionConfiguration( data: ContentDataCodersConfiguration() .encoding(.json, with: JSONEncoder()) .decoding(.json, with: JSONDecoder()) ) )
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() 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") 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 outputreceivedResponsenotify about the server final response (a valid output or error)