Skip to content

Instantly share code, notes, and snippets.

@smaljaar
Forked from DejanEnspyra/IAPHandler.swift
Last active April 13, 2018 19:05
Show Gist options
  • Save smaljaar/3d391ff0db9bdc4654626c002b295afa to your computer and use it in GitHub Desktop.
Save smaljaar/3d391ff0db9bdc4654626c002b295afa to your computer and use it in GitHub Desktop.
[SWIFT] How to add In-App Purchases in your iOS app.
//
// 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