QuickRest 0.7

QuickRest 0.7

DocsDocumented
TestsTested
LangLanguage SwiftSwift
License MIT
ReleasedLast Release Mar 2017
SPMSupports SPM

Maintained by Aryan Ghassemi.


Downloads

Total34
Week1
Month20

Installs

Apps3
Apps WeekApps This Week 1
powered by Segment

GitHub

Stars1
Watchers1
Forks0
Issues0
Contributors1
Pull Requests0

Code

Files9
SizeIntegration Size 56 kb
LOCLines of Code 293


  • By
  • Aryan Ghassemi

QuickRest

Build Status

Example

Quick rest is a simple networking/mapping library that allows you to build and connect to APIs very quickly.

Let’s build a simple network layer to fetch repositories from Github. We first create the models. Models shoould implement Deserializable

public struct Repository {
    public let id: Int
    public let nuame: String
    public let description: String?
    public let owner: Owner
    public let language: Language?
}

extension Repository: Deserializable {
    public init(json: [String : Any]) throws {
        id = try json.readValue(key: "id")
        nuame = try json.readValue(key: "name")
        description = try? json.readValue(key: "description")
        owner = try json.readDeserializable(key: "owner")
        language = try? json.readEnum(key: "language")
    }
}

public struct Owner {
    public let id: Int
    public let username: String
    public let avatarUrl: String?
}

extension Owner: Deserializable {
    public init(json: [String : Any]) throws {
        id = try json.readValue(key: "id")
        username = try json.readValue(key: "login")
        avatarUrl = try? json.readValue(key: "avatar_url")
    }
}

public enum Language: String {
    case java = "Java"
    case swift = "Swift"
    case objectiveC = "Objective-C"
    case Ruby = "Ruby"
}

Next we create a network manager class

public class NetworkManager {

    private let networkService : NetworkService

    public init() {
        networkService = NetworkHttpService(
            baseUrlString: "https://api.github.com/",
            urlSession: URLSession.shared,
            networkRequestInterceptor: nil,
            timeout: 60)
    }

    @discardableResult public func fetchRepositories(username: String, completion: @escaping (NetworkResponse<[Repository]>)->Void) -> URLSessionTask {
        return networkService.fetchObjects(
            type: Repository.self,
            path: "users/\(username)/repos",
            completion: completion)
    }

}

That’s it. We are done. Now let use the network manager to fetch the objects from github

let networkManger = NetworkManager()

networkManger.fetchRepositories(username: "aryaxt") {
  switch $0.result {
    case .success(let repos):
        print(repos)

    case .failure(let error):
        print(error)
    }
}

Mapping

There are 4 type-safe methods that can be used for mapping

readValue

Used for mapping any fields where the value can be directly set from the dictionary (ex: String, Int, Double, dictionary, array of strings, etc)

readDeserializable

Used for mapping models that implement Deserializable protocol

readDeserializableList

Used for mapping an array of models that implement Deserializable protocol

readDeserializableList

Used for mapping an array of models that implement Deserializable protocol

readEnum

Used for reading enums. If the value in the dictionary is the same as the raw representable value of the enum it’ll be converted to the enum representable

All these methods can be used for optional values. In order to map optionals simply use try? instead of try

Making API calls

NetworkHttpService has 2 methods used for triggersing an API call, fetchObject and fetchObjects

fetchObject is used when the root level response is a dictionary and the dictionary directly maps to a model. For exampe

networkService.fetchObject(type: User.self, path: "users/5", completion: completion)
{
   "firstName" : "Aryan"
   "lastName" : "Ghassemi"
}

fetchObjects is used when the API call returns a list of objects. Usually the response would be an array

networkService.fetchObjects(type: User.self, path: "users/search", completion: completion)
[{
   "firstName" : "Aryan"
   "lastName" : "Ghassemi"
}]

In some cases the response is wrapped inside another key in the dictionary; unwrapKey can be used to map the results to an array directly instead of having to create a model to wrap the array

networkService.fetchObjects(type: User.self, path: "users/search", unwrapKey: "results", completion: completion)
{"results" : [{
   "firstName" : "Aryan"
   "lastName" : "Ghassemi"
   }]
}

Customozing Requests

In some cases you may need to modify the request. For example pass a custom header. You could pass a class/struct implementing NetworkRequestInterceptor to NetworkHttpService.

public struct MyNetworkRequestInterceptor: NetworkRequestInterceptor {
    public func inttercept(request: inout URLRequest) {
        request.addValue("MyApp", forHTTPHeaderField: "User-Agent")
        request.addValue("123123", forHTTPHeaderField: "Access-Token")
    }
}

TODO:

Allow fetching [String: Any] type by having it conform to Deserializable. This feature will be availbale on next version of swift https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md