Last active
January 10, 2017 20:56
-
-
Save danielt1263/8f59ed66ff0bb66e8d6cd261210d356e to your computer and use it in GitHub Desktop.
App Store implementation
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
// | |
// AppStore.swift | |
// | |
// Created by Daniel Tartaglia on 1/10/17. | |
// Copyright © 2017 Daniel Tartaglia. MIT License. | |
// | |
import Foundation | |
import StoreKit | |
enum AppStorePaymentTransactionState { | |
case purchasing // Transaction is being added to the server queue. | |
case purchased // Transaction is in queue, user has been charged. | |
case failed // Transaction was cancelled or failed before being added to the server queue. | |
case restored // Transaction was restored from user's purchase history. | |
case deferred // The transaction is in the queue, but its final status is pending external action. | |
} | |
protocol AppStore { | |
var products: [AppStoreProduct] { get } | |
var canMakePurchases: Bool { get } | |
var delegate: AppStoreDelegate? { get set } | |
func fetchAvailableProducts() | |
func restorePurchases() | |
func purchase(product: AppStoreProduct) | |
} | |
protocol AppStoreDelegate { | |
func productDescriptionsLoaded() | |
func restorePurchasesComplete() | |
func purchase(product: AppStoreProduct, state: AppStorePaymentTransactionState) | |
} | |
struct AppStoreProduct { | |
var price: NSDecimalNumber | |
var priceLocale: Locale | |
var localizedTitle: String | |
var localizedDescription: String | |
var productIdentifier: String | |
} | |
// MARK: implementation | |
private extension AppStoreProduct { | |
init(product: SKProduct) { | |
price = product.price | |
priceLocale = product.priceLocale | |
localizedTitle = product.localizedTitle | |
localizedDescription = product.localizedDescription | |
productIdentifier = product.productIdentifier | |
} | |
} | |
private extension AppStorePaymentTransactionState { | |
init(state: SKPaymentTransactionState) { | |
switch state { | |
case .purchasing: | |
self = .purchasing | |
case .purchased: | |
self = .purchased | |
case .failed: | |
self = .failed | |
case .restored: | |
self = .restored | |
case .deferred: | |
self = .deferred | |
} | |
} | |
} | |
class AppStoreImpl: NSObject, AppStore, SKProductsRequestDelegate, SKPaymentTransactionObserver | |
{ | |
var delegate: AppStoreDelegate? | |
var products: [AppStoreProduct] { | |
return iapProducts.map { AppStoreProduct(product: $0) } | |
} | |
init(productIDs: [String]) { | |
self.productIDs = productIDs | |
} | |
func fetchAvailableProducts() { | |
let productIdentifiers = NSSet(array: productIDs) | |
productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>) | |
productsRequest.delegate = self | |
productsRequest.start() | |
} | |
func restorePurchases() { | |
SKPaymentQueue.default().add(self) | |
SKPaymentQueue.default().restoreCompletedTransactions() | |
} | |
var canMakePurchases: Bool { | |
return SKPaymentQueue.canMakePayments() | |
} | |
func purchase(product: AppStoreProduct) { | |
guard let index = iapProducts.index(where: { $0.productIdentifier == product.productIdentifier }) else { return } | |
let payment = SKPayment(product: iapProducts[index]) | |
SKPaymentQueue.default().add(self) | |
SKPaymentQueue.default().add(payment) | |
print("PRODUCT TO PURCHASE: \(product.productIdentifier)") | |
} | |
// MARK: SKProductsRequestDelegate | |
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { | |
if (response.products.count > 0) { | |
iapProducts = response.products | |
delegate?.productDescriptionsLoaded() | |
} | |
} | |
// MARK: SKPaymentTransactionObserver | |
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { | |
for transaction in transactions { | |
if let productIndex = products.index(where: { $0.productIdentifier == transaction.payment.productIdentifier }) { | |
let purchaseState = AppStorePaymentTransactionState(state: transaction.transactionState) | |
delegate?.purchase(product: products[productIndex], state: purchaseState) | |
switch transaction.transactionState { | |
case .purchased, .restored: | |
SKPaymentQueue.default().finishTransaction(transaction) | |
default: break | |
} | |
} | |
} | |
} | |
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { | |
delegate?.restorePurchasesComplete() | |
} | |
private var productsRequest = SKProductsRequest() | |
private var iapProducts = [SKProduct]() | |
private let productIDs: [String] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment