[ad_1]
Introducing SwiftHttp
An superior Swift HTTP library to quickly create communication layers with API endpoints. The library tries to separate the shopper request logic from the request constructing and response dealing with. That is the principle purpose why it has a HttpClient protocol which can be utilized to carry out information, obtain and add duties. You possibly can implement your personal HttpClient, however SwiftHttp comes with a built-in UrlSessionHttpClient based mostly on Basis networking.
So the shopper is accountable for executing the requests, however we nonetheless have to explain the request itself in some way. That is the place the HttpRawRequest object comes into play. You possibly can simply create a base HttpUrl and carry out a request utilizing the HttpRawRequest object. When working with a uncooked request you possibly can specify extra header fields and a uncooked physique information object too. 💪
let url = HttpUrl(scheme: "https",
host: "jsonplaceholder.typicode.com",
port: 80,
path: ["todos"],
useful resource: nil,
question: [:],
fragment: nil)
let req = HttpRawRequest(url: url, methodology: .get, headers: [:], physique: nil)
let shopper = UrlSessionHttpClient(session: .shared, log: true)
let response = attempt await shopper.dataTask(req)
let todos = attempt JSONDecoder().decode([Todo].self, from: response.information)
The HTTP shopper can carry out community calls utilizing the brand new async / await Swift concurrency API. It’s attainable to cancel a community request by wrapping it right into a structured concurrency Activity.
let process = Activity {
let api = TodoApi()
_ = attempt await api.listing()
}
DispatchQueue.world().asyncAfter(deadline: .now() + .milliseconds(10)) {
process.cancel()
}
do {
let _ = attempt await process.worth
}
catch {
if (error as? URLError)?.code == .cancelled {
print("cancelled")
}
}
It is a neat tick, you may as well examine the explanation contained in the catch block, whether it is an URLError with a .cancelled code then the request was cancelled, in any other case it should be some type of community error.
So that is how you need to use the shopper to carry out or cancel a community process, however normally you do not wish to work with uncooked information, however encodable and decodable objects. Once you work with such objects, you may wish to validate the response headers and ship extra headers to tell the server about the kind of the physique information. Simply take into consideration the Content material-Kind / Settle for header fields. 🤔
So we’d wish to ship extra headers alongside the request, plus it would be good to validate the standing code and response headers earlier than we attempt to parse the information. This looks like a circulate of widespread operations, first we encode the information, set the extra header fields, and when the response arrives we validate the standing code and the header fields, lastly we attempt to decode the information object. It is a typical use case and SwiftHttp calls this workflow as a pipeline.
There are 4 kinds of built-in HTTP pipelines:
- Uncooked – Ship a uncooked information request, return a uncooked information response
- Encodable – Ship an encodable object, return a uncooked information response
- Decodable – Ship a uncooked information request, return a decodable object
- Codable – Ship an encodable object, return a decodable object
We are able to use a HttpRawPipeline and execute our request utilizing a shopper as an executor.
let baseUrl = HttpUrl(host: "jsonplaceholder.typicode.com")
let shopper = UrlSessionHttpClient(session: .shared, log: true)
let pipeline = HttpRawPipeline(url: baseUrl.path("todos"), methodology: .get)
let response = attempt await pipeline.execute(shopper.dataTask)
let todos = attempt JSONDecoder().decode([Todo].self, from: response.information)
print(response.statusCode)
print(todos.rely)
On this case we had been utilizing the dataTask perform, however should you anticipate the response to be an enormous file, you may wish to think about using a downloadTask, or should you’re importing a considerable amount of information when sending the request, it’s best to select the uploadTask perform. 💡
So on this case we needed to manually decode the Todo object from the uncooked HTTP response information, however we will use the decodable pipeline to make issues much more easy.
let baseUrl = HttpUrl(host: "jsonplaceholder.typicode.com")
let shopper = UrlSessionHttpClient(session: .shared, log: true)
let pipeline = HttpDecodablePipeline<[Todo]>(url: baseUrl.path("todos"),
methodology: .get,
decoder: .json(JSONDecoder(), validators: [
HttpStatusCodeValidator(.ok),
HttpHeaderValidator(.key(.contentType)) {
$0.contains("application/json")
},
]))
let todos = attempt await pipeline.execute(shopper.dataTask)
print(todos.rely)
As you possibly can see, on this case the as an alternative of returning the response, the pipeline can carry out extra validation and the decoding utilizing the offered decoder and validators. You possibly can create your personal validators, there’s a HttpResponseValidator protocol for this objective.
The encodable pipeline works like the identical, you possibly can specify the encoder, you possibly can present the encodable object and you will get again a HttpResponse occasion.
let shopper = UrlSessionHttpClient(session: .shared, log: true)
let todo = Todo(id: 1, title: "lorem ipsum", accomplished: false)
let pipeline = HttpEncodablePipeline(url: baseUrl.path("todos"),
methodology: .put up,
physique: todo,
encoder: .json())
let response = attempt await pipeline.execute(shopper.dataTask)
print(response.statusCode == .created)
The codable pipeline is a mixture of the encodable and decodable pipeline. 🙃
let baseUrl = HttpUrl(host: "jsonplaceholder.typicode.com")
let shopper = UrlSessionHttpClient(session: .shared, log: true)
let todo = Todo(id: 1, title: "lorem ipsum", accomplished: false)
let pipeline = HttpCodablePipeline<Todo, Todo>(url: baseUrl.path("todos", String(1)),
methodology: .put,
physique: todo,
encoder: .json(),
decoder: .json())
let todo = attempt await pipeline.execute(shopper.dataTask)
print(todo.title)
As you possibly can see that is fairly a typical sample, and once we’re speaking with a REST API, we’ll carry out roughly the very same community calls for each single endpoint. SwiftHttp has a pipeline assortment protocol that you need to use to carry out requests with out the necessity of explicitly organising these pipelines. This is an instance:
import SwiftHttp
struct Todo: Codable {
let id: Int
let title: String
let accomplished: Bool
}
struct TodoApi: HttpCodablePipelineCollection {
let shopper: HttpClient = UrlSessionHttpClient(log: true)
let apiBaseUrl = HttpUrl(host: "jsonplaceholder.typicode.com")
func listing() async throws -> [Todo] {
attempt await decodableRequest(executor: shopper.dataTask,
url: apiBaseUrl.path("todos"),
methodology: .get)
}
}
let todos = attempt await api.listing()
When utilizing a HttpCodablePipelineCollection you possibly can carry out an encodable, decodable or codable request utilizing an executor object. This can scale back the boilerplate code wanted to carry out a request and every little thing goes to be kind secure due to the generic protocol oriented networking layer. You possibly can setup as many pipeline collections as you want, it’s attainable to make use of a shared shopper or you possibly can create a devoted shopper for every.
By the best way, if one thing goes fallacious with the request, or one of many validators fail, you possibly can all the time examine for the errors utilizing a do-try-catch block. 😅
do {
_ = attempt await api.listing()
}
catch HttpError.invalidStatusCode(let res) {
let decoder = HttpResponseDecoder<CustomError>(decoder: JSONDecoder())
do {
let error = attempt decoder.decode(res.information)
print(res.statusCode, error)
}
catch {
print(error.localizedDescription)
}
}
catch {
print(error.localizedDescription)
}
That is how SwiftHttp works in a nutshell, after all you possibly can setup customized encoders and decoders, however that is one other matter. In case you are within the mission, be happy to provide it a star on GitHub. We’ll use it sooner or later rather a lot each on the shopper and server facet. ⭐️⭐️⭐️
[ad_2]
