iOS の Twitter シェア簡易実装 2019

iOSのアプリで、Twitter シェア機能をつけてくれという話があり、やり方と状況を調べたのでメモ。

## Twitter シェアとは

まずイメージしたのはこういうやつである。ネイティブの投稿ダイアログが出てきて、TwitterやFacebookに投稿できる。

<img src="https://qiita-image-store.s3.amazonaws.com/0/49010/ef7509e3-c61c-8473-d935-cc79321280a4.png" width=300>

かつては簡単に実装できたのだが、現在は状況が異なっている。おそらく、ライブラリを使って、簡単に2,3行で実装できるという状況ではなくなっている。

## 2019年1月現在のTwitterシェア用ライブラリの状況

かつてはと前置いたが、以前はこういう機能を提供するライブラリがあった。

* Social.framework
  * ネイティブの画面でTwitterでシェアするダイアログをポップアップで出す便利なもの
  * OS 側で SNSアカウントの情報を持たなくなったiOS11 (2017年9月11日リリース) からは使用できない
* TwitterKit (〜2018年10月30日)
  * Twitter公式製のライブラリで、 Social.framework が廃止される際には移行ガイドが公開されたりしていた [^1]
  * 2018年10月30日にサポートを終了 [^2]

Social.framework は iOS10以下の端末であれば以前として使えるのだが、 2019年1月1日現在 iOS10 以下の端末はわずか8% との調査もあり、現実的ではない[^3]。 

TwitterKit に関しては Apache License でコードが公開され続けているため、fork して使ったりすることはできるが、今後の Twitter の仕様変更に追随していけるかどうかの保証はない。

以上のような状況のため、体験は異なるが、アプリからの Twitter シェアは以下のような方法で対応せざるを得ない状況なのではないかと思われる。

* Universal Links を使う
* UIActivityViewController を使う

もしくは独自に実装したいときは、色々やることはあるが [Twitter API](https://developer.twitter.com/en/docs/api-reference-index) を直接使うか、[SwifterKit](https://github.com/mattdonnelly/Swifter) というOSSがあるらしいのでそれなどを使って画面込みで実装することになりそう。

## Universal Links を使う

こうすると、Twitterアプリが入っている場合はTwitterアプリの投稿画面が開き、入っていない場合はSafariが開く。

これをやるだけなら非常に簡単で、 `https://twitter.com/intent/tweet` を `UIApplication.shared.open()` するだけである。

```swift
let text = "何かデフォルトで欄に入っててほしい文字列"
let encodedText = text.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
if let encodedText = encodedText,
    let url = URL(string: "https://twitter.com/intent/tweet?text=\(encodedText)") {
    UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
```

`?text=` 以下に文字列を指定することで、デフォルトで投稿欄に入っていて欲しい文字列を指定することができる。画像は設定できない。

空白文字や日本語などに対応するため、ほとんどの場合 `text.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)` でパーセントエンコーディング方式の文字列に変換しておく必要がある。

## UIActivityViewController を使う

シェアボタンを押すとよく下から出てくるやつである。

<img src="https://qiita-image-store.s3.amazonaws.com/0/49010/abe829dd-38e1-48f3-4c2c-7d7da4e0637f.png" width=300>

これも細かいことを考えなければ簡単で、 `UIActivityViewController` のインスタンスをシェアしてほしいアイテムを初期値として生成し、present するだけである。この方法であれば画像も設定できる。

```swift
let text = "何かデフォルトで欄に入っててほしい文字列"
let image: UIImage = #imageLiteral(resourceName: "画像リソース名")
let shareItems = [image, text] as [Any]
let controller = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
present(controller, animated: true, completion: nil)
```

ただし、Twitter へのシェアに制限したいとしても、 Twitterアプリが入っていない場合は Twitter は選択肢に出てこない。

さらには Save Image を押されたときに写真アプリへの権限を取っていないとアプリが落ちるだとか、 Facebook ではテキストを渡せないだとか色々罠があるらしいので、ちゃんと実装したい場合は注意したほうが良さそうである。 [^4]

## まとめ

* ネイティブでTwitter への投稿画面をライブラリを使って、簡単に実装できる状況が、以前はあったが今はないかもしれない
* 簡易的に、以下の方法を使うことはできそう
  * Universal Links を使う
      * Twitter アプリが入ってたらアプリ、入ってなかったらSafariが開くやつ
      * デフォルト画像はセットできない
  * UIActivityViewController を使う
      * 色々なアプリに共有するためのモーダル
      * ただし、Twitter以外にもシェアできるようになってしまうため、場合によっては共有先によってハンドリングが必要

ほかに方法あるよとかあればコメント欄などで教えてください :pray:

[^1]: Migrating from iOS Social Framework https://github.com/twitter/twitter-kit-ios/wiki/Migrating-from-iOS-Social-Framework

[^2]: Twitter Kit SDKのサポート終了のお知らせ https://blog.twitter.com/developer/ja_jp/topics/community/2018/TKSDK_tr.html

[^3]: 【随時更新】iOS バージョン別シェアを発表。日本国内のシェア・全体の推移は？ https://moduleapps.com/mobile-marketing/ios-ver/

[^4]: UIActivityViewControllerには罠がある https://blog.sorausagi.org/2017/10/uiactivityviewcontroller.html