It simplifies your RESTful API calls, automatically convert the HttpResponse
into specified Model as well as the Error using the new apple ’s Codable feature.
For example in a request for fetching specific user information and you have a User
model, all you have to do is make the User model conforms to Codable and specify it when using the RequestCaller.
{
"name":"kel",
"email":"me@iamkel.net"
}
User
model that conforms to Codable.
struct User: Codable {
var name:String
var email:String
}
This will automatically convert the response into an instance User
model.
Example:
let caller = RequestCaller(config: URLSessionConfiguration.default)
func fetchUser(byUserId userId) -> Observable<Result<User, ErrorModel>> {
let request:URLRequest = RequestModel(
httpMethod: .get,
path: "v1/users/\(userId)")
.asURLRequest()
return caller.call(request)
}
Let say it’s an array of users; since Array conforms to Codable, all you have to do is specify the type to be [User]
.
Example:
func fetchUsers() -> Observable<Result<[User], ErrorModel>> {
let request:URLRequest = RequestModel(
httpMethod: .get,
path: "v1/users")
.asURLRequest()
return caller.call(request)
}
About handling ResponseError:
RxRetroSwift provided a typealias ErrorCodable which is a combination of HasErrorInfo and Decodable protocol:
public typealias DecodableError = Decodable & HasErrorInfo
For example, the json error response of your login request is
{
"message": "Unable to login."
"details": {
"password": "You changed your password 2 months ago."
}
}
And you have this as Model:
struct ErrorModel {
var errorCode:Int?
var message:String
var details:[String:String]
}
How about dealing to a request that don't expect to return an object or model?
RxRetroSwift provide a method that will return Observable<Result<RawResponse>, DecodableErrorModel>>.
public func call<DecodableErrorModel:DecodableError>(_ request: URLRequest)
-> Observable<Result<RawResponse, DecodableErrorModel>>
public struct RawResponse {
public var statusCode:Int
public var data:Data?
init(statusCode:Int, data:Data?) {
self.statusCode = statusCode
self.data = data
}
}
To run the example project, clone the repo, and run pod install
from the Example directory first.
- Easy to use and simple, Just few lines of codes (excluding RxSwift).
- Clean and Neat implementation.
- Flexible error handling.
- Simplify your rest API client.
- Unit and integration test (done)
- Add Example (done)
- Support different authentication method for your
URLRequest
.
RxRetroSwift is now available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'RxRetroSwift'
.package(url: "https://github.com/michaelhenry/RxRetroSwift", from: "2.1"),
Using JSONPlaceholder API. You can also check the Sample Project
class APIClient {
static var shared = APIClient()
var caller = RequestCaller.shared
private init() {
RequestModel.defaults.baseUrl = "https://jsonplaceholder.typicode.com"
}
func fetchPosts() -> Observable<Result<[Post], ErrorModel>> {
let request = RequestModel(
httpMethod: .get,
path: "posts")
.asURLRequest()
return caller.call(request)
}
func insertPost(post:Post) -> Observable<Result<Post, ErrorModel>> {
let request = RequestModel(
httpMethod: .post,
path: "posts",
payload: post.dictionaryValue)
.asURLRequest()
return caller.call(request)
}
func fetchComments() -> Observable<Result<[Comment], ErrorModel>> {
let request = RequestModel(
httpMethod: .get,
path: "comments")
.asURLRequest()
return caller.call(request)
}
func fetchAlbums() -> Observable<Result<[Album], ErrorModel>> {
let request = RequestModel(
httpMethod: .get,
path: "albums")
.asURLRequest()
return caller.call(request)
}
func fetchPhotos() -> Observable<Result<[Photo], ErrorModel>> {
let request = RequestModel(
httpMethod: .get,
path: "photos")
.asURLRequest()
return caller.call(request)
}
func fetchTodos() -> Observable<Result<[Todo], ErrorModel>> {
let request = RequestModel(
httpMethod: .get,
path: "todos")
.asURLRequest()
return caller.call(request)
}
func fetchUsers() -> Observable<Result<[User],ErrorModel>> {
let request = RequestModel(
httpMethod: .get,
path: "users")
.asURLRequest()
return caller.call(request)
}
}
class TestAPIClient:QuickSpec {
override func spec() {
describe("Using JSONPlaceholder API") {
let apiClient = APIClient.shared
it("Check Posts result count"){
let observable = apiClient.fetchPosts()
expect(observable.map { $0.value!.count }).first == 100
}
it("Can insert post"){
var post = Post()
let title = "This is my post"
let userId = 101
let body = "This is a message body"
post.title = title
post.userId = userId
post.body = body
let observable = apiClient.insertPost(post: post)
expect(observable.map { $0.value?.title ?? "" }).first == title
expect(observable.map { $0.value?.userId ?? 0 }).first == userId
expect(observable.map { $0.value?.body ?? "" }).first == body
}
it("Check Comments result count"){
let observable = apiClient.fetchComments()
expect(observable.map { $0.value!.count }).first == 500
}
it("Check Albums result count"){
let observable = apiClient.fetchAlbums()
expect(observable.map { $0.value!.count }).first == 100
}
it("Check Photos result count"){
let observable = apiClient.fetchPhotos()
expect(observable.map { $0.value!.count }).first == 5000
}
it("Check Todos result count"){
let observable = apiClient.fetchTodos()
expect(observable.map { $0.value!.count }).first == 200
}
it("Check Users result count"){
let observable = apiClient.fetchUsers()
expect(observable.map { $0.value!.count }).first == 10
}
}
}
}
Just feel free to submit pull request or suggest anything that would be useful.
Michael Henry Pantaleon, me@iamkel.net
RxRetroSwift is available under the MIT license. See the LICENSE file for more info.