מסך ניהול מנויים מעוצב בצורה גרועה מאיץ ביטולים — עד 40% מהמשתמשים נוטשים מבלי להבין מה הם מפסידים. אנו בונים מסכים שקוראים סטטוס בזמן אמת באמצעות StoreKit 2 ב-iOS ו-Google Play Billing Library 6 באנדרואיד, משלבים תקופת חסד, הצעות שימור וכפתור ניהול מנויים מנוהל על ידי המערכת. הגישה שלנו מפחיתה נטישה ומעלה את שיעורי החידוש. עם ניסיון של 5+ שנים במודלי מנויים ויותר מ-20 מסכים שיושמו עבור אפליקציות עם 10k–500k משתמשים פעילים, אנו מכירים את הניואנסים של הפלטפורמות ועוזרים להימנע מדחיות ב-App Review (הנחיה 3.1.2).
אילו נתונים להציג
קבוצה מינימלית:
- התוכנית הנוכחית: שם, מחיר, תקופה
- תאריך החיוב הבא או תאריך התפוגה (אם בוטל)
- סטטוס: פעיל / מבוטל (גישה עד) / תקופת חסד / ניסיון חיוב חוזר
- כפתור להחלפת תוכנית (שדרוג/הורדה)
- קישור למסך הביטול המערכתי של Apple/Google
ללא הצגת תקופת החסד אתה מאבד עד 30% מהחידושים — משתמשים פשוט לא מספיקים לעדכן את הכרטיס שלהם. שימור משתמש עולה פי 5 פחות מאשר רכישת משתמש חדש, כך שההשקעה בתהליך הביטול מחזירה את עצמה בתוך החודש הראשון. הצגת סטטוס תקופת החסד עם באנר ייעודי מפחיתה חרדת משתמשים ומעלה את הסבירות לחידוש.
כיצד אנו קוראים את מצב המנוי (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
)
}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, הגישה נמשכת עד false.
השוואה: StoreKit 2 לעומת Play Billing 6
| פרמטר | iOS (StoreKit 2) | אנדרואיד (Play Billing 6) |
|---|---|---|
| קריאת סטטוס | expirationDate (SubscriptionInfo.state, .subscribed וכו') |
.inGracePeriod + BillingClient.queryPurchasesAsync() |
| תקופת חסד | אוטומטית דרך purchaseState |
דורש בדיקת .inGracePeriod ו-acknowledgementState |
| שדרוג/הורדה | purchaseTime |
Product.subscription?.update() עם SKU מעודכן |
| מסך מערכתי | BillingClient.launchBillingFlow() |
Deeplink ל-Google Play |
| הצעה שיווקית | AppStore.showManageSubscriptions מסוג SubscriptionOffer או .introductory |
.promotional |
דוגמת רכיב SwiftUI
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()
}
}
} מדוע כפתור ניהול המנויים המערכתי נדרש
Apple מחייבת גישה למסך המערכתי (הנחיה 3.1.2). בלעדיו — דחייה. השתמש ב-showManageSubscriptions כדי לפתוח את מסך ניהול המנויים המערכתי — תיעוד המפתחים של Apple. היישום פשוט:
Button("Управление подпиской в App Store") {
Task {
try? await AppStore.showManageSubscriptions(in: windowScene)
}
}באנדרואיד — deeplink ל-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) כיצד אנו מעצבים את תהליך הביטול
שיטות השימור הטובות ביותר בעת ניסיון ביטול:
- לפני הפנייה למסך המערכתי, הצג רשימת תכונות שהמשתמש יפסיד.
- אם המנוי פעיל זמן רב (3+ חודשים), הצע השהיה (נתמך באופן טבעי באנדרואיד).
- למשתמשים עם היסטוריה ארוכה, הצג הצעה שיווקית עם הנחה.
זו לא תבנית אפלה — זו תזכורת כנה. אל תגזים: לכל היותר מסך אישור אחד. הסטטיסטיקות שלנו: שימור לאחר הצעה שיווקית הוא 60%. הצעות שיווקיות יכולות לייצר עד 15% הכנסה נוספת. אם המשתמש עדיין עוזב, תעד את הסיבה באמצעות אנליטיקה כדי לשפר את המוצר.
השוואה: מסך בסיסי לעומת מסך שימור
| פרמטר | מסך בסיסי | מסך עם שימור |
|---|---|---|
| קריאת סטטוס | StoreKit 2 / Play Billing | + תקופת חסד + ניסיון חיוב חוזר |
| תצוגה | תוכנית, תאריך, סטטוס | + רשימת תכונות, הצעה שיווקית |
| תהליך ביטול | רק מסך מערכתי | מסך מערכתי + מסך שני עם בחירת השהיה/הנחה |
| זמן פיתוח | 2–3 ימים | 4–5 ימים |
| משתמשים שנשמרו | 10–15% | 40–60% |
מה כלול בעבודה
- קריאת סטטוס באמצעות StoreKit 2 / Google Play Billing Library 6
- רכיב תצוגה: תוכנית, תאריך, סטטוס, תקופת חסד
- מחליף תוכניות (שדרוג/הורדה) עם תיאור תנאים
- שילוב של
BillingFlowParams.SubscriptionUpdateParams/ Play Store deeplink - תהליך ביטול אופציונלי עם הצעת שימור
- תיעוד לרכיבים ולארכיטקטורת מסך המנויים
- תמיכה ל-14 יום לאחר המסירה
לוח זמנים ועלות
2–3 ימים למסך בסיסי עם סטטוס בזמן אמת. עם תהליך ביטול מורכב והצעות שימור — עד 5 ימים. העלות מחושבת באופן אישי. קבל ייעוץ — נעריך את הפרויקט שלך.
צור קשר כדי לדון בצרכי ניהול המנויים שלך. נתאים פתרון ללוגיקה העסקית שלך.







