I have another question about auto-renewable subscriptions. I am currently trying to implement In-App purchase subscription choices (one monthly and one yearly; both are in the same subscription group) to my app using SubscriptionStoreView and the modifiers .onInAppPurchaseCompletion, .subscriptionStatusTask, and .manageSubscriptionSheet. I do NOT offer any other type of In-App purchases and will not be in the future.
This question specifically deals with using .subscriptionStatusTask. Is there a way in this closure to know exactly which subscription has been purchased? I thought I figured this out by looking at the value returned, mapping it, and then looking at transaction payloadValue but I don’t think that this is necessarily correct. For example, if I buy the monthly subscription and then immediately upgrade and buy the yearly, the transaction.payloadValue.productID still shows monthly (presumably because the monthly subscription must finish first?). My app needs to know if the user actually purchased yearly because even more options are available to them if they did. How/where can I see this just using .subscriptionStatusTask?
Below is some sample code that I hastily threw together. It has basic logic and comments/questions for stuff I’m trying to figure out.
struct ContentViewSO2: View {
@State private var paywallShown = false
@State private var isPro = false
@State private var presentingSubscriptionSheet = false
@State private var showManageSubscriptionButton = false
@State private var subType = ""
var body: some View {
VStack(spacing: 75){
Text("Testing Store Paywall and Subscription Status Sheet")
.multilineTextAlignment(.center)
.foregroundColor(.black)
.font(.system(size: 18, weight: .heavy, design: .rounded))
.padding(.vertical, 75)
.subscriptionStatusTask(for: "C5FF4E1D") { taskState in
if let value = taskState.value {
//.. check to see if there was EVER a subscription in order to know whether or not to present the .manageSubscriptionSheet. It seems that if there was never a subscription, the .manageSubscriptionSheet will not appear... it just clocks
if value.count > 0 {
showManageSubscriptionButton = true
} else {
showManageSubscriptionButton = false
}
let _: [()] = value.map { status in
//.. transaction = what's currently purchased???????
let statusTransaction = status.transaction
let payloadValue = try? status.transaction.payloadValue
let prodId = payloadValue?.productID
print("product id = (prodId)")
if prodId == "com.HR.test.yearly" {
subType = "yearly"
} else if prodId == "com.HR.test.monthly" {
subType = "monthly"
} else {
subType = "none"
}
//.. if there is a value that's not .revoked and not .expired
isPro = !value
.filter { $0.state != .revoked && $0.state != .expired }
.isEmpty
//.. for more debugging
let renewalState = status.state
switch renewalState {
case .revoked:
print("renewalState = revoked")
case .expired:
print("renewalState = expired")
case .subscribed:
print("renewalState = subscribed")
case .inBillingRetryPeriod:
print("renewalState = inBillingRetryPeriod")
case .inGracePeriod:
print("renewalState = inGracePeriod")
default:
print("renewalState = ???")
}
}
}
}
isPro ?
Text("IS PRO FROM .subscriptionStatusTask")
.foregroundColor(.white)
.background(Color.green)
:
Text("IS NOT PRO FROM .subscriptionStatusTask")
.foregroundColor(.white)
.background(Color.red)
switch subType {
case "monthly":
Text("Monthly Subscription")
.padding(.horizontal, 25)
.foregroundColor(.white)
.background(Color.blue)
case "yearly":
Text("Yearly Subscription")
.padding(.horizontal, 25)
.foregroundColor(.white)
.background(Color.teal)
default:
Text("No Subscription")
.padding(.horizontal, 25)
.foregroundColor(.white)
.background(Color.red)
}
//.. check to see if there was EVER a subscription in order to know whether to present the .manageSubscriptionSheet. It seems that if there was never a subscription, the .manageSubscriptionSheet will not appear... it just clocks. If there has not been any subscription EVER, show paywall instead of manage subscription sheet
if !showManageSubscriptionButton {
Button(action: {
paywallShown = true
}, label: {
Text("Press to test paywall")
})
.buttonStyle(.borderedProminent)
.sheet(isPresented: $paywallShown) {
HRShop()
}
} else {
Button(action: {
self.presentingSubscriptionSheet = true
}, label: {
Text("Show .manageSubScriptionSheet")
})
.manageSubscriptionsSheet(
isPresented: $presentingSubscriptionSheet,
subscriptionGroupID: "C5FF4E1D"
)
}
}
Spacer()
}
}
Note: When I purchase a monthly subscription and then immediately purchase the yearly (via .manageSubscriptionSheet), the var prodId that I get back is still showing a monthly subscription. When I look at Debug -> StoreKit -> Manage Transactions, it shows that the subscription is “monthly”, that it will expire 9-7-24, and that a yearly subscription will renew. However, if I click on my manage subscription button again, it shows a yearly subscription. So, how can I see in .subscriptionStatusTask where I actually purchased a yearly subscription?
Also, if I then cancel the yearly subscription from the .manageSubscriptionSheet, it shows a message that says, “if you confirm and end your subscription now, you can still access it until Sept 7, 2024. Why 2024? Shouldn’t this be 2025? Did I already actually “pay” for a year or is this payment delayed until the monthly runs out? If I actually confirm the cancel, Debug -> StoreKit -> Manage Transactions shows they still have a monthly subscription but that now the subscription (monthly or yearly) will NOT renew.
Question: So, when a user changes subscriptions via .manageSubscriptionSheet, are the actual purchases immediate? I’m kind of thinking they are not, at least in this particular scenario. Which means that if a user buys the monthly option first and then initiates a yearly purchase also, that I have to have my code look at renewal info also (which I’m not doing here), grant them yearly access features based on that, hope that they don’t cancel that subscription, and drop them back down to monthly features if they do cancel the yearly renewal. Correct?
What else am I missing and/or am I not understanding correctly? As always, any help would be greatly appreciated.