parent
e40f9d672f
commit
0ab3149980
@ -0,0 +1,111 @@ |
|||||||
|
// |
||||||
|
// Store.swift |
||||||
|
// Poker Analytics 6 |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 20/04/2022. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import StoreKit |
||||||
|
|
||||||
|
//public enum StoreError: Error { |
||||||
|
// case failedVerification |
||||||
|
//} |
||||||
|
|
||||||
|
protocol StoreDelegate { |
||||||
|
func productsReceived() |
||||||
|
func errorDidOccur(error: Error) |
||||||
|
} |
||||||
|
|
||||||
|
//extension Notification.Name { |
||||||
|
// static let StoreEventHappened = Notification.Name("storePurchaseSucceeded") |
||||||
|
//} |
||||||
|
|
||||||
|
class Store: ObservableObject { |
||||||
|
|
||||||
|
@Published private(set) var products: [Product] = [] |
||||||
|
|
||||||
|
@Published private(set) var purchasedTransactions = Set<StoreKit.Transaction>() |
||||||
|
|
||||||
|
var delegate: StoreDelegate? = nil |
||||||
|
|
||||||
|
var updateListenerTask: Task<Void, Error>? = nil |
||||||
|
|
||||||
|
init() { |
||||||
|
|
||||||
|
self.updateListenerTask = listenForTransactions() |
||||||
|
|
||||||
|
Task { |
||||||
|
//Initialize the store by starting a product request. |
||||||
|
await self.requestProducts() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
deinit { |
||||||
|
self.updateListenerTask?.cancel() |
||||||
|
} |
||||||
|
|
||||||
|
func indexOf(identifier: String) -> Int? { |
||||||
|
return self.products.map { $0.id }.firstIndex(of: identifier) |
||||||
|
} |
||||||
|
|
||||||
|
@MainActor |
||||||
|
func requestProducts() async { |
||||||
|
do { |
||||||
|
|
||||||
|
let currentPlan = AppGuard.main.currentPlan |
||||||
|
let identifiers: [String] = [StorePlan.unlimited.rawValue] |
||||||
|
products = try await Product.products(for: identifiers) |
||||||
|
Logger.log("products = \(self.products.count)") |
||||||
|
self.delegate?.productsReceived() |
||||||
|
} 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 AppGuard.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) async throws -> StoreKit.Transaction? { |
||||||
|
// Begin a purchase. |
||||||
|
let result = try await product.purchase() |
||||||
|
|
||||||
|
switch result { |
||||||
|
case .success(let verificationResult): |
||||||
|
|
||||||
|
let transaction = try await AppGuard.main.processTransactionResult(verificationResult) |
||||||
|
|
||||||
|
// Always finish a transaction. |
||||||
|
await transaction.finish() |
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime(uptimeNanoseconds: 200000), execute: { |
||||||
|
NotificationCenter.default.post(name: Notification.Name.StoreEventHappened, object: nil) |
||||||
|
}) |
||||||
|
|
||||||
|
return transaction |
||||||
|
case .userCancelled, .pending: |
||||||
|
return nil |
||||||
|
default: |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue