A poorly designed subscription management screen accelerates cancellations — up to 40% of users churn without understanding what they lose. We build screens that read real-time status via StoreKit 2 on iOS and Google Play Billing Library 6 on Android, integrate grace period, retention offers, and the system-managed subscription button. Our approach reduces churn and boosts renewal rates. With 5+ years of experience in subscription models and over 20 implemented screens for apps with 10k–500k active users, we know platform nuances and help avoid App Review rejections (guideline 3.1.2).
What data to display
Minimum set:
- Current plan: name, price, period
- Next billing date or expiration date (if cancelled)
- Status: active / cancelled (access until) / grace period / billing retry
- Button to switch plans (upgrade/downgrade)
- Link to Apple/Google system cancellation screen
Without showing grace period you lose up to 30% of renewals — users simply don't have time to update their card. Retaining a user costs 5 times less than acquiring a new one, so investment in the cancellation flow pays off within the first month. Displaying grace period status with a dedicated banner reduces user anxiety and increases renewal probability.
How we read subscription state (StoreKit 2)
import StoreKit
struct SubscriptionInfo {
let productID: String
let displayName: String
let price: String
let renewalDate: Date?
let expirationDate: Date?
let willAutoRenew: Bool
let state: Product.SubscriptionInfo.RenewalState
let isInGracePeriod: Bool
}
func loadSubscriptionInfo(productIds: [String]) async -> SubscriptionInfo? {
guard let product = try? await Product.products(for: productIds).first,
let status = try? await product.subscription?.status.first else {
return nil
}
let renewalInfo = try? status.renewalInfo.payloadValue
let transaction = try? status.transaction.payloadValue
return SubscriptionInfo(
productID: product.id,
displayName: product.displayName,
price: product.displayPrice,
renewalDate: renewalInfo?.renewalDate,
expirationDate: transaction?.expirationDate,
willAutoRenew: renewalInfo?.willAutoRenew ?? false,
state: status.state,
isInGracePeriod: status.state == .inGracePeriod
)
}
renewalInfo.willAutoRenew indicates whether auto-renewal is active or cancelled. If false, access continues until expirationDate.
Comparison: StoreKit 2 vs Play Billing 6
| Parameter | iOS (StoreKit 2) | Android (Play Billing 6) |
|---|---|---|
| Status reading | SubscriptionInfo.state (.subscribed, .inGracePeriod, etc.) |
BillingClient.queryPurchasesAsync() + purchaseState |
| Grace period | Automatic via .inGracePeriod |
Requires checking acknowledgementState and purchaseTime |
| Upgrade/downgrade | Product.subscription?.update() |
BillingClient.launchBillingFlow() with updated SKU |
| System screen | AppStore.showManageSubscriptions |
Deeplink to Google Play |
| Promotional offer | SubscriptionOffer type .introductory or .promotional |
BillingFlowParams.SubscriptionUpdateParams |
SwiftUI component example
struct SubscriptionManagementView: View {
@StateObject private var viewModel = SubscriptionManagementViewModel()
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 24) {
if let info = viewModel.subscriptionInfo {
CurrentPlanCard(info: info)
if info.state == .inGracePeriod {
GracePeriodWarning()
}
if info.willAutoRenew, let date = info.renewalDate {
Text("Следующее списание: \(date.formatted(.dateTime.day().month().year()))")
.foregroundStyle(.secondary)
} else if let date = info.expirationDate {
Text("Доступ активен до: \(date.formatted(.dateTime.day().month().year()))")
.foregroundStyle(.secondary)
}
PlanPickerSection(currentPlan: info.productID)
}
ManageButton()
}
.padding()
}
.task { await viewModel.load() }
}
}
Why the system subscription management button is required
Apple mandates access to the system screen (guideline 3.1.2). Without it — rejection. Use showManageSubscriptions to open the system subscription management screen — Apple Developer Documentation. Implementation is simple:
Button("Управление подпиской в App Store") {
Task {
try? await AppStore.showManageSubscriptions(in: windowScene)
}
}
On Android — a deeplink to Google Play:
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("https://play.google.com/store/account/subscriptions?sku=premium_monthly&package=${packageName}")
}
startActivity(intent)
How we design the cancellation flow
Best retention practices on attempted cancellation:
- Before redirecting to the system screen, show a list of features the user will lose.
- If the subscription has been active for a long time (3+ months), offer a pause (natively supported on Android).
- For users with a long history, show a promotional offer with a discount.
This is not a dark pattern — it is an honest reminder. Do not overdo it: one confirmation screen at most. Our statistics: retention after a promo offer is 60%. Promotional offers can generate up to 15% additional revenue. If the user still leaves, capture the reason via analytics to improve the product.
Comparison: basic vs retention screen
| Parameter | Basic screen | Screen with retention |
|---|---|---|
| Status reading | StoreKit 2 / Play Billing | + grace period + billing retry |
| Display | plan, date, status | + feature list, promo offer |
| Cancel flow | only system screen | system screen + second screen with pause/discount selection |
| Development time | 2–3 days | 4–5 days |
| Retained users | 10–15% | 40–60% |
What is included in the work
- Status reading via StoreKit 2 / Google Play Billing Library 6
- Display component: plan, date, status, grace period
- Plan switcher (upgrade/downgrade) with terms description
- Integration of
AppStore.showManageSubscriptions/ Play Store deeplink - Optional cancellation flow with retention offer
- Documentation for components and subscription screen architecture
- 14-day support after delivery
Timeline and cost
2–3 days for a basic screen with real-time status. With a complex cancellation flow and retention offers — up to 5 days. Cost is calculated individually. Get a consultation — we will evaluate your project.
Contact us to discuss your subscription management needs. We will tailor a solution to your business logic.







