SwiftUIっぽくHTMLを作成するライブラリを作ってみた


例えば、

```html
<ul>
  <li>ベーグル</li>
  <li>バゲット
    <ul>
      <li>パリジャン</li>
      <li>バタール</li>
    </ul>
  </li>
</ul>
```

のようなHTMLを、

```swift
ul.children {
    li.children { "ベーグル" }
    li.children {
        "バゲット"
        ul.children {
            li.children { "パリジャン" }
            li.children { "バタール" }
        }
    }
}
```

のようなSwiftで書けるライブラリを作成したのでご紹介です。

# Swift Package Manager

`MarkUpDynamic` としてSwift Package Managerで配布しています。

https://github.com/coe/MarkUpDynamic

# 使い方

## MarkUp
MarkUpは、マークアップ文書を作成する際に最初に使うものになります。
MarkUpは`dynamicMemberLookup`を利用しているので、後述するElementをドットアクセスで生成することができます。

```swift
let m = MarkUp()

let ul: Element = m.ul
let li: Element = m.li
```

もし、ドットアクセスでは使えない、ハイフンなどを含むElementを使いたい場合は直接dynamicMemberを使ってElementの生成も可能です。

```swift
let m = MarkUp()

let ul = m[dynamicMember: "ul"]
let li = m[dynamicMember: "li"]
```

## Element

Elementは、ひと組のタグを表しています。

### Attributes
Elementは`dynamicCallable`に対応しているので、パラメータを割り当ててAttributeを含めることが可能です。

```swift
let m = MarkUp()
m.li(fuga: "hoge").children { "ベーグル" }.toString()
```

```html
<li fuga="hoge">ベーグル</li>
```

ハイフンつきattribute名を付けたい場合は直接dynamicallyCallを使います。

```swift
let m = MarkUp()
m.form.dynamicallyCall(withKeywordArguments: ["accept-charset": "UTF-8"]).toString()
```

```html
<form accept-charset="UTF-8"></form>
```

### children
Elementの中に含めたいコンテンツを`children`のメソッドで定義します。
childrenは`resultBuilder`に対応しているので、SwiftUIのように複数のContentを指定できます。
Contentは、Elementの他に、Stringも含まれます。


```swift
        let m = MarkUp()
        let ul: Element = m.ul
        let li: Element = m.li(attribute: "hoge")
        
        let htmlString = ul.children {
            "パンの種類"
            li.children { "ベーグル" }
            li.children { "バゲット" }
            li.children { "サンドイッチ" }
        }
        .toString()
```

```html
<ul>パンの種類
  <li attribute="hoge">ベーグル</li>
  <li attribute="hoge">バゲット</li>
  <li attribute="hoge">サンドイッチ</li>
</ul>
```

### 閉じタグの省略

例えばHTMLにおけるmetaタグのような、閉じタグを省略しなければならない場合、`doNotSpecifyEndTag()`を指定します。

```swift
        let m = MarkUp()
        let html = m.html
        let head = m.head
        let meta = m.meta.doNotSpecifyEndTag()

        let htmlString = html.children {
            head.children {
                meta(charset: "UTF-8")
                meta(name: "description",
                       content: "Free Web tutorials")
                meta(name: "keywords",
                       content: "HTML, CSS, JavaScript")
                meta(name: "author",
                       content: "John Doe")
            }
        }
        .toString()
```

```html
<html>
<head>
  <meta charset="UTF-8">
  <meta name="description" content="Free Web tutorials">
  <meta name="keywords" content="HTML, CSS, JavaScript">
  <meta name="author" content="John Doe">
</head>
</html>
```

# 使ってみる

## HTML

```swift
        let m = MarkUp()
        let html = m.html
        let head = m.head
        let meta = m.meta.doNotSpecifyEndTag()
        let body = m.body
        let p = m.p
        
        let htmlString = html.children {
            head.children {
                meta(charset: "UTF-8")
                meta(name: "description",
                       content: "Free Web tutorials")
                meta(name: "keywords",
                       content: "HTML, CSS, JavaScript")
                meta(name: "author",
                       content: "John Doe")
            }
            body.children {
                p.children {
                    "All meta information goes inside the head section."
                }
            }
        }
        .toString()
```


```html
<html>
<head>
  <meta charset="UTF-8">
  <meta name="description" content="Free Web tutorials">
  <meta name="keywords" content="HTML, CSS, JavaScript">
  <meta name="author" content="John Doe">
</head>
<body>
  <p>All meta information goes inside the head section.</p>
</body>
</html>
```

## XML
同じくマークアップ言語のXMLも生成できます。
ドットアクセスなどで日本語も使えて面白いです。

```swift
        let m = MarkUp()
        
        let htmlString = m.書籍目録.children {
            m.書名(出版日: "2007-10-31").children { "XML入門" }
            m.著者.children { "筒井" }
            m.書名(出版日: "2017-10-31").children { "続・XML入門" }
            m.著者.children { "小松" }
        }
        .toString()
```

```xml
<書籍目録>
  <書名 出版日="2007-10-31">XML入門</書名>
  <著者>筒井</著者>
  <書名 出版日="2017-10-31">続・XML入門</書名>
  <著者>小松</著者>
</書籍目録>
```

# まとめ
つかおう！resultBuilder！