QuickRest 0.9

QuickRest 0.9

TestsTested
LangLanguage SwiftSwift
License MIT
ReleasedLast Release Oct 2017
SwiftSwift Version 3.0
SPMSupports SPM

Maintained by Aryan Ghassemi.



  • 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 (extension on top of dictionary)

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

Mapping Methods Usage
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
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

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

######Fetching Raw data
In some cases you may want to fetch raw json data from the API call.

Dictionary conforms to Deserializable, this means you could simply pass [String: Any] to fetchObject and fetchObjects and the completion will call back with a type of Dictionary

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