Swift で Markdown をレンダリングしたい時どうしたか

[Swift その2 Advent Calendar 2016](http://qiita.com/advent-calendar/2016/swift2) 9日目です！

Swift と言いつつ iOS と JavaScript の話がメインになってしまいました :pray: :bow:

# ピーちゃん

ピーちゃんという、 esa.io の iOS クライアントアプリを作っていて、そこで Markdown をアプリ上で表示しています。
その時どうやって Markdown をレンダリングしていったかやり方を書いていきます。

* AppStore: [ピーちゃん](https://itunes.apple.com/jp/app/pichan-unofficial-esa.io-client./id1107328970?mt=8)
* GitHub: [starhoshi/pi\-chan2](https://github.com/starhoshi/pi-chan2)

# Swift で Markdown

Swift で Markdown をレンダリングしたい時は以下の方法があるかと思います。

1. Markdown を自力で Parse して、記法に応じたフォントサイズや太さなどを設定し表示
2. [SimonFairbairn/SwiftyMarkdown](https://github.com/SimonFairbairn/SwiftyMarkdown) などライブラリを使用
3. WebView で JavaScript を使い表示

1 は厳しいというか面倒、2 も何か制限がありあそうだしこれ使っておけば安心というデファクトスタンダードというほどではない。
3 は JS の Markdown ライブラリも豊富にあり良さそう、ということで WebView に Markdown を渡す方式にしました。

## WebView で Markdown を表示

1. Markdown をレンダリングするための html/js をローカルに用意する
2. WebView がその html を読み込む
3. html の読み込みが完了したら Markdown を js に渡す
4. js が Markdown を解釈しレンダリングする

の手順で読み込んでいきます。
ここでは UIWebView を使っていますが、良い子は WKWebView を使ってください。

### 1. Markdown をレンダリングするための html/js をローカルに用意する

#### 各ライブラリの取得

Markdown をレンダリングする js ライブラリとして [marked.js](https://github.com/chjj/marked)、 css ライブラリとして [github\-markdown\-css](https://github.com/sindresorhus/github-markdown-css) を利用します。
せっかくなので emoji も表示したいですね :sushi: 。では [emojify](https://github.com/Ranks/emojify.js) の js と css もお借りしましょう。

上記の js / css をダウンロードしフォルダに配置します。
また、それらのライブラリを利用する本体の `md.html` も作成してしまいましょう。

そうすると、 Xcode にはこのように表示されているはずです。

![unspecified.png](https://qiita-image-store.s3.amazonaws.com/0/5678/4d4f2394-67b9-3ec8-892e-2ff23e20feae.png)

#### md.html でライブラリを読み込む

上記で設置した md.html で 各ライブラリを読み込んでいきます。

```html
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script type="text/javascript" src="./emojify.min.js"></script>
  <link rel="stylesheet" href="./emojify.min.css">
  <script type="text/javascript" src="./marked.js"></script>
  <link rel="stylesheet" href="./github-markdown.css">
  <script>
  function insert(html) {
    marked.setOptions({
      gfm: true,
      tables: true,
      breaks: true
    });
    document.getElementById('content').innerHTML = marked(html);
    emojify.setConfig({img_dir: 'https://github.global.ssl.fastly.net/images/icons/emoji/' });
    emojify.run(document.getElementById('content'));
  }
  </script>
</head>

<body>
  <article class="markdown-body">
    <div id="content" />
  </article>
</body>

</html>
```

各ライブラリの読み込みと `function insert(html)` で Markdown を Swift 側から受け取る準備をしています。

ここら辺は marked.js や emojify の使い方になってくるので、詳しくは各ドキュメントを参照ください。


### 2. WebView がその html を読み込む

ViewController の viewDidLoad などで html を読み込みます。

```swift
    override func viewDidLoad() {
        super.viewDidLoad()
        let url = Bundle().url(forResource: "md", withExtension: "html")
        let req = URLRequest(url: url)
        webView.loadRequest(req)
    }
```

### 3. html の読み込みが完了したら Markdown を js に渡す


```swift
    func webViewDidFinishLoad(_ webView: UIWebView) {
        setMarkdown()
    }

    func setMarkdown() {
        let markdown = "# h1\\n ## h2 \\n * list\\n    * :)"
        let js = "insert('\(markdown)');"
        self.webView.stringByEvaluatingJavaScript(from: js)
    }
```

webView の `stringByEvaluatingJavaScript` を使い、先ほど用意した `function insert(html)` で js を実行しています。

ここで少し注意が必要で、 Swift 側で html の読み込みが完了する前に js を実行してしまうと js の実行が失敗してしまうので、 `webViewDidFinishLoad` が呼ばれるまで待ちます。

### 4. js が Markdown を解釈しレンダリングする

無事レンダリングされました :relaxed: 

<img width="375" alt="2016-12-10 16.32.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/5678/2f089786-0405-7a58-f51d-a6b32aeafbd1.png">


# おわり

WebView を使い Markdown をレンダリングする一通りの流れを記載しました。 :blush: 

あまり Swift Advent Clendar とは関係ない話になってしまいましたが :bow: 誰かの役に立てればと思います :muscle: