Facebookアプリからのディープリンク【iOS編】

# はじめに

皆さんのアプリにはディープリンク組み込んでいますか？
私自身スマホアプリ開発に転向するまで「聞いたことがある」ぐらいで正直中身よくわかってなかったです。
で、そんな曖昧な状態からディープリンクにチャレンジしたんですが、結構苦戦したので忘却録として残そうと思います。

# そもそもディープリンクとは？

端的にいうと<font color="red">**webページなどから特定のリンクを踏んだら自アプリ内の特定画面に誘導する**</font>仕組みのことです。
実現方法が色々あってiOS/Androidそれぞれあります。

iOS

- Custom URL Scheme
- Universal Links

とか

Android

- Deeplink
- App link

とか

詳細な情報はネットに転がってますのでここでは割愛しますm(_ _)m

ただ、ここで注意して欲しいのは<font color="red">**その他のアプリから自分のアプリへのリンクは保証されない**</font>のです！

# Facebookアプリからのディープリンク

さてここからが本題です。
ブラウザからの遷移は全てうまくいくのですがFacebookアプリからの遷移は全くこれっぽっちも動きません💢

Facebookの場合は**アプリ内ブラウザ**でゴニョゴニョやっているらしく対策打たないと機能してくれないんですね…

そこで、まず見つけたのが[Facebook App Links](https://developers.facebook.com/docs/applinks/)です。
本家が紹介している機能ですので、一番最初に試すべきでしょう。


### [App Links on iOS](https://developers.facebook.com/docs/applinks/ios)

iOSの場合についてGoogle先生に手伝ってもらいながら理解しました。

要約するとディープリンクのページにmeta情報を含んでくれれば[Custom URL Scheme]を動かすよ〜
ってことだと思います。
雑なイメージ図ですが多分こんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325497/bc6bfd1f-ebf0-fb4b-0792-ab32745823e8.png)
①〜③をFacebookアプリがやってくれます。


