SwiftyJsonをコードリーディングしてみました。
ブログにはいろいろ書いたのですが、こちらには重要なsubscriptとSequenceTypeについてのみ転載。
複雑な処理がないので初心者でも読みやすく、subscriptやSequenceTypeといった基本的かつ重要なことがわかるのでオススメです。
SwiftyJsonとは
SwiftyJsonとはswiftでjsonを使うときにはほぼデファクトとなっているライブラリです。
以下のような感じで簡単にjsonを使えるので便利です。
let json: JSON = JSON(data: dataFromNetworking)
let user: <String, JSON> = json["user"]. dictionaryValue
let name: String = json["user"]["name"].string
subscript
subscriptとは[ ]で要素にアクセスするやつです。
SwiftyJsonでは以下のようにpathをarrayとして渡してアクセスすることができます。
let path = [9,"list","person","name"]
let name = json[path]
パスのArrayの中は、ArrayのときにInt、DictionaryのときにStringを受け取ることができるので、IntとStringにSubscriptTypeプロトコルを追加し、[SubscriptType]を引数に受け取るように実装してありました。
Arrayの中に複数の型が入っているときは[AnyObject]とするのではなく、このように実装すると安全で良いですね。
public protocol SubscriptType {}
extension Int: SubscriptType {}
extension String: SubscriptType {}
extension JSON {
public subscript(path: [SubscriptType]) -> JSON {
get {
if path.count == 0 {
return JSON.nullJSON
}
var next = self
for sub in path {
next = next[sub:sub]
}
return next
}
set {
// setter
}
}
}
subscriptの中ではエラーチェックの後、配列で渡されたパスをfor文を用いて一つづつ処理しています。
一つづつの処理はInt(Array)とString(Dictionary)のときで処理が変わるので振り分けるだけのメソッドを用意してあります。
その中の分岐でsub is Stringという構文が使われていました。
switch文の中では case let string as String、if文の中では sub is Stringとすることで型チェックができるみたいです。
private subscript(#sub: SubscriptType) -> JSON {
get {
if sub is String {
return self[key:sub as! String]
} else {
return self[index:sub as! Int]
}
}
set {
// setter
}
}
SequenceType
SwiftyJsonではjsonがArray/Dictionaryのときにfor..inを使ったループ処理ができます。
for (key: String, subJson: JSON) in json {
//Do something you want
}
これを実現するためにはSequenceTypeプロトコルを実装する必要があります。
SequenceTypeプロトコルはgenerateメソッドを要求しています。
generateメソッドはGeneratorTypeプロトコルを実装しているGeneratorを返す必要があるのですが、今回はArrayとDictionaryを利用するのでGeneratorTypeについては気にする必要はありません。
もし自作クラスにSequenceTypeを適用するときにはGeneratorTypeも実装する必要があります。
SwiftyJsonではgenerateメソッドは以下のようになっています。
ArrayとDictionaryで処理を分けているだけですね。
GeneratorはGeneratorOfでClosureを使って実装しています。
public func generate() -> GeneratorOf <(String, JSON)> {
switch self.type {
case .Array:
let array_ = object as! [AnyObject]
var generate_ = array_.generate()
var index_: Int = 0
return GeneratorOf<(String, JSON)> {
if let element_: AnyObject = generate_.next() {
return ("\(index_++)", JSON(element_))
} else {
return nil
}
}
case .Dictionary:
let dictionary_ = object as! [String : AnyObject]
var generate_ = dictionary_.generate()
return GeneratorOf<(String, JSON)> {
if let (key_: String, value_: AnyObject) = generate_.next() {
return (key_, JSON(value_))
} else {
return nil
}
}
default:
return GeneratorOf<(String, JSON)> {
return nil
}
}
}