Skip to content

Instantly share code, notes, and snippets.

@KevinVitale
Last active February 12, 2020 00:57
Show Gist options
  • Save KevinVitale/152d2d105620062ecdcf6e9d29488032 to your computer and use it in GitHub Desktop.
Save KevinVitale/152d2d105620062ecdcf6e9d29488032 to your computer and use it in GitHub Desktop.
ClientAPI + callAsFunction
import Foundation
import AnyCodable
#if !canImport(Darwin)
import FoundationNetworking
#endif
public protocol JSONRPCAPI: Codable {
static var prefix :String { get }
}
extension JSONRPCAPI {
static var version :String { "2.0" }
}
extension JSONRPCAPI {
public typealias Request<R: Decodable> = Web3Kit.Request<Self,R>
}
public struct Client<API: JSONRPCAPI> {
private let configuration :ClientConfiguration
private let session :URLSession
private let encoder :JSONEncoder
private let decoder = JSONDecoder()
fileprivate func encoded<T: Encodable>(request: T) -> URLRequest {
var urlRequest = URLRequest(url: configuration.url)
urlRequest.cachePolicy = .reloadIgnoringLocalCacheData
urlRequest.httpMethod = "POST"
urlRequest.httpBody = try? encoder.encode(request)
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
return urlRequest
}
fileprivate func decoded<T: Decodable>(data: Data?) -> Result<Response<T>,Swift.Error> {
Result<Response<T>,Swift.Error> {
guard let data = data else {
// FIXME with a .failure(/* error */)
fatalError("\(#file): \(#function); \(#line)")
}
return try decoder.decode(Response<T>.self, from: data)
}
}
fileprivate func decoded<T: Decodable>(data: Data?) -> Result<[Response<T>],Swift.Error> {
Result<[Response<T>],Swift.Error> {
guard let data = data else {
// FIXME with a .failure(/* error */)
fatalError("\(#file): \(#function); \(#line)")
}
return try decoder.decode([Response<T>].self, from: data)
}
}
}
extension Client {
public func callAsFunction<R: Decodable>(request: API.Request<R>, callback: @escaping (Result<Response<R>,Swift.Error>) -> ()) {
let task = session.dataTask(with: encoded(request: request)) { data, response, error in
guard error == nil else {
callback(.failure(error!))
return
}
callback(self.decoded(data: data))
}
task.resume()
}
public func callAsFunction(request: API.Request<AnyDecodable>..., callback: @escaping (Result<[Response<AnyDecodable>],Swift.Error>) -> ()) {
self.callAsFunction(request: request, callback: callback)
}
public func callAsFunction<R: Decodable>(request: [API.Request<R>], callback: @escaping (Result<[Response<R>],Swift.Error>) -> ()) {
let task = session.dataTask(with: encoded(request: request)) { data, response, error in
guard error == nil else {
callback(.failure(error!))
return
}
callback(self.decoded(data: data))
}
task.resume()
}
public func callAsFunction(request method: String, parameters: [Any] = [], id: Int = 1, callback: @escaping (Result<Response<AnyDecodable>,Swift.Error>) -> ()) {
self.callAsFunction(request: API.Request<AnyDecodable>(method, parameters: parameters, id: id), callback: callback)
}
}
extension Client where API == Web3 {
func derivedClient<API: JSONRPCAPI>() -> Client<API> {
.init(configuration: self.configuration,
session: self.session,
encoder: self.encoder)
}
public init(_ configuration: ClientConfiguration, session: URLSession = URLSession(configuration: .default)) {
self.configuration = configuration
self.session = session
self.encoder = JSONEncoder()
self.encoder.outputFormatting = .prettyPrinted
}
}
public extension Client where API == Web3 {
var bzz: Client<Bzz> { derivedClient() }
var eth: Client<Eth> { derivedClient() }
var net: Client<Net> { derivedClient() }
var shh: Client<Shh> { derivedClient() }
}
public struct Web3 :JSONRPCAPI { public static let prefix :String = "web3" }
public struct Bzz :JSONRPCAPI { public static let prefix :String = "bzz" }
public struct Eth :JSONRPCAPI { public static let prefix :String = "eth" }
public struct Net :JSONRPCAPI { public static let prefix :String = "net" }
public struct Shh :JSONRPCAPI { public static let prefix :String = "shh" }
extension Request where API == Web3 {
public static func clientVersion() -> API.Request<String> {
"clientVersion"
}
public static func sha3(data: String, id: Int = 1) -> API.Request<String> {
API.Request("sha3", parameters: [data], id: id)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment