【Swift】requestReview()がiOS14.0から非推奨だったので対応した
ユーザーにレビューを促すSKStoreViewController.requestReview()
がiOS14から非推奨になっていたので、推奨されている形に直す為に調べました。
環境
- Xcode 12.5
- Swift 5.4
- iOS Deployment Target 14.1
requestReviewとは
ユーザーにアプリのレビューを促すこのような画面を出す処理になります。
'requestReview()' was deprecated in iOS 14.0
Deployment Targetを14.0以上に設定している場合、SKStoreReviewController.requestReview()
を使用すると、'requestReview()' was deprecated in iOS 14.0
と警告が出ます。
つまりは、iOS14.0で非推奨になったので使ってくれるなということですね。 非推奨になったからと言って利用できないわけではないですが、今後急に機能しなくなる可能性も多いにあるので早めに推奨される形に修正しましょう。
解決策
早速ですが、こちらのコードに置き換えたら解決しました。
if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene { SKStoreReviewController.requestReview(in: scene) }
解決方法は分かったのですが、何を意味しているのかを調べてみた。
connectedScenes
var connectedScenes: Set<UIScene> { get }
UIApplication.shared.connectedScenes
は、
connectedScenesとは、メモリ内にある、アクティブな作業を行っている可能性のあるシーンです。connectedScenesに含まれるシーンは、フォアグラウンドまたはバックグラウンド、オンスクリーンまたはオフスクリーンの場合があります。
アプリがマルチウィンドウ対応し、複数画面を表示した時にconnectedScenes
には複数の値がSet型で保持されます。
activationState
Sceneの現在の活性状態を表すactivationState
を調べてみると、
public enum ActivationState : Int { case unattached = -1 case foregroundActive = 0 case foregroundInactive = 1 case background = 2 }
- unattached: シーンが現在アプリに接続されていないことを示す状態
- foregroundActive: シーンがフォアグラウンドで実行されており、現在イベントを受信していることを示す状態態
- foregroundInactive: シーンがフォアグラウンドで実行されているが、イベントを受信していないことを示す状態
- シーンがバックグラウンドで実行されており、画面に表示されていないことを示す状態
なので、UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive })
の部分で、アプリのシーンの中からフォアグラウンドで実行されていてイベントを受信している要素を見つけて返しています。
requestReview(in:)
iOS14から導入された従来のrequestReview()
に代わるメソッドで、今までのreqeustReviewにどのシーンで使用するのかという引数の代入が必要となりました。
なんだか、これからマルチウィンドウ化が進んでいく未来がなんだか見えてきました。
もう少し実行内容を明確化したい
// 以前 SKStoreReviewController.requestReview() // iOS 14.0~ if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene { SKStoreReviewController.requestReview(in: scene) }
これまでは、SKStoreReviewController.requestReview()
だけでレビュー画面を表示できただけになんだか長くてパッと見わかりにくい気が、、。
てとこで、UIApplication
のextensionを作成して、コードをリファクタリングしてみました。
UIApplication Extension
extension UIApplication { var forgroundActiveScene: UIWindowScene? { return self.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene } }
リファクタリング後のコード
フォアグラウンドで活性化しているシーンをscene
に代入しているのが分かりやすくなりました。
if let scene = UIApplication.shared.forgroundActiveScene { SKStoreReviewController.requestReview(in: scene) }
おまけ
「このアプリはiOSのみに限定されていて、iPadのマルチウィンドウには対応するつもりはないから、connectedScenesは常に一つしかないんだ!」 という方は、
この書き方でもreviewRequest(in:)
を動作させることが出来ます。
if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene { SKStoreReviewController.requestReview(in: scene) }
このconnectedScenes
はSet型なので、要素が複数ある場合は必ずしもfirst
が.forgroundActive
であるとは決まっていません。
ですが、このconnectedScenes
の要素が一つに限られる場合のみ、要素には現在起動しているアプリのその時点でのアクティブなウィンドウしか無い為、first
で.forgroundActive
のシーンを取得することが出来ます。
結局、一番スマートそうな解決策
表示する画面がある特定の画面の場合に、view.window
からwindowScene
を取る方法もあるよ。と教えていただきました。
if let scene = view.window?.windowScene { SKStoreReviewController.requestReview(in: scene) }
単純に現在のview
に追加されているwindow
のwindowScene
からscene
を取得する方法です。
scene
の取得方法をあれこれ書いてみましたが、この方法が一番明確でシンプルで良さそうです。
365日ルール
レビューリクエスト画面は、365日で最大3回までしか表示されないルールがあるようです。
なので、どの時、どの場所で出すのが一番効果的かをしっかり考えてリクエスト出す必要がありそうです。
それに伴い、Human Interface Guidelinesに記載されていますが、ボタンなどの操作(例: レビューをするButton)でレビューリクエスト出す処理は行わないで下さいとのことです。リクエストリミットを超えてしまうと、ボタン等を押しても何も反応しなくなるのでユーザーには悪い体験を与えてしまいます。
おわりに
ユーザーに自らアプリを評価していただけるならもちろん嬉しい限りですが、ユーザーがレビューしやすい環境や仕組み作りが大事ということが分かりました。
たくさんレビューリクエストを出せばいいわけではないですし、ユーザーにある程度使ってもらって、アプリへの感想が少し溜まったあたりでレビューリクエストを出すのが良さそうですね。じゃあ、それはいつなんだよ?って感じですが、、。
色々試しながらクリティカルなレビューリクエストを模索していきたいと思います。
最後に、僕はどちらかというとタコが好きです。
参考
- connectedScenes | Apple Developer Documentation
- UIScene.ActivationState | Apple Developer Documentation
- first(where:) | Apple Developer Documentation
- requestReview(in:) | Apple Developer Documentation
- requestReview()' was deprecated in iOS 14.0
- How to ask users to review your app using SKStoreReviewController