When I wrote this post on Jun 22, 2018, Swift was 4 years old. In a few weeks earlier, Swift 4.2 was presented in WWDC 2018 and planned to ship with XCode 10 later on.
At the very first time, I was an Objective-C developer. I felt good with Objective-C. It was King, was number one. But Apple suddenly announced Swift to the public in WWDC 2014.
These two functions add transactions to a payment queue which completes them asynchronously and on completion you decide what to unlock in you app. It also has some functions for failed purchases or restorations and space to do something in those scenarios.
Here's the complete swift file, but note at the top, productIDs. These array elements have to be named exactly what you named them in iTunes Connect when you created them. If you make a typo, productsRequest(...) will not find it.
--- IAPHelpers.swift --- import StoreKit public var productIDs = NSSet(array: ["my.product.id1", "my.product.id2", "my.product.id3"]) public var productRequest = SKProductsRequest() public var productArray: Array = [] public var currentProductID: String! = "" public var product: SKProduct? public var transactionInProgress = false public var canMakePayments = false class IAPHelpers: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver { func requestProductInfo() -> Bool { let result: Bool if SKPaymentQueue.canMakePayments() { productRequest = SKProductsRequest(productIdentifiers: productIDs as! Set) productRequest.delegate = self productRequest.start() print("Can perform In App Purchases.") result = true } else { print("Cannot perform In App Purchases.") result = false } return result } func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) { if response.products.count != 0 { print("Here come the products.") if response.invalidProductIdentifiers.count != 0 { print(response.invalidProductIdentifiers.description) } else { for product in response.products { productArray.append(product) print(productArray) } } } else { print("Waiting for the products.") } } func beginTransaction(payment: SKPayment) { if transactionInProgress == true { return } print("Beginning transaction...") SKPaymentQueue.defaultQueue().addPayment(payment) print("adding payment...") transactionInProgress = true } func restorePurchase() { if transactionInProgress == true { return } SKPaymentQueue.defaultQueue().restoreCompletedTransactions() transactionInProgress = true } func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction: SKPaymentTransaction in transactions { switch transaction.transactionState { case .Purchased: if ( currentProductID == "my.product.id1" ) { print("unlock something") } if ( currentProductID == "my.product.id2" ) { print("unlock something") } if ( currentProductID == "my.product.id3" ) { print("unlock something") } SKPaymentQueue.defaultQueue().finishTransaction(transaction) transactionInProgress = false print("Transaction completed successfully.") break case .Failed: SKPaymentQueue.defaultQueue().finishTransaction(transaction) print("Transaction Failed") transactionInProgress = false break case .Restored: SKPaymentQueue.defaultQueue().finishTransaction(transaction) print("Transaction Restored") transactionInProgress = false break default: break } } } func paymentQueue(queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) { } func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) { for transaction:SKPaymentTransaction in queue.transactions { if ( transaction.payment.productIdentifier == "my.product.id1" ) { print("unlock something") } if ( transaction.payment.productIdentifier == "my.product.id2" ) { print("unlock something") } if ( transaction.payment.productIdentifier == "my.product.id3" ) { print("unlock something") } } print("Purchased Transactions Restored") } func paymentQueue(queue: SKPaymentQueue, updatedDownloads downloads: [SKDownload]) { } func paymentQueue(queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: NSError) { } }
Including the file above won't actually do anything on it's own. You need to use it in other parts of your app. How can we do so? This code sample is from the first ViewController "Index" that gets loaded when you enter the app. We have a button which takes us to a page with a list of things to buy. The button is disabled at first, but when the view loads, it check if the user's iTunes account can make payments and if yes, enables the button.
Clicking the button has been set up to segue us to this other View Controller which has one button to buy something and one button to restore purchases.
--- Purchase.swift --- import StoreKit class PurchaseViewController: UIViewController { let paymentTransactionObserver = IAPHelpers() @IBOutlet weak var purchaseButton: UIButton! @IBOutlet weak var restoreButton: UIButton! @IBAction func purchaseSomething(sender: UIButton) { for product in productArray { if product.productIdentifier == "my.product.id1" { currentProductID = product.productIdentifier purchaseButton.enabled = false paymentTransactionObserver.beginTransaction(SKPayment(product: product)) } } } @IBAction func restoreSomething(sender: UIButton) { paymentTransactionObserver.restorePurchase() restoreButton.enabled = false } }
Lastly, I forgot why we have to include these here in the AppDelegate.swift file, but I just know when we don't, it doesn't work.
--- AppDelegate.swift --- import StoreKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? let paymentTransactionObserver = IAPHelpers() func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // Override point for customization after application launch. SKPaymentQueue.defaultQueue().addTransactionObserver(paymentTransactionObserver) return true } . . . func applicationWillTerminate(application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. SKPaymentQueue.defaultQueue().removeTransactionObserver(paymentTransactionObserver) } }
And that's all I know about Apple's In-App Purchase for now :?
Wileyfox Swift 2 and Swift 2 Plus go official with CyanogenMod 13.1
Wileyfox Swift 2 and Swift 2 Plus go official with CyanogenMod 13.1
Some of you may have heard of the British smartphone OEM Wileyfox (brand established in 2015), a company whose current CEO Nick Muir, worked as general manager at Motorola UK, and CMO Victoria Denman worked in the marketing management of Motorola and Microsoft. Wileyfox sells its smartphones online and they usually come out running CyanogenMod, a custom-made ROM based on Google’s mobile operating…
Become an Advanced iOS 9 App Developer For Only $19
Become an Advanced iOS 9 App Developer For Only $19
Those who want to move into the app development field will find ample opportunities to earn a high salary and express your creativity. But if you want to get into this exciting sector, you have to receive the right education first, and it isn’t always possible to take good courses at local schools, especially if you’re pressed for time or on a tight budget.
Become an Advanced iOS 9 App Developer For Only $19
Become an Advanced iOS 9 App Developer For Only $19
Those who want to move into the app development field will find ample opportunities to earn a high salary and express your creativity. But if you want to get into this exciting sector, you have to receive the right education first, and it isn’t always possible to take good courses at local schools, especially if you’re pressed for time or on a tight budget.
Become an Advanced iOS 9 App Developer For Only $19
Become an Advanced iOS 9 App Developer For Only $19
Those who want to move into the app development field will find ample opportunities to earn a high salary and express your creativity. But if you want to get into this exciting sector, you have to receive the right education first, and it isn’t always possible to take good courses at local schools, especially if you’re pressed for time or on a tight budget.