TestsTested | ✓ |
LangLanguage | SwiftSwift |
License | MIT |
ReleasedLast Release | Oct 2017 |
SwiftSwift Version | 3.0 |
SPMSupports SPM | ✓ |
Maintained by Aryan Ghassemi.
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)
}
}
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 |
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
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")
}
}
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