この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
Universal Linksを使用すると、https://〜のリンクからインストールされているアプリを起動することが可能です。
Universal Linksを試してみました。関連づけファイル(apple-app-site-association)はS3に置きました。
これは、Webベージや、メールで送ったリンクからのアプリ誘導には非常に有効な手段です。
しかし、起動されるべきアプリが既に実行中であり、そのアプリの中で使用しているUIWebView等で、自分自身を起動するUniversal Linksを踏んだ場合どうなるのでしょう?
っと言うことで、調べみました。
2 試験環境
(1) 試験用ページ
試験のために次の2つのページを用意しました。
- アクセス元のページ
- アクセス先のページ
「アクセス元のページ」には、「アクセス先のページ」(Universal Links用のapple-app-site-associationが設置してある)へのリンクが置かれています。
こちらは、「アクセス先のページ」です。上のページでリンクを選択すると、ここに遷移します。
(2) テストアプリ
テストのために、Universal Linksで起動するテストアプリを作成しましました。
トップには、「UIWebView」「WKWebView」「SFSafariViewController」の3つのボタンがあり、それぞれのビューを開いて、先の「アクセス元のページ」を開くようになっています。
また、Universal Linksで起動された場合は、下記のように、ログ表示するとともに、別のビューを開いてURLを表示するようにしました。
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
print("application(_:continue:restorationandler:)")
let url = (userActivity.webpageURL?.absoluteString)!
print("URL=\(url)")
viewController.openUniversalLinksView(url: url)
}
return true
}
下図は、テストアプリを実行している様子です。 最初に、ボタンをタップして、各Webビューを表示しています。そして、一旦アプリを終了させ、SafariでUniversal Linksを選択することでアプリを起動しています。
3 動作確認
(1) UIWebView
UIWebViewからUniversalLinksのリンクをタップすると「アクセス先のページ」が表示されてしまいます。(目的のビューは表示されない)
アプリは既に起動しているので、「Universal Linksは、問題ない」とも言えますが、これは、意図した動作ではないかも知れません。
(2) WKWebView
WKWebViewでも同じく「アクセス先のページ」が表示されてしまいます。(目的のビューは表示されない)
(3) SFSafariViewController
SFSafariViewControllerの場合は、リンクをタップしても、表示は変わりませんでした。(目的のビューは表示されない) しかし、application(_:continue:restorationHandler:)を通過したログが表示されていました。
SFSafariViewControllerは、テストアプリとは、別のプロセスとして動作しているため、バックグランドになっているアプリがUniversal Linksでキックされたいう理解で良いのでしょうか?
しかし、「完了」でSFSafariViewControllerを閉じても、目的のビューには遷移していませんでした。
これは、表示されていたログです。
application(_:continue:restorationHandler:)
URL=https://universal-links-bucket.s3.amazonaws.com/index.html
4 対応要領の一例
動作確認の結果、どのWebViewも、予定した動作ができていないと言えそうです。(目的のビューは表示されない) アプリの仕様によって、対策は色々あると思いますが、今回は、その一例を考えてみました。
(1) UIWebViewの対応
UIWebViewでは、UIWebViewDelegateプロトコルを実装することでwebView(_:shouldStartLoadWith:navigationType:)で遷移前に処理をトラップすることが可能です。
ここで遷移先を確認して、対象URLだった場合は、UIWebViewを表示しているビューをクローズして、目的のビューを表示しました。
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
let url = request.url?.absoluteString
if url == DestinationUrl {
// UniversalLinksで起動された場合の処理
let viewController = navigationController?.viewControllers.first as! ViewController
viewController.openUniversalLinksView(url: url!)
// NavigationControllerで戻る
_ = navigationController?.popViewController(animated: true)
// 遷移をキャンセル
return false
}
return true
}
(2) WKWebViewの対応
WKWebViewの場合も、先の対応と同じです。webView(_:decidePolicyFor:decisionHandler:)でトラップして、同じ要領で目的のビューに遷移しています。
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
let url = navigationAction.request.url?.absoluteString
if url == DestinationUrl {
// UniversalLinksで起動された場合の処理
let viewController = navigationController?.viewControllers.first as! ViewController
viewController.openUniversalLinksView(url: url!)
// NavigationControllerで戻る
_ = navigationController?.popViewController(animated: true)
// 遷移をキャンセル
decisionHandler(.cancel)
return
}
decisionHandler(.allow)
}
(3) SFSafariViewControllerの対応
SFSafariViewControllerでは、遷移先に移動する前にトラップする処理を書けませんので、application(_:continue:restorationHandler:)側から、対処を考えてみました。
Universal Linksで起動されて、目的のビューに遷移する際に、もしSFSafariViewControllerが表示されていたらクロースする処理を追加しました。
func openUniversalLinksView (url: String){
// もし、SFSafariViewControllerが表示されていたらクローズする
sfview?.dismiss(animated: true, completion: nil)
self.url = url
performSegue(withIdentifier: "toUniversalLinks",sender: nil)
}
(4) 動作確認
対応したテストアプリを実行している様子です。 SFSafariViewControllerから遷移する場合に、一旦トップのビューが表示されてしまうのは、SFSafariViewControllerを閉じてしまわないと、画面遷移ができなかったためです。適切な方法が有りましたら、是非教えてやって下さい。
5 最後に
今回は、Universal Linksを自らが踏んだ場合の対策を検討してみました。
ここで紹介した対策は一例に過ぎません。例えば、今回のテストアプリでは、UINavigationControllerでWeb表示のビューに遷移しているので、popViewController(animated:)を使用しましたが、Modalで遷移している場合などは、dismiss(animated:completion:)で、ビューを閉じる必要があるかも知れません。
どちらにしても、UXに違和感を与えないためには、何らかの処置が必要だと思いました。
参考のため、テストアプリ(対策は終わっています)を置きました。
再利用する場合は、Universal Linksのドメイン及びapple-app-site-associationについては、別途、準備が必要です。
[GitHub] https://github.com/furuya02/ULSample
6 参考リンク
Apple Refelence Support Universal Links
Apple プログラミングリファレンス ユニバーサルリンクへの対応
Universal Linksを試してみました。関連づけファイル(apple-app-site-association)はS3に置きました。
[iOS] ディープリンク(Custom URL Scheme)でアプリを起動する