I have a code for auto-renewable subscriptions, how to close the subscription window after paying for the subscription and display the ContentView() structure.
import Foundation
import StoreKit
typealias FetchCompletionHandler = (([SKProduct]) -> Void)
typealias PurchaseCompletionHandler = ((SKPaymentTransaction?) -> Void)
class Store: NSObject, ObservableObject {
@Published var allRecipes = [Recipe]()
private let allProductIdentifiers = Set([
"PRODUCT_ID_APP_STORE_CONNECT"])
private var completedPurchases = [String]() {
didSet {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
for index in self.allRecipes.indices {
self.allRecipes[index].isLocked = !self.completedPurchases.contains(self.allRecipes[index].id)
}
}
}
}
private var productsRequest: SKProductsRequest?
private var fetchedProducts = [SKProduct]()
private var fetchCompletionHandler: FetchCompletionHandler?
private var purchaseCompletionHandler: PurchaseCompletionHandler?
private let userDefaultsKey = "PRODUCT_ID_APP_STORE_CONNECT"
override init() {
super.init()
startObservingPaymentQueue()
fetchProducts { products in
self.allRecipes = products.map { Recipe(product: $0) }
}
}
func loadStorePurchases() {
if let storePurchases = UserDefaults.standard.object(forKey: userDefaultsKey) as? [String] {
self.completedPurchases = storePurchases
}
}
private func startObservingPaymentQueue() {
SKPaymentQueue.default().add(self)
}
private func fetchProducts(_ completion: @escaping FetchCompletionHandler) {
guard self.productsRequest == nil else { return }
fetchCompletionHandler = completion
productsRequest = SKProductsRequest(productIdentifiers: allProductIdentifiers)
productsRequest?.delegate = self
productsRequest?.start()
}
private func buy(_ product: SKProduct, completion: @escaping PurchaseCompletionHandler) {
purchaseCompletionHandler = completion
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
}
}
extension Store {
func product(for identifier: String) -> SKProduct? {
return fetchedProducts.first(where: { $0.productIdentifier == identifier })
}
func purchaseProduct(_ product: SKProduct) {
startObservingPaymentQueue()
buy(product) { _ in }
}
func restorePurchases() {
SKPaymentQueue.default().restoreCompletedTransactions()
}
}
extension Store: SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
var shouldFinishTransaction = false
switch transaction.transactionState {
case .purchased, .restored:
completedPurchases.append(transaction.payment.productIdentifier)
shouldFinishTransaction = true
case .failed:
shouldFinishTransaction = true
case .deferred, .purchasing:
break
@unknown default:
break
}
if shouldFinishTransaction {
SKPaymentQueue.default().finishTransaction(transaction)
DispatchQueue.main.async {
self.purchaseCompletionHandler?(transaction)
self.purchaseCompletionHandler = nil
}
}
}
if !completedPurchases.isEmpty {
UserDefaults.standard.setValue(completedPurchases, forKey: userDefaultsKey)
}
}
}
extension Store: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
let loadedProducts = response.products
let invalidProducts = response.invalidProductIdentifiers
guard !loadedProducts.isEmpty else {
print("Cloud not load the products!")
if !invalidProducts.isEmpty {
print("Invalid Products found: \(invalidProducts)")
}
productsRequest = nil
return
}
// Cache the fetched products
fetchedProducts = loadedProducts
// Notifi anyone waiting on the product load
DispatchQueue.main.async {
self.fetchCompletionHandler?(loadedProducts)
self.fetchCompletionHandler = nil
self.productsRequest = nil
}
}
}
structure Recipe()
import Foundation
import StoreKit
struct Recipe: Hashable {
let id: String
let title: String
let description: String
var isLocked: Bool
var price: String?
let locale: Locale
let imageName: String
lazy var formatter: NumberFormatter = {
let nf = NumberFormatter()
nf.numberStyle = .currency
nf.locale = locale
return nf
}()
init(product: SKProduct, isLocked: Bool = true) {
self.id = product.productIdentifier
self.title = product.localizedTitle
self.description = product.localizedDescription
self.isLocked = isLocked
self.locale = product.priceLocale
self.imageName = product.productIdentifier
if isLocked {
self.price = formatter.string(from: product.price)
}
}
}
this is my structure with subscribe display and subscribe button
import SwiftUI
import Foundation
import StoreKit
struct Te: View {
@ObservedObject var store = Store()
var body: some View {
ZStack {
// After paying for the subscription, the subscription window disappears and the ContentViev() structure appears
ContentView()
VStack {
ZStack(alignment: .center) {
Image("subs")
.resizable()
.overlay(Color.black.opacity(0.6))
.frame(height: 600)
.blur(radius: 15)
.padding(.top)
VStack {
Text("ALL ACCESS")
.font(.system(size: 32))
.fontWeight(.bold)
.foregroundColor(.white)
.multilineTextAlignment(.center)
.padding(.bottom, 1)
Text("UNLOCK ALL ACCESS")
.foregroundColor(.white)
.font(.system(size: 14))
.multilineTextAlignment(.center)
.padding(.horizontal,35)
VStack(alignment: .leading) {
HStack {
Image(systemName: "checkmark.seal")
.resizable()
.scaledToFit()
.frame(width: 25, height: 25)
.foregroundColor(.white)
Text("Ads")
.foregroundColor(.white)
}
HStack {
Image(systemName: "checkmark.seal")
.resizable()
.scaledToFit()
.frame(width: 25, height: 25)
.foregroundColor(.white)
Text("Item")
.foregroundColor(.white)
}
HStack {
Image(systemName: "checkmark.seal")
.resizable()
.scaledToFit()
.frame(width: 25, height: 25)
.foregroundColor(.white)
Text("New")
.foregroundColor(.white)
}
}.padding(.top, 50)
Button(action: {
store.purchaseProduct(store.product(for: "PRODUCT_ID_APP_STORE_CONNECT")!)
}, label: {
Text("SUBSCRIBE")
.fontWeight(.bold)
.padding()
.padding(.horizontal,45)
.background(Color.blue.opacity(0.5))
.foregroundColor(Color.white)
.cornerRadius(10)
})
.padding(.top,45)
.padding(.bottom, 25)
}.padding(.top,5)
}
}
}
}
}
After paying for the subscription, the subscription window should disappear and the ContentView () structure will be displayed in its place, if you know about auto-renewable subscriptions, please look at my code, maybe I made a mistake somewhere. Thanks for any help