はじめに
広告非表示の実装をしたく非消耗型のアプリ内課金実装をしました。
iOS17以降で使えるProductView
を使いながらミニマムでアプリ内課金を実装する方法としてまとめます。
環境
Xcode 15.1
iOS17 ~ サポート
内容
ざっくりと以下の手順でアプリ内課金の実装をしていきます。
- AppStoreConnectのアプリ内課金設定
- アプリ内課金の実装
- 審査提出
1. AppStoreConnectから有料アプリの契約を確認する
ステータスがアクティブになっていなかったので、口座情報と納税フォームを設定していきます。
問題なく設定できたことが確認されるとステータスがアクティブに変わります。
2. AppStoreConnectでアプリ内課金の設定をする
AppStoreConnectの対象アプリのアプリ内課金画面から作成します。
特に難しい項目はないので必須項目を順に埋めていきます。
- 種類・参照名・製品IDを入力しアプリ内課金を作成
種類は広告の非表示機能のため、非消耗型を選択しました。
参照名はApp Store Connectおよび「売上とトレンド」のレポートで使用されます。App Storeには表示されません。名前は最大 64 文字です。
レポートに使用される固有な英数字のIDです。ある製品に対して使用した製品IDは、その製品が削除されても再度使用することはできません。
- アプリ内課金の価格を設定
好きな価格を設定します。
- App Storeのローカリゼーションを設定
ローカリゼーション、表示名と説明を入力します。
ここの表示名と説明はそのまま課金画面に表示されるため注意した方が良いかもです。
- 審査に関する情報を設定
シミュレーターで表示した課金画面のスクリーンショットを貼り、審査メモには広告非表示のためのアプリ内課金ですと記載しました。
スクリーンショットは審査の目的においてのみ使用します。App Storeに表示されることはありません。
3. Xcodeプロジェクトの設定をする
XcodeのSigning&Capabilities
からIn-App Purchase
を追加します。
4. アプリ内課金画面を実装する
iOS17以上ではSwifUIの課金画面をサクッと実装できるようになりました。
広告非表示の一つの課金画面を表示するだけであれば、ProductView
に製品IDを渡すだけでUIを作ることができます。
import SwiftUI
import StoreKit
struct IAPView: View {
var body: some View {
ProductView(id: "AppStoreConnectで設定したIDを入力")
.productViewStyle(.large)
.onInAppPurchaseCompletion { product, result in
if case .success(.success(_)) = result {
// 課金が成功した場合の処理
} else {
// 課金が失敗した場合の処理
}
}
}
}
課金処理
onInAppPurchaseCompletion
を使うことで課金処理の結果に合わせた処理を実装できます。
結果 Product.PurchaseResult
は以下の通り
-
case success(VerificationResult<Transaction>)
→ 成功 -
case userCancelled
→ ユーザーによるキャンセル -
case pending
→ ユーザーによるアクションが必要
購入完了と購入済みのアラートはデフォルトで表示されました。
課金UI
表示するUIは3パターンから選ぶことができます。
func productViewStyle(_ style: some ProductViewStyle) -> some View
.large
, .regular
, .compact
or .automatic
尚、設定画面側でProductView
の表示を.presentationDetents([.height(200)])
としています
HStack {
Text("広告非表示")
Spacer()
if hideAd {
Text("購入済み")
} else {
Button {
showIAPView = true
} label: {
Text("購入する")
}
}
}
.sheet(isPresented: $showIAPView) {
IAPView(hideAd: $hideAd)
.presentationDetents([.height(200)])
}
課金画面のUI実装は他にもできることがたくさんありますが、今回はミニマム実装でやりたいことが満たせそうだったのでここまでに。
と思い審査に一度提出してみたところ、復元ができないのでダメよと怒られてしまいました。当然ですね。うっかりしてました。
復元処理
復元の処理はAppStore.sync()
でAppStoreと同期
Transaction.currentEntitlement(for: "AppStoreConnectで設定したIDを入力")
で課金情報を取得します。
import SwiftUI
import StoreKit
struct IAPView: View {
var body: some View {
+ VStack(spacing: 20) {
ProductView(id: "AppStoreConnectで設定したIDを入力")
.productViewStyle(.large)
.onInAppPurchaseCompletion { product, result in
if case .success(.success(_)) = result {
// 課金が成功した場合の処理
} else {
// 課金が失敗した場合の処理
}
}
+ Button("復元はこちら") {
+ Task {
+ do {
+ try await AppStore.sync()
+ let verificationResult = await Transaction.currentEntitlement(for: "AppStoreConnectで設定したIDを入力")
+ if case .verified = verificationResult {
+ // 復元が成功した場合の処理
+ } else {
+ // 復元が失敗した場合の処理
+ }
+ } catch {
+ // 復元が失敗した場合の処理
+ }
+ }
+ }
}
}
}
ここまでがミニマム実装です!
5. 審査に提出
最初のアプリ内課金は、新しいアプリバージョンと共に提出する必要があります。アプリ内課金を作成したら、アプリバージョンのページの「アプリ内課金とサブスクリプション」セクションからそのアプリ内課金を選択してください。その後、そのアプリバージョンをApp Reviewに提出してください。
アプリの申請画面にアプリ内課金を追加するフィールドが表示されます。
作成したアプリ内課金を追加して通常通り審査に提出します。
一度は復元の実装漏れでリジェクトされたものの、再提出して無事審査に通りました🎉
おわりに
今回は新規の個人アプリだったのでiOS17~サポートとしました。ProductView
が使えたことで、かなり実装が楽だった印象です。
サブスク課金なども実装してみようかなと思いました。
参考