Skip to content

Commit 3d7c5b4

Browse files
committed
[readme] update README
1 parent fb919ea commit 3d7c5b4

File tree

1 file changed

+86
-44
lines changed

1 file changed

+86
-44
lines changed

README.md

Lines changed: 86 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,65 @@
33
![swift](https://img.shields.io/badge/Swift-5.5%2B-orange?logo=swift&logoColor=white)
44
![platforms](https://img.shields.io/badge/Platforms-iOS%20%7C%20macOS-lightgrey)
55
![tests](https://github.com/pjechris/SimpleHTTP/actions/workflows/test.yml/badge.svg)
6+
67
[![twitter](https://img.shields.io/badge/twitter-pjechris-1DA1F2?logo=twitter&logoColor=white)](https://twitter.com/pjechris)
8+
[![doc](https://img.shields.io/badge/read%20the%20doc-8CA1AF?logo=readthedocs&logoColor=white)](https://pjechris.github.io/SimpleHTTP/)
79

8-
Simple declarative HTTP API framework
10+
Make HTTP API calls easier. Built on top of URLSession.
911

10-
## Basic Usage
12+
## Installation
1113

12-
### Building a request
13-
First step is to build a request. You make requests by providing extension on top of `Request` type:
14+
Use Swift Package Manager to install the library:
1415

1516
```swift
16-
extension Request {
17-
static let func login(_ body: UserBody) -> Self where Output == UserResponse {
18-
.post("login", body: body)
19-
}
20-
}
17+
dependencies: [
18+
.package(url: "https://github.com/pjechris/SimpleHTTP", from: "0.4.0"),
19+
]
2120
```
2221

23-
And... voila! We defined a `login(_:)` request which will request login endpoint by sending a `UserBody` and waiting for a `UserResponse`. Now it's time to use it.
22+
The package come with 2 modules:
2423

25-
You can declare constant endpoints if needed (refer to Endpoint documentation to see more):
24+
- `SimpleHTTP` which bring the full framework API described in this README
25+
- `SimpleHTTPFoundation` which only bring a few addition to Foundation API. See this [article](https://swiftunwrap.com/article/designing-http-framework-foundation/) or [API doc](https://pjechris.github.io/SimpleHTTP/) to have a glimpse of what is provided.
2626

27-
```swift
28-
extension Endpoint {
29-
static let login: Endpoint = "login"
30-
}
27+
## Basic Usage
3128

29+
### Building a request
30+
31+
You make requests by creating `Request` objects. You can either create them manually or provide static definition by extending `Request`:
32+
33+
```swift
3234
extension Request {
33-
static let func login(_ body: UserBody) -> Self where Output == UserResponse {
34-
.post(.login, body: body)
35+
static let func login(_ body: UserBody) -> Request<UserResponse> {
36+
.post("login", body: body)
3537
}
3638
}
3739
```
3840

41+
This defines a `Request.login(_:)` method which create a request targeting "login" endpoint by sending a `UserBody` and expecting a `UserResponse` as response.
42+
3943
### Sending a request
4044

41-
To send a request use a `Session` instance. `Session` is somewhat similar to `URLSession` but providing additional functionalities.
45+
You can use your request along `URLSession` by converting it into a `URLRequest` by calling `request.toURLRequest(encoder:relativeTo:accepting)`.
46+
47+
You can also use a `Session` object. `Session` is somewhat similar to `URLSession` but providing additional functionalities:
48+
49+
- encoder/decoder for all requests
50+
- error handling
51+
- ability to [intercept](#interceptor) requests
4252

4353
```swift
4454

45-
let session = Session(baseURL: URL(string: "https://github.com")!, encoder: JSONEncoder(), decoder: JSONDecoder())
55+
let session = Session(
56+
baseURL: URL(string: "https://github.com")!,
57+
encoder: JSONEncoder(),
58+
decoder: JSONDecoder()
59+
)
4660

47-
session.publisher(for: .login(UserBody(username: "pjechris", password: "MyPassword")))
61+
try await session.response(for: .login(UserBody(username: "pjechris", password: "MyPassword")))
4862

4963
```
5064

51-
You can now use the returned publisher however you want. Its result is similar to what you have received with `URLSession.shared.dataTaskPublisher(for: ...).decode(type: UserResponse.self, decoder: JSONDecoder())`.
52-
5365
A few words about Session:
5466

5567
- `baseURL` will be prepended to all call endpoints
@@ -58,69 +70,99 @@ A few words about Session:
5870

5971
## Send a body
6072

73+
Request support two body types:
74+
75+
- [Encodable](#encodable)
76+
- [Multipart](#multipart)
77+
6178
### Encodable
6279

63-
You will build your request by sending your `body` to construct it:
80+
To send an Encodable object just set it as your Request body:
6481

6582
```swift
6683
struct UserBody: Encodable {}
6784

6885
extension Request {
69-
static func login(_ body: UserBody) -> Self where Output == LoginResponse {
86+
static func login(_ body: UserBody) -> Request<LoginResponse> {
7087
.post("login", body: body)
7188
}
7289
}
7390
```
7491

75-
We defined a `login(_:)` request which will request login endpoint by sending a `UserBody` and waiting for a `LoginResponse`
76-
7792
### Multipart
7893

79-
You we build 2 requests:
94+
You can create multipart content from two kind of content
95+
96+
- From a disk file (using a `URL`)
97+
- From raw content (using `Data`)
8098

81-
- send `URL`
82-
- send a `Data`
99+
First example show how to create a request sending an audio file as request body:
83100

84101
```swift
85102
extension Request {
86-
static func send(audio: URL) throws -> Self where Output == SendAudioResponse {
103+
static func send(audioFile: URL) throws -> Request<SendAudioResponse> {
87104
var multipart = MultipartFormData()
88-
try multipart.add(url: audio, name: "define_your_name")
89-
return .post("sendAudio", body: multipart)
105+
106+
try multipart.add(url: audioFile, name: "define_your_name")
107+
108+
return .post("v1/sendAudio", body: multipart)
90109
}
110+
}
111+
```
112+
113+
Second example show same request but this time audio file is just some raw unknown data:
91114

92-
static func send(audio: Data) throws -> Self where Output == SendAudioResponse {
115+
```swift
116+
static func send(audioFile: Data) throws -> Request<SendAudioResponse> {
93117
var multipart = MultipartFormData()
94-
try multipart.add(data: data, name: "your_name", fileName: "your_fileName", mimeType: "right_mimeType")
95-
return .post("sendAudio", body: multipart)
118+
119+
try multipart.add(data: audioFile, name: "your_name", mimeType: "audioFile_mimeType")
120+
121+
return .post("v1/sendAudio", body: multipart)
96122
}
97123
}
98124
```
99125

100-
We defined the 2 `send(audio:)` requests which will request `sendAudio` endpoint by sending an `URL` or a `Data` and waiting for a `SendAudioResponse`
101-
102-
We can add multiple `Data`/`URL` to the multipart
126+
Note you can add multiple contents inside a `MultipartFormData`. For instance here we send both a audio file and an image:
103127

104128
```swift
105129
extension Request {
106-
static func send(audio: URL, image: Data) throws -> Self where Output == SendAudioImageResponse {
130+
static func send(audio: URL, image: Data) throws -> Request<SendAudioImageResponse> {
107131
var multipart = MultipartFormData()
132+
108133
try multipart.add(url: audio, name: "define_your_name")
109-
try multipart.add(data: image, name: "your_name", fileName: "your_fileName", mimeType: "right_mimeType")
110-
return .post("sendAudioImage", body: multipart)
134+
try multipart.add(data: image, name: "your_name", mimeType: "image_mimeType")
135+
136+
return .post("v1/send", body: multipart)
137+
}
138+
}
139+
```
140+
141+
## Constant endpoints
142+
143+
You can declare constant endpoints if needed (refer to Endpoint documentation to see more):
144+
145+
```swift
146+
extension Endpoint {
147+
static let login: Endpoint = "login"
148+
}
149+
150+
extension Request {
151+
static let func login(_ body: UserBody) -> Request<UserResponse> {
152+
.post(.login, body: body)
111153
}
112154
}
113155
```
114156

115157
## Interceptor
116158

117-
Protocol `Interceptor` enable powerful request interceptions. This include authentication, logging, request retrying, etc...
159+
When using Session you can add automatic behavior to your requests/responses using `Interceptor` like authentication, logging, request retrying, etc...
118160

119161
### `RequestInterceptor`
120162

121-
`RequestInterceptor` allow to adapt a or retry a request whenever it failed:
163+
`RequestInterceptor` allows to adapt and/or retry a request:
122164

123-
- `adaptRequest` method is called before making a request and allow you to transform it adding headers, changing path, ...
165+
- `adaptRequest` method is called before making a request allowing you to transform it adding headers, changing path, ...
124166
- `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
125167

126168
### `ResponseInterceptor`

0 commit comments

Comments
 (0)