【RevenueCat】Jetpack Compose で CompositionLocal を使ったサブスク状態管理 Tips
RevenueCat、便利ですよね。
Jetpack Compose 製の個人アプリにサブスクを導入する際、RevenueCat のおかげでスムーズに実装できました。今回は、その際に利用した CompositionLocal
を使った サブスク状態の管理Tips をご紹介したいと思います。
CompositionLocal
を使うのか?
なぜサブスクの情報は、アプリの様々な場所で必要になります。例えば、サブスク購読者限定の機能を有効にしたり、非購読者には購読を促す UI を表示したり。
通常、Jetpack Compose でデータを引き回すには State Hoisting などを使いますが、アプリの規模が大きくなると、必要な場所にバケツリレーのように渡していくのは手間がかかります。コードの見通しも悪くなりがちです。
お行儀よくするなら Repository でサブスク情報を取得して…、などがありそうですが、そこまでする元気もない…。(笑)
そこで、CompositionLocal
。これを使えば、Compose ツリーの特定の部分で共通のデータを提供できます。一度設定してしまえば、そのツリー内のどの Composable からも直接購読情報にアクセスできるようになるので、個人開発アプリではうってつけでした。
CompositionLocal
で購読状態をハンドリングする
まずは、購読の状態を表す enum と、その情報を保持するための CompositionLocal
を定義しました。この enum には、例えば購読の有無に応じて異なる制限値(カテゴリ作成上限数など)を持たせると、より柔軟に機能の出し分けが可能になります。
// サブスク状態管理用種別
enum class SubscriptionStatus(
val categoryLimit: Int,
) {
ACTIVE(
categoryLimit = Int.MAX_VALUE,
),
INACTIVE(
categoryLimit = 5,
);
val isActivated: Boolean
get() = this == ACTIVE
val isDeactivated: Boolean
get() = this == INACTIVE
}
// SubscriptionLocal.kt
val LocalSubscriptionStatus = compositionLocalOf { SubscriptionStatus.INACTIVE }
次に、アプリのルートとなる Composable 関数( MainActivity
の setContent
ブロック内)で、RevenueCat から取得したサブスク情報を基に CompositionLocalProvider
を設定します。LaunchedEffect
を使って RevenueCat の購読情報が更新されるたびに SubscriptionStatus
を更新するフローを組み込むのがポイントです。こうすることで、ユーザーの購読状態が変更された際も、アプリ全体にリアルタイムに反映されます。
// MainActivity.kt (一部抜粋)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val subscriptionStatusFlow = remember { MutableStateFlow(SubscriptionStatus.INACTIVE) }
val subscriptionStatus by subscriptionStatusFlow.collectAsState()
// Composable でサブスク購読状態の更新
LaunchedEffect(Unit) {
Purchases.sharedInstance.updatedCustomerInfoListener =
UpdatedCustomerInfoListener { customerInfo ->
subscriptionStatusFlow.update {
// 有効なサブスクがあるかどうか
if (customerInfo.activeSubscriptions.any()) {
SubscriptionStatus.ACTIVE
} else {
SubscriptionStatus.INACTIVE
}
}
}
// アプリ起動時の初回購読情報取得
Purchases.sharedInstance.getCustomerInfo { customerInfo, _ ->
if (customerInfo != null) {
subscriptionStatusFlow.update {
if (customerInfo.activeSubscriptions.any()) {
SubscriptionStatus.ACTIVE
} else {
SubscriptionStatus.INACTIVE
}
}
}
}
}
// LocalSubscriptionStatusを提供
CompositionLocalProvider(
LocalSubscriptionStatus provides subscriptionStatus,
) {
YourAppNavHost() // アプリのナビゲーションやメインコンテンツ
}
}
}
}
上記のコードでは、MutableStateFlow
でサブスク状態を管理し、その最新の値である subscriptionStatus
を CompositionLocalProvider
に渡しています。これで、YourAppNavHost()
以下のすべての Composable 関数が LocalSubscriptionStatus
を参照できるようになります。
CompositionLocal
を参照し、サブスク購読者だけの判定を楽にする
好きな箇所でCompositionLocalProvider
で情報が提供されていれば、あとはどの Composable 関数からでも、current
プロパティを使って購読情報を参照するだけとなります。
例えば、購読者のみに表示したいUIがある場合、以下のようにシンプルに条件分岐できます。SubscriptionStatus
enum で定義した categoryLimit
のような情報も同時に利用できるので、UIの出し分けや機能制限の実装が楽になります。
// SomeScreen.kt
@Composable
fun SomeScreen() {
// 実際に使う箇所
val subscriptionStatus = LocalSubscriptionStatus.current
Column {
if (subscriptionStatus.isActivated) {
// サブスク購読者のみに表示されるコンテンツ
Text("サブスク購読者限定コンテンツ")
Button(onClick = { /* ... */ }) {
Text("プレミアム機能へアクセス")
}
} else {
// 非購読者向けのコンテンツ
Text("プレミアムコンテンツをアンロックするには購読が必要です。")
Button(onClick = { /* ... */ }) {
Text("今すぐ購読する")
}
}
// カテゴリの制限値も利用できる
Text("カテゴリ作成可能数: ${subscriptionStatus.categoryLimit}")
}
}
まとめ
RevenueCat と Jetpack Compose を組み合わせる際に CompositionLocal
を活用することで、サブスク状態の管理を楽に実装することができました。
-
CompositionLocal
でサブスク状態をアプリの上位 Composable から一括提供 - 必要な場所で
CompositionLocal.current
を使って簡単にサブスク状態を参照 -
LaunchedEffect
とMutableStateFlow
を組み合わせることで、RevenueCat からのサブスク情報更新にもリアルタイムに対応
この記事が、どこかの誰かのアプリ開発の参考になれば幸いです。
Discussion