You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
3.8 KiB
125 lines
3.8 KiB
//
|
|
// Store.swift
|
|
// Poker Analytics 6
|
|
//
|
|
// Created by Laurent Morvillier on 20/04/2022.
|
|
//
|
|
|
|
import Foundation
|
|
import StoreKit
|
|
import LeStorage
|
|
|
|
public enum StoreManagerError: Error {
|
|
case failedVerification
|
|
case missingPlan
|
|
}
|
|
|
|
protocol StoreDelegate {
|
|
func productsReceived(products: [Product])
|
|
func errorDidOccur(error: Error)
|
|
}
|
|
|
|
extension Notification.Name {
|
|
static let StoreEventHappened = Notification.Name("storePurchaseSucceeded")
|
|
}
|
|
|
|
class StoreManager {
|
|
|
|
@Published private(set) var purchasedTransactions = Set<StoreKit.Transaction>()
|
|
|
|
var delegate: StoreDelegate? = nil
|
|
|
|
var updateListenerTask: Task<Void, Error>? = nil
|
|
|
|
init(delegate: StoreDelegate?) {
|
|
|
|
self.delegate = delegate
|
|
self.updateListenerTask = listenForTransactions()
|
|
|
|
Task {
|
|
//Initialize the store by starting a product request.
|
|
await self.requestProducts()
|
|
}
|
|
}
|
|
|
|
deinit {
|
|
self.updateListenerTask?.cancel()
|
|
}
|
|
|
|
@MainActor
|
|
func requestProducts() async {
|
|
do {
|
|
let identifiers: [String] = StoreItem.allCases.map { $0.rawValue }
|
|
Logger.log("Request products: \(identifiers)")
|
|
|
|
var products: [Product] = try await Product.products(for: identifiers)
|
|
products = products.sorted { p1, p2 in
|
|
return p2.price > p1.price
|
|
}
|
|
|
|
Logger.log("products = \(products.count)")
|
|
self.delegate?.productsReceived(products: products)
|
|
} catch {
|
|
self.delegate?.errorDidOccur(error: error)
|
|
Logger.error(error)
|
|
}
|
|
}
|
|
|
|
func listenForTransactions() -> Task<Void, Error> {
|
|
return Task.detached {
|
|
//Iterate through any transactions which didn't come from a direct call to `purchase()`.
|
|
for await result in Transaction.updates {
|
|
do {
|
|
|
|
let transaction = try await Guard.main.processTransactionResult(result)
|
|
|
|
//Always finish a transaction.
|
|
await transaction.finish()
|
|
} catch {
|
|
self.delegate?.errorDidOccur(error: error)
|
|
|
|
//StoreKit has a receipt it can read but it failed verification. Don't deliver content to the user.
|
|
print("Transaction failed verification")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func purchase(_ product: Product, quantity: Int? = nil) async throws -> StoreKit.Transaction? {
|
|
Logger.log("Store purchase started...")
|
|
|
|
var options: Set<Product.PurchaseOption> = []
|
|
let uuid: UUID = Store.main.mandatoryUserUUID()
|
|
let tokenOption = Product.PurchaseOption.appAccountToken(uuid)
|
|
options.insert(tokenOption)
|
|
|
|
if let quantity = quantity {
|
|
let quantityOption = Product.PurchaseOption.quantity(quantity)
|
|
options.insert(quantityOption)
|
|
}
|
|
|
|
let result = try await product.purchase(options: options)
|
|
|
|
Logger.log("Store purchase ended with result: \(result)")
|
|
|
|
switch result {
|
|
case .success(let verificationResult):
|
|
|
|
let transaction = try await Guard.main.processTransactionResult(verificationResult)
|
|
|
|
// Always finish a transaction.
|
|
await transaction.finish()
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime(uptimeNanoseconds: 100000), execute: {
|
|
NotificationCenter.default.post(name: Notification.Name.StoreEventHappened, object: nil)
|
|
})
|
|
|
|
return transaction
|
|
case .userCancelled, .pending:
|
|
return nil
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
}
|
|
|