AbstractionKit provides various protocols and structs that make it easier to abstract json APIs.
AbstractionKit does not depend on any other networking and mapping frameworks. So you can bridge between AbstractionKit and any frameworks you usually use.
Define your API endpoint as follows:
struct GetUser: EndpointDefinition {
/// Means this endpoint returns single User object.
typealias Response = SingleResponse<User>
/// Path for resource.
static var path: String = "/user"
/// You can switch server for each endpoint.
/// (You can use .staging or .mock if you defined.)
static var environment: Environment = .production
/// Parameters to contain in request.
let parameters: [String: Any]
/// HTTP method
var method: HTTPMethod = .get
init(userID: Int) {
parameters = [
"id": userID
]
}
}
then simply extract objects from JSON like so:
// `json` is a dictionary that API returned
let user = try GetUser.Response.init(json: json).result
A sample that uses APIKit, Himotoki, and RxSwift is available. Clone and run $ carthage update
then run AbstractionKitSample
.
- Swift 4.0
- Xcode 9.x
- Add
github "hiragram/AbstractionKit" ~> 0.1
to yourCartfile
. - Run
$ carthage update
. - Add built framework in
Carthage/Build/iOS/
to your project. - Append
AbstractionKit.framework
to arguments of$ carthage copy-frameworks
.
Create a wrapper for the network framework. The wrapper transforms AbstractionKit's endpoint instance to network framework's request object and generate object from response JSON.
For example, wrapper for APIKit is as follows,
struct APIKitBridgeRequest<Endpoint: EndpointDefinition>: APIKit.Request {
typealias Response = Endpoint.Response.Result
var baseURL: URL = Endpoint.environment.url(forPath: "")
var path = Endpoint.path
var parameters: Any? {
return endpoint.parameters
}
var method: APIKit.HTTPMethod {
return endpoint.method.apiKitMethod
}
private let endpoint: Endpoint
init(endpoint: Endpoint) {
self.endpoint = endpoint
}
func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Endpoint.Response.Result {
guard let jsonObj = object as? Endpoint.Response.JSON else {
fatalError()
}
return try Endpoint.Response.init(json: jsonObj).result
}
}
This wrapper has AbstractionKit's endpoint definition as a generic type parameter, and conforms to APIKit.Request
protocol.
Then you create an instance of APIKitBridgeRequest
and execute request.
// `GetUser.Response` is `SingleResponse<User>`
let endpoint = GetUser.init(userID: 100)
let request = APIKitBridgeRequest.init(endpoint: endpoint)
Session.send(request, callbackQueue: nil, handler: { (result) in
switch result {
case .success(let user):
print(user)
case .failure(let error):
print(error)
}
})
SingleResponseElement
and ArrayResponseElement
are available to define model object types. Each protocol constraints to implement decode
method. You can use any mapping framework in the method as follows,
struct User: SingleResponseElement, Himotoki.Decodable {
static var singleKey = "user"
var id: Int
var name: String
/// Implementation for Himotoki.Decodable
static func decode(_ e: Extractor) throws -> User {
return try User.init(
id: e <| "id",
name: e <| "name"
)
}
/// Implementation for AbstractionKit.SingleResponseElement
static func decode(from json: Any) throws -> User {
// Using Himotoki internally.
return try decodeValue(json)
}
}
AbstractionKit does not depend on any mapping framework, so you can also map JSON to object manually. (Of course I will never recommend it.)
struct User: SingleResponseElement {
static var singleKey = "user"
var id: Int
var name: String
/// Implementation for AbstractionKit.SingleResponseElement
static func decode(from obj: Any) throws -> User {
let json = obj as! [String: Any]
return User.init(
id: json["id"] as! Int,
name: json["name"] as! String
)
}
}
work in progress