Last active
May 19, 2021 12:25
-
-
Save egzonpllana/abd6d385bb45b1e329fe85a624ee531f to your computer and use it in GitHub Desktop.
StoreKit iOS Swift InAppPurchase Process SKPayment
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
// Created on 7/9/2020. | |
// | |
// Developed by: Kilo Loco | |
// Improved by Egzon Pllana | |
import Foundation | |
import StoreKit | |
protocol IAPServiceDelegate: class { | |
func didFinish(withTransaction transaction: SKPaymentTransaction, withProductIdentifier productIdentifier: String, withTransactionState transactionState: SKPaymentTransactionState) | |
} | |
class IAPService: NSObject { | |
// MARK: - Initializer | |
private override init() {} | |
// MARK: - Properties | |
static let shared = IAPService() | |
var products: [SKProduct] = [] | |
var didFinishRetrievingProducts: (() -> Void)? | |
let paymentQueue = SKPaymentQueue() | |
var delegate: IAPServiceDelegate? | |
func getProducts() { | |
let products: Set = [IAPProduct.yearlyNoTrial.identifier, IAPProduct.yearlySevenDayTrial.identifier] | |
let request = SKProductsRequest(productIdentifiers: products) | |
request.delegate = self | |
request.start() | |
paymentQueue.add(self) | |
} | |
func purchase(product: IAPProduct) { | |
guard let productToPurchase = products.filter({ $0.productIdentifier == product.identifier }).first else { | |
print("ProductToPurchase not found: ", product.identifier) | |
return | |
} | |
let payment = SKPayment(product: productToPurchase) | |
paymentQueue.add(payment) | |
} | |
func restorePurchases() { | |
print("restoring purchaes") | |
paymentQueue.restoreCompletedTransactions() | |
} | |
} | |
// MARK: - SKProducts Request Delegate | |
extension IAPService: SKProductsRequestDelegate { | |
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { | |
self.products = response.products | |
if response.products.count > 0 { | |
didFinishRetrievingProducts?() | |
} | |
response.products.forEach({ print("Product Identifier: \($0.localizedTitle)") }) | |
} | |
} | |
// MARK: - SKPayment Transaction Observer | |
extension IAPService: SKPaymentTransactionObserver { | |
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { | |
transactions.forEach({ | |
print($0.transactionState.status(), $0.payment.productIdentifier) | |
switch $0.transactionState { | |
case .purchasing: break | |
default: | |
self.delegate?.didFinish(withTransaction: $0, withProductIdentifier: $0.payment.productIdentifier, withTransactionState: $0.transactionState) | |
queue.finishTransaction($0) | |
} | |
}) | |
} | |
} | |
// MARK: - SKPaymentTransactionState: status() | |
extension SKPaymentTransactionState { | |
func status() -> String { | |
switch self { | |
case .purchasing: return "purchasing" | |
case .purchased: return "purchased" | |
case .failed: return "failed" | |
case .restored: return "restored" | |
case .deferred: return "deferred" | |
} | |
} | |
} | |
// How to use in your ViewController | |
class MembershipSliderController: UIViewController { | |
//MARK: - View Life Cycle | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
// IAPServiceDelegate | |
IAPService.shared.delegate = self | |
IAPService.shared.getProducts() | |
IAPService.shared.didFinishRetrievingProducts = { [ weak self ] in | |
guard let self = self else { return } | |
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { | |
// Manage UI with loader or any. | |
// self. | |
} | |
} | |
} | |
// MARK: - Actions | |
@IBAction func subscribeButtonPressed(_ sender: UIButton) { | |
IAPService.shared.purchase(product: .yearlyNoTrial) | |
} | |
} | |
// MARK: - IAPServiceDelegate | |
extension MembershipSliderController: IAPServiceDelegate { | |
func didFinish(withTransaction transaction: SKPaymentTransaction, withProductIdentifier productIdentifier: String, withTransactionState transactionState: SKPaymentTransactionState) { | |
guard let purchasedProduct = IAPService.shared.products.filter({ $0.productIdentifier == productIdentifier }).first else { | |
print("Could not identify the purchased product with identifier: ", productIdentifier) | |
// show information alert | |
return | |
} | |
// API Call if needed with data from "purchasedProduct" | |
} | |
} | |
// New file -> IAPProduct.swift | |
import Foundation | |
enum IAPProduct { | |
case yearlySevenDayTrial | |
case yearlyNoTrial | |
var identifier: String { | |
switch self { | |
case .yearlySevenDayTrial: return "com.docapp.autoyearly" | |
case .yearlyNoTrial: return "com.docapp.autoyearlynotrial" | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment