| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- import Foundation
- import StoreKit
- enum PaywallPlan: String, CaseIterable, Identifiable {
- case monthly
- case yearly
- case lifetime
- var id: String { rawValue }
- var title: String {
- switch self {
- case .monthly: "Monthly"
- case .yearly: "Yearly"
- case .lifetime: "Lifetime"
- }
- }
- var subtitle: String {
- switch self {
- case .monthly: "Billed every month"
- case .yearly: "Save 42%. Billed once per year."
- case .lifetime: "One-time payment. All future updates."
- }
- }
- var productID: String {
- switch self {
- case .monthly: SubscriptionProductID.monthly
- case .yearly: SubscriptionProductID.yearly
- case .lifetime: SubscriptionProductID.lifetime
- }
- }
- var fallbackMainPrice: String {
- switch self {
- case .monthly: "$7.99"
- case .yearly: "$49.99"
- case .lifetime: "$79.99"
- }
- }
- var price: String { fallbackMainPrice }
- var priceSuffix: String? {
- switch self {
- case .monthly: "/mo"
- case .yearly: "/yr"
- case .lifetime: nil
- }
- }
- var ctaTitle: String {
- switch self {
- case .monthly: "Get Premium"
- case .yearly: "Get Premium"
- case .lifetime: "Unlock Lifetime"
- }
- }
- var fallbackBillingDescription: String {
- switch self {
- case .monthly:
- return "Billed at \(fallbackMainPrice) every month"
- case .yearly:
- return "Billed at \(fallbackMainPrice) every year"
- case .lifetime:
- return "One-time payment of \(fallbackMainPrice)"
- }
- }
- var isBestValue: Bool {
- self == .lifetime
- }
- var features: [String] {
- [
- "Unlimited AI Generations",
- "Post Generator & Title Optimizer",
- "Comment Writer with smart replies",
- ]
- }
- func planCopy(from config: PaywallConfig) -> PaywallConfig.PlanCopy {
- switch self {
- case .monthly: config.plans.monthly
- case .yearly: config.plans.yearly
- case .lifetime: config.plans.lifetime
- }
- }
- func localizedPrice(from product: Product?, config: PaywallConfig) -> String {
- product?.displayPrice ?? fallbackMainPrice
- }
- func localizedSubtitle(
- from product: Product?,
- config: PaywallConfig,
- introOffer: Product.SubscriptionOffer? = nil
- ) -> String {
- guard let product else { return config.loadingPrice }
- let copy = planCopy(from: config)
- let price = product.displayPrice
- if self == .yearly, let introOffer, let trialTemplate = copy.trialSubtitleTemplate {
- let duration = config.trialDurationDescription(from: introOffer, lowercase: true)
- return trialTemplate
- .replacingOccurrences(of: "{trial}", with: duration)
- .replacingOccurrences(of: "{price}", with: price)
- }
- return copy.subtitleTemplate.replacingOccurrences(of: "{price}", with: price)
- }
- func localizedCTATitle(
- from product: Product?,
- config: PaywallConfig,
- introOffer: Product.SubscriptionOffer? = nil
- ) -> String {
- guard let product else { return config.loadingCTA }
- let copy = planCopy(from: config)
- let price = product.displayPrice
- if self == .yearly, let introOffer {
- return config.trialCTATitle(from: introOffer)
- }
- return copy.ctaTemplate.replacingOccurrences(of: "{price}", with: price)
- }
- func localizedRenewalDisclosure(
- from product: Product?,
- config: PaywallConfig,
- introOffer: Product.SubscriptionOffer? = nil
- ) -> String {
- switch self {
- case .lifetime:
- return config.lifetimeDisclosure
- case .monthly, .yearly:
- guard let product else { return config.loadingDisclosure }
- let price = product.displayPrice
- let period = product.subscription.map {
- config.subscriptionPeriodDescription(from: $0.subscriptionPeriod)
- } ?? config.messages.periodFallback
- if self == .yearly, let introOffer {
- let trial = config.trialDurationDescription(from: introOffer, lowercase: true)
- return config.trialDisclosureTemplate
- .replacingOccurrences(of: "{trial}", with: trial)
- .replacingOccurrences(of: "{price}", with: price)
- .replacingOccurrences(of: "{period}", with: period)
- }
- return config.subscriptionDisclosureTemplate
- .replacingOccurrences(of: "{price}", with: price)
- .replacingOccurrences(of: "{period}", with: period)
- }
- }
- }
- struct PaywallFeature: Identifiable {
- let id = UUID()
- let title: String
- let systemImage: String
- }
- enum PaywallContent {
- static let title = "Unlock Full Pro Power"
- static let subtitle = "Generate unlimited posts, optimize titles, and craft smarter replies with advanced AI."
- static let includedSectionTitle = "Everything included in Premium"
- static let features: [PaywallFeature] = [
- PaywallFeature(title: "Unlimited AI Generations", systemImage: "sparkles"),
- PaywallFeature(title: "Post Generator", systemImage: "square.and.pencil"),
- PaywallFeature(title: "Title Optimizer", systemImage: "textformat.size"),
- PaywallFeature(title: "Comment Writer", systemImage: "bubble.left.and.bubble.right.fill"),
- PaywallFeature(title: "Custom AI Prompts", systemImage: "wand.and.stars"),
- PaywallFeature(title: "Post Analytics", systemImage: "chart.bar.fill"),
- ]
- static let continueWithFreePlan = "Continue with free plan"
- static let restorePurchases = "Restore Purchases"
- static let privacyPolicy = "Privacy Policy"
- static let support = "Support"
- static let termsOfService = "Terms of Services"
- }
|