はじめに
SwiftUIで作成したアプリで、
ユーザーに評価やレビューを依頼するViewを表示しようと思ったのですが、
iOSのバージョンによって使い方がコロコロ変わっているのでまとめてみました。
(iPadOS, macOS, Mac Catalystでも事情は同じですが省略します)
APIの変遷
- iOS 13.0 - (14.0でdeprecated)
SKStoreReviewController.requestReview()
- iOS 14.0 - (deprecatedではない)
// 事前にUIWindowSceneを取得する必要がある。
SKStoreReviewController.requestReview(in: windowScene)
- iOS 16.0 以降
@Environment(\.requestReview) var requestReview
requestReview()
UIWindowSceneを取得しなくても良くなった (面倒くさかった)
コード
EnvironmentValuesをカスタマイズする方法
iOS13 - 16まで可能な限りコードを共通化する。
iOS15_requestReview
をEnvironmentValues
に追加、
- iOS 16以降:
RequestReviewAction
を返す。 - それ以外:
iOS15_RequestReviewAction
を返す。
iOS15_RequestReviewAction
のcallAsFunction()
で各OS用のrequestReview()
を呼ぶ。
※ iOS 15のサポートを終了したらiOS15_...
を削除して、下記カスタマイズを使用しない方法のコードに変更する。
import SwiftUI
import StoreKit
struct ContentView: View {
@Environment(\.iOS15_requestReview) var requestReview
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.onAppear {
AppStore.perform(requestReview)
}
}
}
struct AppStore {
@MainActor
static func perform(_ action: Any) {
if #available(iOS 16.0, *) {
// iOS 16 -
(action as? RequestReviewAction)?()
} else {
// iOS 13 - 15
(action as? iOS15_RequestReviewAction)?()
}
}
}
@available(iOS, introduced: 13.0, obsoleted: 16.0, message: "Use RequestReviewAction instead")
@MainActor
struct iOS15_RequestReviewAction: EnvironmentKey, Sendable {
static let defaultValue = Self()
func callAsFunction() {
if #available(iOS 14.0, *) {
if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
SKStoreReviewController.requestReview(in: scene)
}
} else {
SKStoreReviewController.requestReview()
}
}
}
@available(iOS, introduced: 13.0, obsoleted: 16.0, message: "Use requestReview instead")
extension EnvironmentValues {
@MainActor
var iOS15_requestReview: Any {
get {
if #available(iOS 16.0, *) {
return requestReview
} else {
return self[iOS15_RequestReviewAction.self]
}
}
}
}
カスタマイズを使用しない方法
Minimum Deployments が iOS 16.0 以降
import SwiftUI
import StoreKit
struct ContentView: View {
@Environment(\.requestReview) var requestReview
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.onAppear {
requestReview()
}
}
}
Minimum Deployments が iOS 13.0 - 15.x
import SwiftUI
import StoreKit
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.onAppear {
if #available(iOS 14.0, *) {
if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
SKStoreReviewController.requestReview(in: scene)
}
} else {
SKStoreReviewController.requestReview()
}
}
}
}
他に良い方法があったらご教授ください。
ご注意
- requestReviewを呼び出すタイミングは真面目に考慮していません。
- UIWindowSceneの取得方法も真面目に考慮していません。
開発環境
- Xcode 14.1
参考
Ratings and reviews (Human Interface Guideline)
評価、レビュー、返答
SKStoreReviewController requestReview()
SKStoreReviewController requestReview(in:)
RequestReviewAction