Publishで記事にツイートボタンを表示する方法(Swift)

「PublishでWebサイトを構築する」は6部構成です。
記事を順番に読み進めると、PublishでWebサイトが構築できるようになります。

第1部: [セットアップ&使い方](https://qiita.com/uhooi/items/57e040df2f4aa7f62d89)
第2部: [カスタムテーマの作成](https://qiita.com/uhooi/items/111d4439273775586149)
第3部: [プラグインのインストール](https://qiita.com/uhooi/items/bf0ff67d687b53a11c5b)
第4部: プラグインの作成（未投稿）
第5部: ツイートボタンの表示　__←イマココ__
第6部: [はてなブックマークボタンの表示](https://qiita.com/uhooi/items/40cb71ac3aca57853544)

## はじめに

Publishで記事にツイートボタンを表示する方法を紹介します。

## 環境

- OS：macOS Monterey 12.2.1
- Xcode：13.3 (13E113)
- Swift：5.6
- Publish：0.8.0

## ツイートボタンのHTMLの把握

まずツイートボタンのHTMLを把握し、生成後のイメージを掴みます。
詳細は以下の記事をご参照ください。

https://qiita.com/uhooi/items/9053b5c9b975d0591a61

## 実装

### URLエンコード処理の実装

`String` 型をURLエンコードする処理を実装します。
私は以下の記事の内容をほぼそのまま使わせていただきました。

https://dev.classmethod.jp/articles/urlencode-spec-and-implementation-for-swift/

```swift:String+URL.swift
import Foundation

extension String {
    func urlEncoded() -> String {
        let allowedChars = CharacterSet.alphanumerics.union(.init(charactersIn: "/?-._~"))
        let decoded = removingPercentEncoding ?? self
        return decoded.addingPercentEncoding(withAllowedCharacters: allowedChars) ?? decoded
    }
}
```

`"{文字列}".urlEncoded()` で文字列をURLエンコードできるようになりました。

```swift
"iOS Osushi🍣".urlEncoded() // "iOS%20Osushi%F0%9F%8D%A3"
```

クエリストリングに渡す文字列はすべてURLエンコードすべきです。

### HTML構築処理の更新

テーマのHTML構築処理を更新し、ツイートボタンを配置します。

```diff_swift:Theme+IosOsushi.swift
private struct IosOsushiHTMLFactory<Site: Website>: HTMLFactory {
    func makeItemHTML(for item: Item<Site>, context: PublishingContext<Site>) throws -> HTML {
        HTML(
            .lang(context.site.language),
            .head(for: item, on: context.site),
            .body(
+               .script(.async(), .src("https://platform.twitter.com/widgets.js")),
                .class("item-page"),
                .components {
                    SiteHeader(context: context, selectedSelectionID: item.sectionID)
                    Wrapper {
                        Article {
+                           Div {
+                               TweetButton(item: item, site: context.site)
+                           }
+                           .class("share-buttons")
                            Div(item.content.body).class("content")
                            Span("Tagged with: ")
                            ItemTagList(item: item, site: context.site)
                        }
                    }
                    SiteFooter()
                }
            )
        )
    }

+   private struct TweetButton<Site: Website>: Component {
+       private var tweetText: String { "\(item.title) | \(site.name)" }
+       private var urlString: String { site.url.absoluteString + item.path.absoluteString }
+       private let hashtag = "ios_osushi"
+       private let username = "ios_osushi"
+
+       var item: Item<Site>
+       var site: Site
+       var body: Component {
+           Link("ツイート", url: "https://twitter.com/intent/tweet?text=\(tweetText.urlEncoded())&url=\(urlString.urlEncoded())&hashtags=\(hashtag.urlEncoded())&via=\(username.urlEncoded())")
+               .class("twitter-share-button")
+       }
+   }
}
```

ボタンの下にマージンを付けます。
CSSに詳しくないので、 `class` の命名や、そもそも `class` にマージンを付けるのがいいのかなどがわかりません。
他にいい方法があれば教えていただけると嬉しいです。

```diff_css:styles.css
+ .share-buttons {
+     margin-bottom: 10px;
+ }
```

JSの読み込みは `.body()` 以外の箇所に仕込む方法がわからなかったため、 `<body>...</body>` で行っています。

今回の `TweetButton` のように、要素のまとまりは `Component` を継承した構造体で表現すると可読性が向上します。
`body` に要素のまとまりを記述します。

ツイートのテキストやURL、ハッシュタグ、ユーザー名は、必要に応じて変更してください。

`publish-cli run` を実行し、記事の上部にツイートボタンが表示されたらOKです。
![スクリーンショット 2022-04-06 11.13.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/b4143d27-a4ba-0934-dec6-cf48bc4875d8.png)

## おわりに

これで記事にツイートボタンを表示できました！

## 参考リンク

- [ツイートボタンを設置する by uhooi · Pull Request #24 · ios-osushi/website](https://github.com/ios-osushi/website/pull/24)