// // StatusInfoView.swift // Feels // // Created by Trey Tartt on 7/8/22. // import SwiftUI import StoreKit struct StatusInfoView: View { @EnvironmentObject var iapManager: IAPManager let product: Product let status: Product.SubscriptionInfo.Status var body: some View { ScrollView { Text(statusDescription()) .frame(maxWidth: .infinity, alignment: .center) .font(.body) } .frame(maxWidth: .infinity) .frame(height: 75) } //Build a string description of the subscription status to display to the user. fileprivate func statusDescription() -> String { guard case .verified(let renewalInfo) = status.renewalInfo, case .verified(let transaction) = status.transaction else { return "The App Store could not verify your subscription status." } var description = "" switch status.state { case .subscribed: let renewToProduct: Product? let renewalInfo: RenewalInfo? renewalInfo = try? iapManager.checkVerified(status.renewalInfo) renewToProduct = iapManager.subscriptions.first(where: { $0.key.id == renewalInfo?.autoRenewPreference })?.key description = subscribedDescription(expirationDate: transaction.expirationDate, willRenew: renewalInfo?.willAutoRenew ?? false, willRenewTo: renewToProduct) case .expired: if let expirationDate = transaction.expirationDate, let expirationReason = renewalInfo.expirationReason { description = expirationDescription(expirationReason, expirationDate: expirationDate) } case .revoked: if let revokedDate = transaction.revocationDate { description = "The App Store refunded your subscription to \(product.displayName) on \(revokedDate.formattedDate())." } case .inGracePeriod: description = gracePeriodDescription(renewalInfo) case .inBillingRetryPeriod: description = billingRetryDescription() default: break } return description } fileprivate func billingRetryDescription() -> String { var description = "The App Store could not confirm your billing information for \(product.displayName)." description += " Please verify your billing information to resume service." return description } fileprivate func gracePeriodDescription(_ renewalInfo: RenewalInfo) -> String { var description = "The App Store could not confirm your billing information for \(product.displayName)." if let untilDate = renewalInfo.gracePeriodExpirationDate { description += " Please verify your billing information to continue service after \(untilDate.formattedDate())" } return description } fileprivate func subscribedDescription(expirationDate: Date?, willRenew: Bool, willRenewTo: Product?) -> String { var description = "You are currently subscribed to \(product.displayName)" if let expirationDate = expirationDate { description += ", which will expire on \(expirationDate.formattedDate())," } if willRenew { if let willRenewTo = willRenewTo { if willRenewTo == product { description += " and will auto renew." } else { description += " and will auto renew to \(willRenewTo.displayName) at \(willRenewTo.displayPrice)." } } } else { description += " and will NOT auto renew." } return description } fileprivate func renewalDescription(_ renewalInfo: RenewalInfo, _ expirationDate: Date) -> String { var description = "" if let newProductID = renewalInfo.autoRenewPreference { if let newProduct = iapManager.subscriptions.first(where: { $0.key.id == newProductID }) { description += "\nYour subscription to \(newProduct.key.displayName)" description += " will begin when your current subscription expires on \(expirationDate.formattedDate())." } } else if renewalInfo.willAutoRenew { description += "\nWill auto renew on: \(expirationDate.formattedDate())." } return description } //Build a string description of the `expirationReason` to display to the user. fileprivate func expirationDescription(_ expirationReason: RenewalInfo.ExpirationReason, expirationDate: Date) -> String { var description = "" switch expirationReason { case .autoRenewDisabled: if expirationDate > Date() { description += "Your subscription to \(product.displayName) will expire on \(expirationDate.formattedDate())." } else { description += "Your subscription to \(product.displayName) expired on \(expirationDate.formattedDate())." } case .billingError: description = "Your subscription to \(product.displayName) was not renewed due to a billing error." case .didNotConsentToPriceIncrease: description = "Your subscription to \(product.displayName) was not renewed due to a price increase that you disapproved." case .productUnavailable: description = "Your subscription to \(product.displayName) was not renewed because the product is no longer available." default: description = "Your subscription to \(product.displayName) was not renewed." } return description } }