JSONRPCKitという、SwiftでJSON-RPC 2.0を扱うライブラリを作っているときに必要になったので作ってみました。
文字列または数値のみを許容する型を作る
JSON-RPC 2.0のid
は文字列か数値をとる仕様なので、その制限を型で表現してみました。以下のようにenum
を使うと簡単です。
public enum RequestIdentifier {
case NumberIdentifier(Int)
case StringIdentifier(String)
}
使うときにはJSON、つまり[String: AnyObject]
との相互変換が必要になるので、便利メソッドをいくつか用意しておくと使いやすくなります。
extension RequestIdentifier {
public init?(value: AnyObject) {
switch value {
case let number as Int:
self = .NumberIdentifier(number)
case let string as String:
self = .StringIdentifier(string)
default:
return nil
}
}
public var value: AnyObject {
switch self {
case NumberIdentifier(let number):
return number
case StringIdentifier(let string):
return string
}
}
}
RequestIdentifier
からAnyObject
へ変換
let id = RequestIdentifier.NumberIdentifier(1)
let json: [String: AnyObject] = ["id": id.value]
debugPrint(json) // ["id": 1]
AnyObject
からRequestIdentifier
へ変換
if let id = json["id"].flatMap(RequestIdentifier.init) {
debugPrint(id) // RequestIdentifier.NumberIdentifier(1)
}
Dictionaryのキーとして使えるようにする
ライブラリ内部でこの型をDictionary
のキーとして使いたかったので、Hashable
プロトコルに準拠させました。ハッシュ値として、数値の場合はその値を、文字列の場合はhashValue
を返すようにしてみました。
extension RequestIdentifier: Hashable {
public var hashValue: Int {
switch self {
case NumberIdentifier(let number):
return number
case StringIdentifier(let string):
return string.hashValue
}
}
}
Hashable
はEquatable
を継承しているので、これにも準拠させる必要があります。ハッシュ値が同じときの同値判断のためですね。if case let
で2つの値を取り出しながら比較する形にしてみました。
public func ==(lhs: RequestIdentifier, rhs: RequestIdentifier) -> Bool {
if case let (.NumberIdentifier(left), .NumberIdentifier(right)) = (lhs, rhs) {
return left == right
}
if case let (.StringIdentifier(left), .StringIdentifier(right)) = (lhs, rhs) {
return left == right
}
return false
}
ここまでやると、以下のようにDictionaryのキーとして直接使えるようになります。
var array: [RequestIdentifier: String] = [:]
array[RequestIdentifier.StringIdentifier("a")] = "string key"
array[RequestIdentifier.NumberIdentifier(1)] = "number key"
debugPrint(array) // [RequestIdentifier.StringIdentifier("a"): "string key", RequestIdentifier.NumberIdentifier(1): "number key"]
ちなみに、同じenum
でもAssociated Valuesを持たない場合はそのままDictionaryのキーとして使えるようです。
参考
https://github.com/bricklife/JSONRPCKit/blob/master/JSONRPCKit/RequestIdentifier.swift
http://qiita.com/kishisuke/items/c803b1ce2bbc91e5b278
https://ez-net.jp/article/5D/T5i2IddA/E6FDjcBSY6e1/