1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【iOS】DynamicLinksのURLが正しいはずなのにhandleUniversalLinkでtrueが返らずにハマった

Posted at

背景

仕事でDynamicLinksを用いてアプリを起動しようとしたら、パラメータは合っているはずなのになぜかパラメータをうまく受け取れなくてハマった...

今回はそのときどのような確認をして、どう解決したのかを備忘として残します。

Firebase Dynamic Linksとは

そもそもの説明を簡単にします。

例えば、Webでサービスを利用しているユーザにアプリ利用を促進させたいケースがあります。
その場合に利用されるのがFirebase Dynamic Linksです。

DynamicLinksはアプリのインストールの有無にかかわらず、複数のプラットフォームで機能するリンクです。DynamicLinksを開くと、ネイティブアプリのリンク先のコンテンツに直接移動します。

インストール済みであれば、アプリが起動してパラメータをもとに処理を定義することができます。
インストールしていなければ、iOSの場合はApple Storeのアプリページへの導線まで飛び、アプリのインストール・利用を促せます。

このDynamicLinksはFirebaseコンソールの方から簡単に設定できますし、バックエンドの方でリンクを動的に設定することもできます。
また、OS向けのパラメータも設定でき様々な設定ができます。

iOS向けパラメータの例

パラメータ 説明
ibi リンクを開くために利用するアプリのバンドルID jp.co.hogehoge
imv リンクを開けるアプリの最小バージョンの番号 1.0.0

今回のケースでは、後者のバックエンド側でリンクを作成する方法が該当します。

SDK内部の処理を追ってみた

Firebaseの公式ドキュメントでもあるように、アプリがインストール済みの場合にユニバーサルリンクとして受信されるリンクの処理があります。

AppDelegate.swiftあたり
func application(_ application: UIApplication, continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
  let handled = DynamicLinks.dynamicLinks()
    .handleUniversalLink(userActivity.webpageURL!) { dynamiclink, error in
      // ...
    }

  return handled
}

しかし、DynamicLinksに設定するパラメータは正しいし、DynamicLinks内のディープリンクも間違えてはいないのに(本当に)、いくら試してもhandledfalseしか返ってこず、DynamicLinksを受け取った時の処理が再現されない。。😢

そこでSDK内部の処理まで追ってみることに。

まずは、呼び出しているhandleUniversalLinkで何をしているのかを見てみました。
すると、canHandleUniversalLinkという定数がfalseを返していることがわかり、深ぼってみるとcanParseUniversalLinkURLfalseになっていることに気づきました。

FIRDynamicLinks.m
- (BOOL)handleUniversalLink:(NSURL *)universalLinkURL
                 completion:(FIRDynamicLinkUniversalLinkHandler)completion {
  if ([self matchesShortLinkFormat:universalLinkURL]) {
    __weak __typeof__(self) weakSelf = self;
    [self resolveShortLink:universalLinkURL
                completion:^(NSURL *url, NSError *error) {
    // 今回ここは関連しなかったので省略
    return YES;
  } else {
    [self dynamicLinkFromUniversalLinkURL:universalLinkURL completion:completion];
    BOOL canHandleUniversalLink =
         // canParseUniversalLinkURLで異変が起きているぽいな👀
        [self canParseUniversalLinkURL:universalLinkURL] && universalLinkURL.query.length > 0 &&
        FIRDLDictionaryFromQuery(universalLinkURL.query)[kFIRDLParameterLink];
    return canHandleUniversalLink;
  }
}

そして、canHandleUniversalLinkの中にはFIRDLIsAValidDLWithFDLDomainメソッドの記述しかなかったので、そこを辿ってみました。

FDLUtilities.m
BOOL FIRDLIsAValidDLWithFDLDomain(NSURL *_Nullable URL) {
  BOOL matchesRegularExpression = false;
  NSString *urlStr = URL.absoluteString;

  if ([URL.host containsString:@".page.link"] || [URL.host containsString:@".app.goo.gl"] ||
      [URL.host containsString:@".app.google"]) {

    // このケースに来ているな👀

    // Matches the *.page.link and *.app.goo.gl domains.
    matchesRegularExpression =
        ([urlStr rangeOfString:
                     @"^https?://"
                     @"[a-zA-Z0-9]+((\\.app\\.goo\\.gl)|(\\.page\\.link)|(\\.app\\.google))((\\/"
                     @"?\\?.*link=https?.*)|(\\/[a-zA-Z0-9-_]+)((\\/?\\?.*=.*)?$|$))"
                       options:NSRegularExpressionSearch]
             .location != NSNotFound);
    // ↑↑↑ こいつが怪しいな👀🤔😠 ↑↑↑

    if (!matchesRegularExpression) {
        // こっちには落ちてきていない
    }
  }

  return matchesRegularExpression;
}

matchesRegularExpressionの結果を返していることがわかったので、ここで定義されている正規表現が怪しいというところまでわかりました。
じゃあ実際、この正規表現はどうなっているのかをみていきましょう🕵️‍♂️

自分が検証していたリンクはhttps://hogehoge.page.link/?ibi=xxxxx&isi=xxxxx&link=https://hugahugaみたいな形式になっていました。
page.linkまでの正規表現に関してはひとまず問題はなさそうだったので、クエリパラメータが怪しそうです。

よく見てみると、なんかパラメータは?linkから始まるようにしないといけない条件になっていないか...

((\\/?\\?.*link=https?.*)|(\\/[a-zA-Z0-9-_]+)((\\/?\\?.*=.*)?$|$))

まさかと思い、パラメータの先頭が?linkから始まるように調整してもらったところ無事正常に動いた...

スクリーンショット 2023-03-03 19.24.26.png

Firebaseのコンソール上から作成するDynamicLinksでは、長いダイナミックリンクを見るとわかるように、ドメインの後はlinkのパラメータがつくようになっていたのでこういう仕様なんだなと受け入れるしかないようです...
※画像雑ですみません🙇‍♂️

まとめ

要するに、バックエンドで動的に作成するときにディープリンクを用いる場合はドメイン直後のパラメータの先頭はlinkから始まるようにしないといけないようです。

簡単に比較するとこんな感じ

🙆‍♂️ https://hogehoge.page.link/?link=https://hugahuga&ibi=xxxxx&isi=xxxxx
🙅‍♂️ https://hogehoge.page.link/?ibi=xxxxx&isi=xxxxx&link=https://hugahuga

パラメータは順不同であってほしい世界でした。
ちなみに、記事執筆時のfirebase-iOS-sdkの最新バージョンは10.7.0でしたが、この正規表現に変更はありませんでしたので、引き続きこの仕様であることを注意しないといけません。

この投稿が同様にハマっている他の方の助けになることを願っています。

参照

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?