All Projects → michaelhenry → RxRetroSwift

michaelhenry / RxRetroSwift

Licence: MIT license
A Reactive way inspired by the simplicity of Retrofit when creating REST API Calls.

Programming Languages

swift
15916 projects
ruby
36898 projects - #4 most used programming language

Projects that are alternatives of or similar to RxRetroSwift

ServiceStack.Webhooks
Add Webhooks to your ServiceStack services
Stars: ✭ 26 (-61.19%)
Mutual labels:  restful-api
springboot-rest-api-angularjs-https
REST API https with Spring Boot and Angular JS. Use MySQL, Hibernate and Spring Security.
Stars: ✭ 38 (-43.28%)
Mutual labels:  restful-api
HttpServerLite
TCP-based simple HTTP and HTTPS server, written in C#.
Stars: ✭ 44 (-34.33%)
Mutual labels:  restful-api
HyperVue
🚀 基于 Hyperf + Vue + ElementUI 前后端分离的通用型管理后台
Stars: ✭ 23 (-65.67%)
Mutual labels:  restful-api
restgoose
Model-driven REST API framework using decorators
Stars: ✭ 28 (-58.21%)
Mutual labels:  restful-api
public
BitDust project source codes : official Public Git repository (mirror on GitHub) : https://bitdust.io
Stars: ✭ 19 (-71.64%)
Mutual labels:  restful-api
readthis-api
100000p.com
Stars: ✭ 17 (-74.63%)
Mutual labels:  restful-api
vbo365-rest
Unofficial Self-Service Web Portal for Veeam Backup for Microsoft Office 365
Stars: ✭ 44 (-34.33%)
Mutual labels:  restful-api
airad
Beego based Restful API service
Stars: ✭ 63 (-5.97%)
Mutual labels:  restful-api
wine
A lightweight and flexible framework to help build elegant web API
Stars: ✭ 39 (-41.79%)
Mutual labels:  restful-api
contentdb
Content database for Minetest mods, games, and more
Stars: ✭ 74 (+10.45%)
Mutual labels:  restful-api
cpp-rest-api
RESTFul Web service by C++, implemented basic REST endpoints and RESTVerbs (GET,POST,PUT,DELETE).
Stars: ✭ 13 (-80.6%)
Mutual labels:  restful-api
rack-cargo
🚚 Batch requests for Rack apps (works with Rails, Sinatra, etc)
Stars: ✭ 17 (-74.63%)
Mutual labels:  restful-api
tinyspec
Simple syntax for describing REST APIs
Stars: ✭ 95 (+41.79%)
Mutual labels:  restful-api
serverless-rest-api
Building RESTful Web APIs with Firebase Cloud Function, Firestore, Express and TypeScript
Stars: ✭ 103 (+53.73%)
Mutual labels:  restful-api
server
A server for tf2pickup.org
Stars: ✭ 16 (-76.12%)
Mutual labels:  restful-api
FeedSDK
Java SDK for downloading large gzipped (tsv) item feed files and applying filters for curation
Stars: ✭ 20 (-70.15%)
Mutual labels:  restful-api
gorest
Go RESTful API starter kit with Gin, JWT, GORM (MySQL, PostgreSQL, SQLite), Redis, Mongo, 2FA, email verification, password recovery
Stars: ✭ 135 (+101.49%)
Mutual labels:  restful-api
swagger-brake
Swagger contract checker for breaking API changes
Stars: ✭ 49 (-26.87%)
Mutual labels:  restful-api
djburger
Framework for safe and maintainable web-projects.
Stars: ✭ 75 (+11.94%)
Mutual labels:  restful-api

RxRetroSwift

CI Status Version License Platform

What does it do?

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":"[email protected]"
}

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
  }
}

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Requirements

Features

  • Easy to use and simple, Just few lines of codes (excluding RxSwift).
  • Clean and Neat implementation.
  • Flexible error handling.
  • Simplify your rest API client.

TODO

  • Unit and integration test (done)
  • Add Example (done)
  • Support different authentication method for your URLRequest.

Installation

Cocoapods

RxRetroSwift is now available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'RxRetroSwift'

Swift Package Manager

.package(url: "https://github.com/michaelhenry/RxRetroSwift", from: "2.1"),

Sample implementation

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)
  }
}

Testing

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
      }
    }
  }
}

Contributions

Just feel free to submit pull request or suggest anything that would be useful.

Author

Michael Henry Pantaleon, [email protected]

License

RxRetroSwift is available under the MIT license. See the LICENSE file for more info.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].