-
-
Save smaljaar/3d391ff0db9bdc4654626c002b295afa to your computer and use it in GitHub Desktop.
[SWIFT] How to add In-App Purchases in your iOS app.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// IAPHandler.swift | |
// | |
// Created by Dejan Atanasov on 13/07/2017. | |
// Copyright © 2017 Dejan Atanasov. All rights reserved. | |
// | |
import UIKit | |
import StoreKit | |
enum PurchaseEvent { | |
case disabled | |
case restored | |
case failedToGetProductFromStore | |
case failedToRestorePurchases | |
case cancelled | |
case completed | |
func message() -> String{ | |
switch self { | |
case .disabled: return "Purchases are disabled on your device." | |
case .restored: return "You have successfully restored your purchase." | |
case .failedToGetProductFromStore: return "The product could not be retrieved. Try again by relaunching the app." | |
case .failedToRestorePurchases: return "Restoring your purchases was unsuccessful. Please try again." | |
case .cancelled, .completed: return ""//don't do anything | |
} | |
} | |
} | |
class IAPHandler: NSObject { | |
private override init() {} | |
static let shared = IAPHandler() | |
fileprivate var productsRequest = SKProductsRequest() | |
fileprivate var iapProductsRetrievedFromAppStore = [SKProduct]() | |
/// Shows an alert message informing about success or failure in certain cases. | |
var onPurchaseProcessingStopped: ((PurchaseEvent) -> Void)? | |
/// Enable to update UI when payment processing has started | |
var onBeginPaymentProcess: (() -> Void)? | |
// MARK: - MAKE PURCHASE OF A PRODUCT | |
func purchaseMyProduct(productIdentifier: String){ | |
if iapProductsRetrievedFromAppStore.count == 0 { return } | |
if SKPaymentQueue.canMakePayments() { | |
guard let product = productWithIdentifier(productIdentifier) else { | |
onPurchaseProcessingStopped?(.failedToGetProductFromStore) | |
return | |
} | |
onBeginPaymentProcess?() | |
let payment = SKPayment(product: product) | |
SKPaymentQueue.default().add(self) | |
SKPaymentQueue.default().add(payment) | |
print("PRODUCT TO PURCHASE: \(product.productIdentifier)") | |
} else { | |
onPurchaseProcessingStopped?(.disabled) | |
} | |
} | |
private func productWithIdentifier(_ productIdentifier: String) -> SKProduct? { | |
return iapProductsRetrievedFromAppStore.first(where: { (product) -> Bool in | |
return product.productIdentifier == productIdentifier | |
}) | |
} | |
// MARK: - RESTORE PURCHASE | |
func restorePurchase(){ | |
SKPaymentQueue.default().add(self) | |
SKPaymentQueue.default().restoreCompletedTransactions() | |
} | |
// MARK: - FETCH AVAILABLE IAP PRODUCTS | |
func fetchAvailableProducts(){ | |
// Put here your IAP Products ID's | |
let productIdentifiers: Set = ["iapProductIdentifierExample"] | |
productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers) | |
productsRequest.delegate = self | |
productsRequest.start() | |
} | |
} | |
extension IAPHandler: SKProductsRequestDelegate, SKPaymentTransactionObserver { | |
// MARK: - REQUEST IAP PRODUCTS | |
func productsRequest (_ request:SKProductsRequest, didReceive response:SKProductsResponse) { | |
if (response.products.count > 0) { | |
iapProductsRetrievedFromAppStore = response.products | |
for product in iapProductsRetrievedFromAppStore{ | |
let numberFormatter = NumberFormatter() | |
numberFormatter.formatterBehavior = .behavior10_4 | |
numberFormatter.numberStyle = .currency | |
numberFormatter.locale = product.priceLocale | |
let price1Str = numberFormatter.string(from: product.price) | |
print(product.localizedDescription + "\nfor just \(price1Str!)") | |
} | |
} | |
} | |
//MARK: - Restoring Purchases | |
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) { | |
onPurchaseProcessingStopped?(.failedToRestorePurchases) | |
} | |
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { | |
onPurchaseProcessingStopped?(.restored) | |
} | |
//MARK: - IAP PAYMENT QUEUE | |
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { | |
for transaction in transactions { | |
switch transaction.transactionState { | |
case .purchased, .restored: | |
handlePurchasedOrRestored(transaction: transaction) | |
case .failed: //called also when user taps cancel after starting the purchase. | |
SKPaymentQueue.default().finishTransaction(transaction) | |
onPurchaseProcessingStopped?(.cancelled) | |
case .deferred, .purchasing: | |
break | |
} | |
} | |
} | |
//TODO receipt, call NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; | |
//https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW2 | |
func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool { | |
// check if user can purchase the product | |
return true // or false if user shall not purchase the product yet | |
} | |
private func handlePurchasedOrRestored(transaction: SKPaymentTransaction) { | |
SKPaymentQueue.default().finishTransaction(transaction) | |
UserDefaults.standard.set(true, forKey: transaction.payment.productIdentifier) | |
NotificationCenter.default.post(name: Notification.Name(rawValue: transaction.payment.productIdentifier), object: nil) | |
onPurchaseProcessingStopped?(.completed) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment