JSON-RPC Request Builder — Swift source
Build valid JSON-RPC 2.0 requests, notifications, success responses, and error responses, plus batch arrays. Validate message structure.
This is the Swift implementation — the same logic the interactive tool runs, in a shareable, citable form.
// json-rpc-builder — Swift port: JSON-RPC 2.0 message builder + structural validation.
import Foundation
let jsonrpcVersion = "2.0"
struct Outcome { let ok: Bool; let json: String; let error: String? }
func standardError(_ code: Int) -> String {
[-32700: "Parse error", -32600: "Invalid Request", -32601: "Method not found",
-32602: "Invalid params", -32603: "Internal error", -32000: "Server error"][code] ?? "Error"
}
// [String: Any] + JSONSerialization is the stdlib way to hold arbitrary JSON.
// (Key order in the output is unspecified; JSON-RPC itself does not mandate one.)
func stringify(_ obj: Any, pretty: Bool = false) throws -> String {
let data = try JSONSerialization.data(withJSONObject: obj, options: pretty ? [.prettyPrinted] : [])
return String(decoding: data, as: UTF8.self)
}
// `hasParams` tells "absent" apart from a real JSON null, mirroring the TS sentinel.
func buildRequest(_ method: String, params: Any? = nil, hasParams: Bool = false,
id: Any = 1) throws -> Outcome {
guard !method.isEmpty else {
return Outcome(ok: false, json: "", error: "method must be a non-empty string")
}
var obj: [String: Any] = ["jsonrpc": jsonrpcVersion, "method": method]
if hasParams { obj["params"] = params }
obj["id"] = id
return Outcome(ok: true, json: try stringify(obj), error: nil)
}
// A notification is a request without an id: fire-and-forget, no reply.
func buildNotification(_ method: String, params: Any? = nil,
hasParams: Bool = false) throws -> Outcome {
guard !method.isEmpty else {
return Outcome(ok: false, json: "", error: "method must be a non-empty string")
}
var obj: [String: Any] = ["jsonrpc": jsonrpcVersion, "method": method]
if hasParams { obj["params"] = params }
return Outcome(ok: true, json: try stringify(obj), error: nil)
}
func buildSuccessResponse(id: Any, result: Any) throws -> Outcome {
let obj: [String: Any] = ["jsonrpc": jsonrpcVersion, "result": result, "id": id]
return Outcome(ok: true, json: try stringify(obj), error: nil)
}
func buildErrorResponse(id: Any, code: Int, message: String? = nil,
data: Any? = nil, hasData: Bool = false) throws -> Outcome {
var err: [String: Any] = ["code": code, "message": message ?? standardError(code)]
if hasData { err["data"] = data }
let obj: [String: Any] = ["jsonrpc": jsonrpcVersion, "error": err, "id": id]
return Outcome(ok: true, json: try stringify(obj), error: nil)
}
/// Lightweight shape check — reports every problem, not just the first.
/// A parsed JSON null is NSNull (not nil), so present-but-null fields count as present.
func validateRpc(_ obj: Any?) -> (valid: Bool, errors: [String]) {
guard let o = obj as? [String: Any] else { return (false, ["Not an object."]) }
var errors: [String] = []
if o["jsonrpc"] as? String != jsonrpcVersion { errors.append("jsonrpc must be \"2.0\".") }
if let m = o["method"], !(m is String) { errors.append("method must be a string.") }
if o["result"] != nil && o["error"] != nil {
errors.append("cannot have both result and error.")
}
if o["method"] == nil && o["result"] == nil && o["error"] == nil {
errors.append("must have method, result, or error.")
}
return (errors.isEmpty, errors)
}
let req = try buildRequest("getBalance", params: ["0x1234"], hasParams: true)
print(req.json)
print(try buildNotification("blockHeader").json)
print(try buildSuccessResponse(id: 1, result: "0x1bc16d674ec80000").json)
print(try buildErrorResponse(id: 1, code: -32601).json)
let verdict = validateRpc(["jsonrpc": "1.0"])
print("\(verdict.valid): \(verdict.errors)")
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →