JSONRPCKit is a type-safe JSON-RPC 2.0 library purely written in Swift.
// Generating request JSON
let batchFactory = BatchFactory(version: "2.0", idGenerator: NumberIdGenerator())
let request = Subtract(minuend: 42, subtrahend: 23)
let batch = batchFactory.create(request)
let encoder = JSONEncoder()
let data = try! encoder.encode(batch)
let jsonString = String(data: data, encoding: .utf8) // {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}
// Parsing response JSON
let responseObject: String =
"""
{ "jsonrpc": "2.0", "result": 19, "id": 1}
"""
let responseData = responseObject.data(using: .utf8)!
let response = try! batch.responses(from: responseData)
response // 19 (type of response is inferred from SubtractRequest.Response)
- Swift 5.3 / Xcode 12.2 or later
- If you use Swift 3.1 (Xcode 8.3), you can use 2.0.3 instead.
- iOS 12.0 or later
- macOS 10.10 or later
- watchOS 2.0 or later
- tvOS 9.0 or later
- Linux is also supported
- Create Decodable Response type
- Define request type
- Generate request JSON
First of all, define a request type that conforms to Request
.
struct Subtract: JSONRPCKit.Request {
typealias Response = Int
let minuend: Int
let subtrahend: Int
var method: String {
return "subtract"
}
var parameters: Encodable? {
return [minuend, subtrahend]
}
}
If your parameters are not the same type
var parameters: Encodable? {
return [42, "Answer to the question"]
}
you can use the included type erasure wrapper AnyEncodable
:
var parameters: Encodable? {
return [AnyEncodable(42), AnyEncodable("Answer to the question")]
}
To generate request JSON, pass Request
instances to BatchFactory
instance, which has common JSON-RPC version and identifier generator.
When BatchFactory
instance receives request(s), it generates identifier(s) for the request(s) and request JSON by combining id, version, method and parameters.
let batchFactory = BatchFactory(version: "2.0", idGenerator: NumberIdGenerator())
let request1 = Subtract(minuend: 42, subtrahend: 23)
let request2 = Subtract(minuend: 23, subtrahend: 42)
let batch = batchFactory.create(request1, request2)
The request JSON can be generated by JSONEncoder.
let encoder = JSONEncoder()
let data = try! encoder.encode(batch)
let jsonString = String(data: data, encoding: .utf8)
jsonString
looks like below:
[
{
"method" : "subtract",
"jsonrpc" : "2.0",
"id" : 1,
"params" : [
42,
23
]
},
{
"method" : "subtract",
"jsonrpc" : "2.0",
"id" : 2,
"params" : [
23,
42
]
}
]
Suppose that following JSON is returned from server:
[
{
"result" : 19,
"jsonrpc" : "2.0",
"id" : 1,
"status" : 0
},
{
"result" : -19,
"jsonrpc" : "2.0",
"id" : 2,
"status" : 0
}
]
To parse response object, execute responses(from:)
of Batch
instance.
When responses(from:)
is called, Batch
finds corresponding response object by comparing request id and response id.
After it find the response object, it executes responses(from:)
of Response
to get Request.Response
from the response object.
let responseStr: String = ...
let responseData = responseObject.data(using: .utf8)!
let (response1, response2) = try! batch.responses(from: responseData)
print(response1) // 19
print(response2) // -19
JSON-RPC over HTTP by APIKit
APIKit is a type-safe networking abstraction layer.
APIKit also has RequestType
that represents HTTP request.
import APIKit
struct MyServiceRequest<Batch: JSONRPCKit.Batch>: APIKit.Request {
let batch: Batch
typealias Response = Batch.Responses
var baseURL: URL {
return URL(string: "https://api.example.com/")!
}
var method: HTTPMethod {
return .post
}
var path: String {
return "/"
}
var parameters: Any? {
let encoder = JSONEncoder()
guard let data = try? encoder.encode(batch) else {
return nil
}
return try? JSONSerialization.jsonObject(with: data, options: [])
}
func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response {
let jsonData = try JSONSerialization.data(withJSONObject:object)
return try batch.responses(from: jsonData)
}
}
let batchFactory = BatchFactory(version: "2.0", idGenerator: NumberIdGenerator())
let request1 = Subtract(minuend: 42, subtrahend: 23)
let request2 = Subtract(minuend: 23, subtrahend: 42)
let batch = batchFactory.create(request1, request2)
let httpRequest = MyServiceRequest(batch: batch)
Session.sendRequest(httpRequest) { result in
switch result {
case .Success(let response1, let response2):
print(response1.count) // CountCharactersResponse
print(response2.count) // CountCharactersResponse
case .Failure(let error):
print(error)
}
}
JSONRPCKit is released under the MIT License.