After reviewing many REST clients for iOS , we realize that all are very verbose , which is unnecessary. This library was born from the need to simplify the communication between client and server.
- Swift 4+
For Swift 2.2, 2.3 and 3 check the branches.
You can read the doc's in this wiki
To add EasyRest to your project, add the following in your podfile
pod 'EasyRest'
pod 'EasyRest/PromiseKit'
class Post : Codable {
var id: String?
var title: String?
}
// Maps API calls and infos, like method and query params
enum JsonPlaceholderRoutable: Routable {
// Each API interaction is a case
case posts
// Using query params
case postsFilter(userId: Int)
// Example with path
case post(id: Int)
case deletePost(id: Int)
// Example with body
case makePost(body: PostModel)
// Example with path, body and header
case editPost(id: Int, body: PostModel)
case editTitle(id: Int, body: PostModel)
// Now test every possibility in a fashion Swift syntax
var rule: Rule {
switch(self) {
case .posts:
return Rule(
method: .get, // HTTP verb
path: "/posts/", // Endpoint
isAuthenticable: false, // Uses EasyRest Authenticated Service?
parameters: [:]) // query/path/header params? Header Body? Multipart form data? Put here!
case let .postsFilter(userId):
return Rule(
method: .get,
path: "/posts/",
isAuthenticable: false,
parameters: [.query: ["userId": "\(userId)"]])
case let .post(id):
return Rule(
method: .get,
path: "/posts/{id}/",
isAuthenticable: false,
parameters: [.path: ["id": id]])
case let .deletePost(id):
return Rule(
method: .delete,
path: "/posts/{id}/",
isAuthenticable: false,
parameters: [.path: ["id": id]])
case let .makePost(body):
return Rule(
method: .post,
path: "/posts/",
isAuthenticable: false,
parameters: [.body: body])
case let .editPost(id, body):
return Rule(
method: .put,
path: "/posts/{id}",
isAuthenticable: false,
parameters: [
.path: ["id": id],
.body: body,
.header: ["Content-type": "application/json; charset=UTF-8"]])
case let .editTitle(id, body):
return Rule(
method: .patch,
path: "/posts/{id}",
isAuthenticable: false,
parameters: [
.path: ["id": id],
.body: body,
.header: ["Content-type": "application/json; charset=UTF-8"]])
}
}
}
enum TestRoute: Routable{
case me(String)
case post(String)
var rule: Rule {
switch(self) {
case let .me(name):
return Rule(method: .get, path: "/api/v1/users/me/", isAuthenticable: true, parameters: [.query : ["name": name]])
case let .post(id):
let parameters : [ParametersType: AnyObject] = [:]
return Rule(method: .get, path: "/api/v1/posts/\(id)/", isAuthenticable: true, parameters: parameters)
}
}
}
class TestRouteService : OAuth2Service<TestRoute> {
override var base: String { return BASE_URL }
override var interceptors: [Interceptor] { return [DefaultHeadersInterceptor()] }
convenience init() {
self.init()
}
func me(name: String, onSuccess: (result: Response<UserTest>?) -> Void, onError: (ErrorType?) -> Void, always: () -> Void) {
try! call(.me(name), type: UserTest.self, onSuccess: onSuccess, onError: defaultErrorHandler(onError), always: always)
}
func post(id: Int, onSuccess: (result: Response<Post>?) -> Void, onError: (ErrorType?) -> Void, always: () -> Void) {
try! call(.post(id), type: Post.self, onSuccess: onSuccess, onError: defaultErrorHandler(onError), always: always)
}
func defaultErrorHandler(onError: (ErrorType?) -> Void) -> (ErrorType?) -> Void {
return { error in
/* Do whatever is default for all errors, like
switch error.cause {
case .InternetConnection:
// backOff()
case .FailedJsonSerialization:
Crashlytics.sendMessage(error....)
Sentry.captureEvent(...)
}
*/
onError(error)
}
}
}
We also support PromiseKit calls:
class JsonPlaceholderService : Service<JsonPlaceholderRoutable> {
override public var base: String { return "https://jsonplaceholder.typicode.com" }
}
And then:
let service = JsonPlaceholderService()
try! service.call(.post(id: 1), type: PostModel.self).promise
.done { result in
print("RESPONSE ITEM ID: \(result!.body!.id!)")
}.catch { error in
print("Error : \(error.localizedDescription)")
}.finally {
print("This code will be called all the time")
}
class DefaultHeadersInterceptor : Interceptor {
required init() {}
func requestInterceptor<T: Codable>(_ api: API<T>) {
api.headers["Content-Type"] = "application/json"
api.headers["Accept"] = "application/json"
api.headers["Device-Token"] = "8ec3bba7de23cda5e8a2726c081be79204faede67529e617b625c984d61cf5c1"
api.headers["Device-Agent"] = "iOS_SANDBOX"
api.headers["Accept-Language"] = "pt-br"
}
func responseInterceptor<T: Codable, U>(_ api: API<T>, response: DataResponse<U>) {
}
}
- Remove Genome and use a Swift 4 Codable
- Retry call
- Send request so connect the Internet
- Create Unit Tests for main functionalities
- Create Unit Tests for authenticable services
- Create Unit Tests for multpart-data uploads
- Cancel request
- Create a wiki
- Add a repsonse model for send extra information like http status code
- File Upload
- Improve request Syntax
- Error Handler
- Add support for coloring in AppCode
- Download files in memory
- Download files directily to storage (memory optimal)
- Cache system based on NSCache
EasyRest is available under the MIT license. See the LICENSE file for more info.