final class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver { static let shared = IAPManager() private var products = [SKProduct]() private var productBeingPurchased: SKProduct? enum Product: String, CaseIterable { case removeAds = "JokesRUs.RemoveAds" case summerIcon = "JokesRUs.Icons.Summer" case tacoIcon = "JokesRUs.Icons.Taco" case pizzaIcon = "JokesRUs.Icons.Pizza" case hotdogIcon = "JokesRUs.Icons.HotDog" case hamburgerIcon = "JokesRUs.Icons.Hamburger" case doughnutIcon = "JokesRUs.Icons.Doughnut" } let group = DispatchGroup() public func fetchProducts() { let request = SKProductsRequest(productIdentifiers: Set(Product.allCases.compactMap(({ $0.rawValue })))) request.delegate = self request.start() } func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { products = response.products } func request(_ request: SKRequest, didFailWithError error: Error) { guard request is SKProductsRequest else { return } print("Product fetch request failed") } public func purchase(product: Product, completion: @escaping () -> Void) { guard SKPaymentQueue.canMakePayments() else { return } guard let storeKitProduct = products.first(where: { $0.productIdentifier == product.rawValue }) else { return } Utilities.purchaseFailed = Bool() let paymentRequest = SKPayment(product: storeKitProduct) SKPaymentQueue.default().add(self) SKPaymentQueue.default().add(paymentRequest) completion() } public func restore() { print("start restore") Utilities.restoredPurchases = false SKPaymentQueue.default().restoreCompletedTransactions() } func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { transactions.forEach({ transaction in switch transaction.transactionState { case .purchasing: //No op print("purchasing") break case .purchased: handlePurchase(transaction.payment.productIdentifier) Utilities.purchaseFailed = false break case .failed: print("purchase failed") Utilities.purchaseFailed = true Utilities.restoredPurchases = false NotificationCenter.default.post(name: NSNotification.Name(rawValue: "checkFailedPurchase"), object: nil) break case .restored: print("purchases restored") handlePurchase(transaction.payment.productIdentifier) Utilities.restoredPurchases = true print("finish restore") NotificationCenter.default.post(name: NSNotification.Name(rawValue: "checkRestored"), object: nil) SKPaymentQueue.default().finishTransaction(transaction) break case .deferred: print("deferred") break @unknown default: print("default") break } }) } private func handlePurchase(_ id: String) { print("Pass on Icon = \(IconChange.passOnIcon)") UserDefaults.standard.setValue(true, forKey: id) print("id = \(id)") Utilities.purchaseID = id if id == "JokesRUs.RemoveAds" { Utilities.ShowAds = false NotificationCenter.default.post(name: NSNotification.Name(rawValue: "AdsRemoved"), object: nil) } if id == "JokesRUs.Icons.Summer" { print("summer bought") IconChange.setIconPurchased = true IconChange.summerIconUnlocked = true IconChange.numUnlocked = IconChange.numUnlocked + 1 UserDefaults.standard.setValue(IconChange.numUnlocked + 1, forKey: "numUnlocked") UserDefaults.standard.setValue(true, forKey: "summerIconUnlocked") NotificationCenter.default.post(name: NSNotification.Name(rawValue: "setIcon"), object: nil) } if id == "JokesRUs.Icons.Taco" { print("taco bought") IconChange.setIconPurchased = true IconChange.tacoIconUnlocked = true IconChange.numUnlocked = IconChange.numUnlocked + 1 UserDefaults.standard.setValue(IconChange.numUnlocked + 1, forKey: "numUnlocked") UserDefaults.standard.setValue(true, forKey: "tacoIconUnlocked") NotificationCenter.default.post(name: NSNotification.Name(rawValue: "setIcon"), object: nil) } if id == "JokesRUs.Icons.Pizza" { print("pizza bought") IconChange.setIconPurchased = true IconChange.pizzaIconUnlocked = true IconChange.numUnlocked = IconChange.numUnlocked + 1 UserDefaults.standard.setValue(IconChange.numUnlocked + 1, forKey: "numUnlocked") UserDefaults.standard.setValue(true, forKey: "pizzaIconUnlocked") NotificationCenter.default.post(name: NSNotification.Name(rawValue: "setIcon"), object: nil) } if id == "JokesRUs.Icons.HotDog" { print("hotdog bought") IconChange.setIconPurchased = true IconChange.hotdogIconUnlocked = true IconChange.numUnlocked = IconChange.numUnlocked + 1 UserDefaults.standard.setValue(IconChange.numUnlocked + 1, forKey: "numUnlocked") UserDefaults.standard.setValue(true, forKey: "hotdogIconUnlocked") NotificationCenter.default.post(name: NSNotification.Name(rawValue: "setIcon"), object: nil) } if id == "JokesRUs.Icons.Hamburger" { print("hamburger bought") IconChange.setIconPurchased = true IconChange.hamburgerIconUnlocked = true IconChange.numUnlocked = IconChange.numUnlocked + 1 UserDefaults.standard.setValue(IconChange.numUnlocked + 1, forKey: "numUnlocked") UserDefaults.standard.setValue(true, forKey: "hamburgerIconUnlocked") NotificationCenter.default.post(name: NSNotification.Name(rawValue: "setIcon"), object: nil) } if id == "JokesRUs.Icons.Doughnut" { print("doughnut bought") IconChange.setIconPurchased = true IconChange.doughnutIconUnlocked = true IconChange.numUnlocked = IconChange.numUnlocked + 1 UserDefaults.standard.setValue(IconChange.numUnlocked + 1, forKey: "numUnlocked") UserDefaults.standard.setValue(true, forKey: "doughnutIconUnlocked") NotificationCenter.default.post(name: NSNotification.Name(rawValue: "setIcon"), object: nil) } } }