#### サーバ側
まずサーバ側にhtmlを用意します。
htmlファイルの名前はなんでも良くて[https://hogehoge.com]
にアクセスがきたら用意したhtmlに飛ぶようにサーバ側で設定します。
apacheならRewriteで行けました。

そして、サーバ側に用意したhtmlのヘッダにmetaデータを仕込みます。
※パラメータの解説は[[App Linksの技術文書を和訳してみました](https://www.eisbahn.jp/yoichiro/2014/05/app_links.html)]が参考になりました。

色々試しましたが最低限必要なのは

```html
<html>
<head>
  <meta property="al:ios:url" content="hogehoge-app://" />
  <meta property="al:ios:app_name" content="hogehoge" />
  <meta property="al:web:should_redirect" content="false" />
</head>
<body>
hogehoge app.</br>
</body>
</html>
```
でした。基本的にサーバ側はこれだけでOK。
ただ、アプリ未インストールならストアにとか誘導したい場合はこのhtmlに簡単なスクリプト仕込めばできるのでやっておいていいかもです。

```html
<html>
<head>
  <meta property="al:ios:url" content="hogehoge-app://" />
  <meta property="al:ios:app_name" content="hogehoge" />
  <meta property="al:web:should_redirect" content="false" />
  <meta property="al:ios:app_store_id" content="自分のアプリID" />
  <script>
    var ANDROID_PACKAGE = '自分のアプリのPlayStoreへのリンク';
    var IOS_LINK = '自分のアプリのAppStoreへのリンク?mt=8'; //mt=8の説明は割愛しますm(_ _)m
    var ua = navigator.userAgent.toLowerCase();
    if (ua.search(/iphone|ipad|ipod/) > -1) {
        window.location.replace(IOS_LINK);
    } else if (ua.search(/android/) > -1) {
        window.location.replace('intent://#Intent;package=' + ANDROID_PACKAGE + ';end');
    }

    function closeBrowser (event) {
        window.close();
    }

    if ('visibilityState' in document) {
        document.addEventListener('visibilitychange', closeBrowser, false);
    } else if ('onfocus' in this) {
        addEventListener('focus', closeBrowser, false);
    }
  </script>
</head>
<body>
hogehoge app.</br>
</body>
</html>
```
自分のiOSアプリのストアリンクは[Link Maker](https://linkmaker.itunes.apple.com/ja-jp)で確認できます。
自分のAndroidは[Play Store](https://play.google.com/store/apps)で探しましょう。

#### アプリ側
まずはInfo.plistにURL Schemesを定義します。
今回のサンプルは「hogehoge-app」ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325497/b53acc56-6bd4-5ab7-fb9d-ced2302b11a5.png)
上で書いたhtmlの

```html
<meta property="al:ios:url" content="hogehoge-app://" />
````
ここと一致するようにしましょう。


FacebookアプリがCustom URL Schemeを投げてくれるので自分のアプリ側で受け取ります。
受け取れるデータはこんな感じで

```json
"hogehoge-app://?al_applink_data={
    \"target_url\":\"https:\\/\\/hogehoge.com\\/top\",
    \"extras\":{
        \"fb_app_id\":999999999999999},
        \"referer_app_link\":{\"url\":\"fb:\\/\\/\\/?app_id=999999999999999\",
        \"app_name\":\"Facebook\"
    }
}"
```
「\\」バックスラッシュがうざいですwww
公式にあるBoltsSDK使えば簡単に取れるみたいなこと書いてましたが、それだけのためにSDK入れるのやだなと思ったので強引にreplaceしてます。
（注）サンプルなのでAppDelegateに直接書いてますが別ファイルに処理は切り出した方がいいでしょう。

```swift

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
        let appLinkDataKey = "al_applink_data"
        let appLinkUrlKey = "target_url"
        if let urlParameter = url.urlParameters.filter({ $0.key == appLinkDataKey}).first {
            let json = JSON(parseJSON: urlParameter.value)
            if let targetUrl = json[appLinkUrlKey].string, let url = URL(string: sanitize(urlString: targetUrl)) {
                // TODO: ここに画面遷移の処理を書く！
            }
        }
        return true
    }

    private func sanitize(urlString: String) -> String {
        if let range = urlString.range(of: "\\") {
            let replacedString = urlString.replacingCharacters(in: range, with: "")
            return sanitize(urlString: replacedString)
        }
        return urlString
    }

```

あと、公式サイトには戻る操作のリンクもあるって書いてましたが見当たらなかったので特に制御しないことにしました。

これでiOSは無事Facebookアプリからディープリンクできるようになってるはずです！

#### 補足
ただ、<font color="red">Custom url scheme は廃止予定？</font>とのことですので、いつかは動かなくなる日が近い気がします。
現在[2020/06/12時点]ではiOS13以上でも動いてます。
おそらくですがアプリの実装をAppDelegateではなくSceneDelegateで実装することで同じことができるのではと思います。
[カスタムURLスキーム| ディープリンクiOS 13以降のSwift 5](https://www.swiftdevcenter.com/custom-url-scheme-deep-link-ios-13-and-later-swift-5/)

### OGPキャッシュについて
いきなりですがOGP情報ってご存知ですか？

OGP情報とはFacebookでシェアされたとき、キャッチ画像で、どんなタイトルで、どんなURLでシェアされるかを指定するプロトコル(Open Graph protocol)とのことです。
（SNSで拡散を通じて、そのページを多くの人に見てもらうためには必須らしいです）
↓の画像の部分
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325497/b509e632-7d62-e922-61d3-670ce48029c1.png)

実はiOSのFacebookアプリはこの情報をキャッシュして使用していますので、サーバ側の情報が書き換わったとしてもすぐに反映されません。そのことを知らずにリンクが動かなくなってかなり焦りました…

で、そのキャッシュを削除する方法（情報を最新にする方法）ですが確実なのはグラフAPIエクスプローラからpostでAPIを叩くことでした。
（シェアデバッガーでも削除できるみたいなこと書いてましたが私がやった時は更新されなかったです。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325497/6acb7d2e-e962-d36f-0672-d85b9f99ee91.png)
※そんなサイト無い（hogehoge.com）ので"404 Not Found"になってます。
※グラフAPIエクスプローラでAPIを叩くには<font color="red">**Facebook開発者アカウント**</font>が必要です。



# 終わりに
以上、FacebookアプリからのディープリンクiOS編でした。
使用する機会があるかわかりませんが、以外に記事が少ないのでお役に立てると幸いです。
Androidはもっと苦戦してまして、後日記事にまとめたいと思います。


# 参考
大変参考になりました。ありがとうございます🙇‍♂️

- [URLスキーム・独自ディープリンク実装に代わる、Universal Links(iOS 9で導入)でより良いUXを実現](https://qiita.com/mono0926/items/2bf651246714f20df626)
- [App Linksの技術文書を和訳してみました](https://www.eisbahn.jp/yoichiro/2014/05/app_links.html)
- [FacebookのOGPキャッシュをクリアするデバッガー](https://www.tmseek.jp/fbcashcl)
- [カスタムURLスキーム| ディープリンクiOS 13以降のSwift 5](https://www.swiftdevcenter.com/custom-url-scheme-deep-link-ios-13-and-later-swift-5/)