これは Swift Advent Calendar 2018 24日目の記事です。
昨日23日目は @uounɹɐʇの「Conditional Conformanceで遊ぼう」でした。
前説
先日福岡で行われた 第5回 HAKATA.swift ~福岡でSwiftの勉強会~ でLTとして話した内容です。
github.com
READMEの冒頭には
SwiftSyntax is a set of Swift bindings for the libSyntax library. It allows for Swift tools to parse, inspect, generate, and transform Swift source code.
とあり、Swiftの
に用いることができ、所謂、メタプログラミングに使用できます。
メタプログラミングについてはこちらがおすすめ
speakerdeck.com
お題
iOSで色を扱う場合にお馴染み、UIKitデフォルトAPIとして提供されるイニシャライザ
UIColor.init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)
ですが、これには以下のような不満点があります。
- RGB1~255表現を0~1の少数値に変換して表現するため、直感的ではない
- 特にデザイナーはHexColor (16進トリプレット) で知りたい
ということで、swift-syntaxをを用いて、コメントでHexColorを入れてみようと思います。
想定はこんなコード
Contents.swift
import UIKit
let string = "ABCDE"
let color1 = UIColor(red: 0.55, green: 0.0, blue: 0.0, alpha: 1.0)
let array = [1, 2, 3, 4, 5]
let color2 = UIColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0)
let dictionary = ["foo": 1,
"bar": 2,
"baz": 3]
let color3 = UIColor.init(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0)
用意したツールは こちら
github.com
$ swift run swift-color-detector help
dump \(path): Dump code
rewrite \(path): Add HexColor Comments and Save
まずは使ってみる
dump
これをdumpしてみると
$ swift run swift-color-detector dump Contents.swift
import UIKit
let string = "ABCDE"
let color1 = UIColor(red: 0.55, green: 0.0, blue: 0.0, alpha: 1.0)
let array = [1, 2, 3, 4, 5]
let color2 = UIColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0)
let dictionary = ["foo": 1,
"bar": 2,
"baz": 3]
let color3 = UIColor.init(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0)
と出力されます。
$ swift run swift-color-detector rewrite Contents.swift
とすると、与えたpathのコードの書き換えも行います。
swift-syntaxを扱う主要なコード
今回のサンプルでswift-syntaxをメインで扱う部分は、以下ファイルの60行強で実現されています。
swift-color-detector/ColorSyntaxRewriter.swift at master · yutailang0119/swift-color-detector · GitHub
段階としては
ColorSyntaxRewriter
ColorInitializerSyntaxRewriter
UIColor.init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)
を検査
ColorLiteralSyntaxRewriter
という3ステップで実現しています。
まとめ
いかがでしたでしょうか?
簡単に扱えそうな気がしてきませんか?
そもそも自分もメタプログラミングは挑戦を始めたばかりで、 apple/swift-syntax
も手探りなため、最適解とは限りません。
ぜひ、アドバイスをもらえるとありがたいです。