概要
@dynamicMemberLookup
を使ってSwiftでHTML/XMLを生成するDSLを作ります。(作りました)
#SwiftPM
SwiftPMの形で配布しています。
#コード例
HTML
以下のような形でSwiftを記述します。
MarkUp()
.html[MarkUp()
.body[MarkUp()
.table[MarkUp()
.tr[MarkUp()
.td[character: "one"]
.td[character: "two"]
]
]
]
].generate()
上記のような構造でSwiftのコードを書くと、以下のような形でHTMLなテキストを生成します。
<html>
<body>
<table>
<tr>
<td>one</td>
<td>two</td>
</tr>
</table>
</body>
</html>
XML
同様にXMLも生成できます。
レシピ
と 手順
をドットアクセスでつないでいきます。
MarkUp(doctype: #"<?xml version="1.0" encoding="UTF-8"?>"#)
.レシピ[MarkUp()
.手順[character: "全ての材料を一緒にして混ぜます。"]
.手順[character: "オーブンに入れて温度を180℃にして30分間焼きます。"]
]
.generate()
こうすると以下のような形でXMLを生成します。
<?xml version="1.0" encoding="UTF-8"?>
<レシピ>
<手順>全ての材料を一緒にして混ぜます。</手順>
<手順>オーブンに入れて温度を180℃にして30分間焼きます。</手順>
</レシピ>
#仕組み
これらのタグの生成には、 @dynamicMemberLookup
を使っています。
dynamicMemberLookup
dynamicMemberLookupは、Swift 4.2より追加された機能です。
@dynamicMemberLookup
が付与されると、まるでプロパディが実装されているかのようにドットでアクセスできて、内部ではsubscript(dynamicMember:)
の部分でドットアクセスに対する処理を書いていきます。
上記コードの場合、.html
や.body
、.レシピ
などのアクセスで subscript(dynamicMember:)
が動き、タグを生成する処理を行っています。
public subscript(dynamicMember member: String) -> MarkUp {
tagName(member)
}
public func tagName(_ tagName: String) -> MarkUp {
queue.append(self)
return MarkUp(tag: tagName,queue: queue)
}