I have a ContentView
which uses an EnvironmentObject
. After an In-App Purchase this object should be saved to the CoreData
. My ContentView
looks something like this:
//ContentView
import SwiftUI
struct ContentView: View {
@EnvironmentObject var modelData: ModelData
@Environment(\.managedObjectContext) private var viewContext
@StateObject var storeManager: StoreManager
var body: some View {
Button(action: {
purchaseAndSaveToCoreData()
}, label: {
Text("buy for 100")
})
}
func purchaseAndSaveToCoreData() {
//Open the storeManager and do the purchase
storeManager.purchaseProduct(product: storeManager.myProducts[0])
//Wait for the purchase callback and then save an object from the EnvironmentObject modelData to CoreData here <- How?
let newItemToStore = Item(context: viewContext)
newItemToStore.name = modelData.name
do {
try viewContext.save()
} catch {
print(error.localizedDescription)
}
}
}
And my StoreManager
class is this:
//StoreManager
import Foundation
import StoreKit
class StoreManager: NSObject, ObservableObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
//FETCH PRODUCTS
//[...] Fetching Process goes here
//HANDLE TRANSACTIONS
@Published var transactionState: SKPaymentTransactionState?
func purchaseProduct(product: SKProduct) {
if SKPaymentQueue.canMakePayments() {
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
} else {
print("User can't make payment.")
}
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchasing:
transactionState = .purchasing
case .purchased:
UserDefaults.standard.setValue(true, forKey: transaction.payment.productIdentifier)
queue.finishTransaction(transaction)
transactionState = .purchased
//Can I use my environmentObject modelData here?
case .restored:
UserDefaults.standard.setValue(true, forKey: transaction.payment.productIdentifier)
queue.finishTransaction(transaction)
transactionState = .restored
case .failed, .deferred:
print("Payment Queue Error: \(String(describing: transaction.error))")
queue.finishTransaction(transaction)
transactionState = .failed
default:
queue.finishTransaction(transaction)
}
}
}
func restoreProducts() {
print("Restoring products ...")
SKPaymentQueue.default().restoreCompletedTransactions()
}
}
Everything works fine, the purchase takes place. But I can't get a crucial thing to work. How can I wait inside the ContentView
for a callback of the paymentQueue
when it's in .purchased
mode?
I thought about the @Published transactionState
and wanted to use it inside the purchaseAndSaveToCoreData()
function like this:
//ContentView
//[...]
storeManager.purchaseProduct(product: storeManager.myProducts[0])
//Wait for the purchase callback and then save an object from the EnvironmentObject modelData to CoreData here <- How?
if storeManager.transactionState == .purchased {
let newItemToStore = Item(context: viewContext)
newItemToStore.name = modelData.name
do {
try viewContext.save()
} catch {
print(error.localizedDescription)
}
}
But the if
statement runs of course immediately after the storeManager.purchaseProduct()
function and doesn't fire. I know the right timing to look for is inside the StoreManager
at the case .purchased
. But I can't use my EnvironmentObject
there. I'm relatively new to Swift so I might be missing a key feature about Callbacks, States or triggering functions here.