Skip to content

Instantly share code, notes, and snippets.

@egzonpllana
Last active May 19, 2021 12:25
Show Gist options
  • Save egzonpllana/abd6d385bb45b1e329fe85a624ee531f to your computer and use it in GitHub Desktop.
Save egzonpllana/abd6d385bb45b1e329fe85a624ee531f to your computer and use it in GitHub Desktop.
StoreKit iOS Swift InAppPurchase Process SKPayment
// 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