ProductリストをUIに表示させるバックエンドフローは以下
// array of fetched product ids
var identifiers: [String] = []
// array of Product
var products: [Products] = []
// fetch from server and store data in Keychain
func fetchProductsFromServer() async {
let url = URL(string: "https://jamapp.me/test_getInAppProducts")!
do {
let (data, _) = try await URLSession.shared.data(from: url)
KeychainWrapper.standard.set(data, forKey: dataKey)
} catch {
print(error)
}
}
// decode from keychain and fill identifier array
func decodeProductsFromKeychain() async {
guard let data = KeychainWrapper.standard.data(forKey: self.dataKey) else { return }
do {
let decode = try JSONDecoder().decode(ServerResponse.self, from: data)
decodedProducts = Set(decode.products)
for product in decode.products {
identifiers.insert(product.identifier)
}
} catch {
print(error)
}
}
// fetch from AppStore
func fetchProductsFromAppStore() async {
do {
products = try await Product.products(for: identifiers)
for product in products {
if await product.currentEntitlement != nil {
// storing currentEntitlement in Keychain for off-line use...
KeychainWrapper.standard.set(true, forKey: product.id)
// storing in isPurchasedDict to update UI
await MainActor.run {
self.isPurchasedDict[product.id] = KeychainWrapper.standard.bool(forKey: product.id)
}
} else {
KeychainWrapper.standard.set(false, forKey: product.id)
await MainActor.run {
self.isPurchasedDict[product.id] = KeychainWrapper.standard.bool(forKey: product.id)
}
}
}
} catch {
print(error)
}
}
in order to handle any unhandled transactions, listen for Transaction.updates in appDelegate
extension AppDelegate: UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// listening for transaction updates
Task {
await listenForTransactions()
}
return true
}
func listenForTransactions() async {
for await verificationResult in Transaction.updates {
switch verificationResult {
case .verified(let transaction):
KeychainWrapper.standard.set(true, forKey: transaction.productID)
await transaction.finish()
case .unverified(let transaction, _):
await transaction.finish()
}
}
}
}
in order to buy a product, do below.
func buyProduct(product: Product) {
Task {
do {
let result = try await product.purchase()
switch result {
case .success(let verification):
switch verification {
case .verified(let transaction):
await updatePurchase(transaction: transaction)
await transaction.finish()
case .unverified(let transaction, _):
await transaction.finish()
}
case .userCancelled:
()
case .pending:
()
@unknown default:
()
}
} catch {
print(error)
}
}
}
func updatePurchase(transaction: Transaction) async {
KeychainWrapper.standard.set(true, forKey: transaction.productID)
await MainActor.run {
isPurchasedDict[transaction.productID] = true
}
}