目新しいものでもなく、あちこちで見かける内容ですが。
Mirror を使うとそのインスタンスが持つ情報を簡単に取得できるので、デバッグ時にはかなり助かります。
Swift3.0向けに extension にてより扱いやすい形で書いてみました。
実装
protocol Reflectable {
}
extension Reflectable where Self: Any {
private func toString(_ child: Mirror.Child) -> String? {
guard let label = child.label else {
return nil
}
return "\(label): \(child.value)"
}
var reflected: String {
return Mirror(reflecting: self).children.flatMap(toString).joined(separator: "\n")
}
}
Swift3.0で Any が変わったのもあって、 extension 時の束縛で where Self: Any
と書けるようになって、関数の中で self
が使えるようになったのが地味にいい感じです。
extension の名前悩みましたが一旦 Reflectable にしました。(前そんな名前Swiftの標準であったような…)
使用例
使用したいclassやstuct、enumに Reflectable を適合させて、 reflected
を呼び出すだけです。
また、下記の Fuga のように、 CustomStringConvertible を適合させて、
var description: String の中で reflected
を呼び出せば、 print
したときに、自分自身の情報をそのまま出力することができます。
struct Hoge: Reflectable {
let a: Int
let b: String
let c: [Int]
let ddd: (String, Int)
let e: Fuga?
}
struct Fuga: CustomStringConvertible, Reflectable {
let x: Double
var description: String {
return "@\(reflected)@"
}
}
let hoge: Hoge = Hoge(a: 100, b: "test", c: [100, 200, 300], ddd: ("Hoge", 123), e: Fuga(x: 3.14))
print(hoge.reflected)
// => "a: 100\\nb: test\\nc: [100, 200, 300]\\nddd: (\\"Hoge\\", 123)\\ne: Optional(@x: 3.14@)"
これでデバッグ中に変数の中身とかを出す時は助かりますね。
さらに発展させる(filterできるように)
全て表示されるのは…というのがあるので、
特定のものだけ出力したい 、 特定のものは除外したい という場合は、以下のように書き換えます。
protocol Reflectable {
}
extension Reflectable where Self: Any {
private func toString(_ child: (String, Any)) -> String {
return "\(child.0): \(child.1)"
}
private var list: [(String, Any)] {
return Mirror(reflecting: self).children.flatMap { $0.label != nil ? ($0.label!, $0.value) : nil }
}
var reflected: String {
return list.map(toString).joined(separator: "\n")
}
func reflected(includes: [String]) -> String {
return list.filter { includes.contains($0.0) }.map(toString).joined(separator: "\n")
}
func reflected(excludes: [String]) -> String {
return list.filter { !excludes.contains($0.0) }.map(toString).joined(separator: "\n")
}
}
これで、
let hoge: Hoge? = Hoge(a: 100, b: "test", c: [100, 200, 300], ddd: ("Hoge", 123), e: Fuga(x: 3.14))
print(hoge?.reflected)
// => "a: 100\nb: test\nc: [100, 200, 300]\nddd: ("Hoge", 123)\ne: Optional(@x: 3.14@)"
print(hoge?.reflected(includes: ["a", "b"]))
// => "a: 100\nb: test"
print(hoge?.reflected(excludes: ["a"]))
// => "b: test\nc: [100, 200, 300]\nddd: ("Hoge", 123)\ne: Optional(@x: 3.14@)"
といった感じで出力できるようになります。
どうしてもkey部を文字列でハードコーディングする必要がありますが、そこまで苦にはならないかと…!
これでより扱いやすくなるかと思います。