Advertisement
Scroll to top
Final product imageFinal product imageFinal product image
What You'll Be Creating

Introduction

In-app purchase is a great feature for all those developers who want to get more revenue and offer extra content and features through their applications. For example, for games you can buy gems or coins, and for photography apps you may unlock new effects or tools. And you can do all this using a credit card or other payment method, without exiting the app. 

In this tutorial I'll cover all the necessary steps to create a Consumable and Non-Consumable IAP product on iTunes Connect, and I'll show you the code you'll need to purchase both items. I've made a sample Xcode project with a label and two buttons, so download it and follow along with this tutorial to understand how it works.

Create a Sandbox Tester in iTunes Connect

I assume you've already created an iOS app in the My Apps section on iTunes Connect. The first thing you should do is create a Sandbox Tester to test IAP on your real device (no Simulator—it doesn't support In-App Purchases). 

Enter Users and Roles, go to the Sandbox Tester tab, and click the (+) sign next to Tester.

Users and roles in iTunes ConnectUsers and roles in iTunes ConnectUsers and roles in iTunes Connect

Fill out the form to add a new sandbox tester. Once you've saved your info, go back to the My App section and click on the icon of your app to enter its details and create IAP products.

Create IAP Products in iTunes Connect

Consumable Products

Click the Features tab and then the (+) sign next to In-App Purchases. You can create one product at a time, so let's start with a Consumable one.

Select Consumable In-App PurchaseSelect Consumable In-App PurchaseSelect Consumable In-App Purchase

A Consumable IAP, as its name suggests, is a product that you can buy multiple times. We'll use it to collect additional "coins" in our demo app. 

Click Create to initialize your IAP item. On the next screen, you can set up all the info about your product:

  • Reference Name: this name will be used on iTunes Connect and in Sales and Trends reports. It won't be displayed on the App Store and you can type any name you want, but it can't be longer than 64 characters.
  • Product ID: A unique alphanumeric identifier that will be fetched by the app in order to recognize your product. Usually developers use a web-reverse syntax for product ids. In this example we chose com.iaptutorial.coins. Later on we'll paste this ID as a string into our code.
  • Price: Choose a price tier from the dropdown menu. Remember that in order to sell your in-app purchase product on the App Store, you must have applied for a Paid Application Agreement in the Agreements, Tax & Banking section.
  • Localizations: For the sake of this tutorial we've chosen only English, but you can add more languages by clicking on the (+) button. Then type a Display Name and a Description. Both of them will be visible on the App Store. 
  • Screenshot: Upload a screenshot for review. It will not be displayed in the App Store and it must have a valid size for your app platform, so if your app is Universal, you may upload an iPad screenshot.
  • Review Notes: Any additional information about your IAP which may be helpful for the reviewer.
Create a new IAP productCreate a new IAP productCreate a new IAP product

Once you're done, click Save and you'll get this alert:

Your first In-App Purchase must be submitted with a new app version. Select it from the app’s In-App Purchases section and click Submit.

Non-Consumable Products

Now click the In-App Purchases button on the list on the left, right above the Game Center button, and add a new IAP product. This time, select the Non-Consumable option:

Create a Non-Consumable productCreate a Non-Consumable productCreate a Non-Consumable product

Click Create and repeat the steps we mentioned above. Since this will be a Non-Consumable product, users will be able to purchase it only once, and Apple requires the ability to restore such purchases. That happens in case you uninstall the app and reinstall it again, or download it from another device with your same Apple ID and need to get your purchases back without paying for them twice. So later we'll add a Restore Purchase function in our code.

The Product ID we created now is com.iaptutorial.premium, with a price tier of USD $2.99. We've called it Unlock Premium Version.

Once you're done filling all the fields, save your product and go back to the In-App Purchases page. Now you should have a list of your two products, with their Name, Type, ID and Status set as Ready to Submit.

In-app Purchases listIn-app Purchases listIn-app Purchases list

Go back to your app's page by clicking on the App Store and Prepare for Submission buttons. Scroll down to the In-App Purchases section, right below General App Information, and click to the (+) button to add your IAP products. 

Add In-App Purchases to your apps info pageAdd In-App Purchases to your apps info pageAdd In-App Purchases to your apps info page

Select all of them and click Done.

Select your IAP productsSelect your IAP productsSelect your IAP products

Finally, click Save in the top-right corner of the screen and you'll be done with configuring In-App Purchase products on iTunes Connect.

Log in to Sandbox Tester on an iOS device

Before getting to the code, there's one more thing left to do. Go to Settings > iTunes & App Store on your iOS device. If you're already logged in with your original Apple ID, tap on it and choose Sign Out. Then simply sign in with the credentials for the sandbox tester you created. After signing in, you may get an alert like this:

Alert from the deviceAlert from the deviceAlert from the device

Just ignore its message and tap Cancel. Your device will ask you your sandbox login credentials again while trying to make a purchase and will recognize your test account so you won't be charged a penny on your credit card for any purchase you make.

Exit Settings, plug your device into your Mac via the USB cable, and let's finally start coding!

The Code

If you've downloaded our demo project, you'll see that all the necessary code for In-App Purchase has been written, so if you run it you'll get something like this:

Demo IAP appDemo IAP appDemo IAP app

If you want to test the app, you should change the Bundle Identifier to your own id. Otherwise, Xcode will not allow you to run the app on a real device and the app will not recognize the two IAP products you've created.

Chage Bundle ID in the General tab in XCodeChage Bundle ID in the General tab in XCodeChage Bundle ID in the General tab in XCode

Enter ViewController.swift and check the code. First of all we've added an import statement for StoreKit and the delegates we need in order to track payment transactions and product requests.

1
import StoreKit
2
3
class ViewController: UIViewController,
4
SKProductsRequestDelegate,
5
SKPaymentTransactionObserver
6
{

Then we've declared a few views which will be useful.

1
 /* Views */
2
    @IBOutlet weak var coinsLabel: UILabel!
3
    @IBOutlet weak var premiumLabel: UILabel!
4
    @IBOutlet weak var consumableLabel: UILabel!
5
    @IBOutlet weak var nonConsumableLabel: UILabel!
6
    

coinsLabel and premiumLabel will be used to show the results of purchases for both products. consumableLabel and nonConsumableLabel will show the description and price of each IAP product, the ones we've previously created in iTunes Connect. 

Now it's time to add some variables:

1
/* Variables */
2
    let COINS_PRODUCT_ID = "com.iaptutorial.coins"
3
    let PREMIUM_PRODUCT_ID = "com.iaptutorial.premium"
4
    
5
    var productID = ""
6
    var productsRequest = SKProductsRequest()
7
    var iapProducts = [SKProduct]()
8
    var nonConsumablePurchaseMade = UserDefaults.standard.bool(forKey: "nonConsumablePurchaseMade")
9
    var coins = UserDefaults.standard.integer(forKey: "coins")
10
    

The first two lines are to recall our product IDs. It's important that those strings exactly match the ones registered in iTunes Connect's In-App Purchase section.

  • productID is a string we'll be using later to detect what product we will choose to buy.
  • productsRequest is an instance of SKProductsRequest, needed to search for IAP products from your app on iTC.
  • iapProducts is a simple array of SKProducts. Please note that SK prefix means StoreKit, the iOS framework we'll be using to handle purchases.

The last two lines load two variables of type Boolean and Integer needed to track purchases of coins and the premium version, respectively consumable and non-consumable products.

The following code in viewDidLoad() performs a few things as soon as the app starts:

1
    // Check your In-App Purchases

2
    print("NON CONSUMABLE PURCHASE MADE: \(nonConsumablePurchaseMade)")
3
    print("COINS: \(coins)")
4
    
5
    // Set text

6
    coinsLabel.text = "COINS: \(coins)"
7
    
8
    if nonConsumablePurchaseMade { premiumLabel.text = "Premium version PURCHASED!"
9
    } else { premiumLabel.text = "Premium version LOCKED!"}
10
    
11
    // Fetch IAP Products available

12
    fetchAvailableProducts()

First we just log each purchase to the Xcode console. Then we display the total amount of coins that we bought with the coinsLabel. Since we're running the demo app for the first time, it will show COINS: 0.

The if statement sets the premiumLabel's text according to whether the non-consumable product was purchased. To start off, it will show Premium version LOCKED! since we haven't made the premium purchase yet.

The last line of code calls a method we'll see later, which simply fetches the products we've previously stored in iTC.

Now let's see what the two purchase buttons we've set in our demo app do:

1
// MARK: -  BUY 10 COINS BUTTON

2
@IBAction func buy10coinsButt(_ sender: Any) {
3
    purchaseMyProduct(product: iapProducts[0])
4
}
5
    
6
    
7
// MARK: - UNLOCK PREMIUM BUTTON

8
@IBAction func unlockPremiumButt(_ sender: Any) {
9
    purchaseMyProduct(product: iapProducts[1])
10
}

Both methods will call a function that will check if the device can make purchases, and if it can, the app will call the StoreKit delegate methods to process the purchase.

As mentioned before, we need a third button to restore our non-consumable purchase. Here is its code:

1
// MARK: - RESTORE NON-CONSUMABLE PURCHASE BUTTON

2
@IBAction func restorePurchaseButt(_ sender: Any) {
3
    SKPaymentQueue.default().add(self)
4
    SKPaymentQueue.default().restoreCompletedTransactions()
5
}
6
    
7
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
8
    nonConsumablePurchaseMade = true
9
    UserDefaults.standard.set(nonConsumablePurchaseMade, forKey: "nonConsumablePurchaseMade")
10
        
11
    UIAlertView(title: "IAP Tutorial",
12
    message: "You've successfully restored your purchase!",
13
    delegate: nil, cancelButtonTitle: "OK").show()
14
}
15
 

The IBAction function is attached to the Restore Purchase button in the Storyboard and starts connecting to Apple's In-App Purchase system to restore the purchase if that has been already made.

paymentQueueRestoreCompletedTransactionsFinished() is the delegate method from StoreKit framework that will save our nonConsumablePurchaseMade variable to true after the purchase has been successfully restored.

We're done with buttons, so let's see what the fetchAvailableProducts() function does:

1
// MARK: - FETCH AVAILABLE IAP PRODUCTS

2
func fetchAvailableProducts()  {
3
        
4
    // Put here your IAP Products ID's

5
    let productIdentifiers = NSSet(objects:
6
        COINS_PRODUCT_ID,
7
        PREMIUM_PRODUCT_ID
8
    )
9
        
10
    productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
11
    productsRequest.delegate = self
12
    productsRequest.start()
13
}
14
    

We first create an instance of NSSet, which is basically an array of strings. We'll store the two product IDs we've previously declared there.

Then we start an SKProductsRequest based on those identifiers, in order for the app to display the info about the IAP products (description and price), which will be processed by this delegate method:

1
// MARK: - REQUEST IAP PRODUCTS

2
func productsRequest (_ request:SKProductsRequest, didReceive response:SKProductsResponse) {
3
    if (response.products.count > 0) {
4
        iapProducts = response.products
5
            
6
        // 1st IAP Product (Consumable) ------------------------------------

7
        let firstProduct = response.products[0] as SKProduct
8
        
9
        // Get its price from iTunes Connect

10
        let numberFormatter = NumberFormatter()
11
        numberFormatter.formatterBehavior = .behavior10_4
12
        numberFormatter.numberStyle = .currency
13
        numberFormatter.locale = firstProduct.priceLocale
14
        let price1Str = numberFormatter.string(from: firstProduct.price)
15
        
16
        // Show its description

17
        consumableLabel.text = firstProduct.localizedDescription + "\nfor just \(price1Str!)"
18
        // ------------------------------------------------

19
        
20
        
21
        
22
        // 2nd IAP Product (Non-Consumable) ------------------------------

23
        let secondProd = response.products[1] as SKProduct
24
        
25
        // Get its price from iTunes Connect

26
        numberFormatter.locale = secondProd.priceLocale
27
        let price2Str = numberFormatter.string(from: secondProd.price)
28
        
29
        // Show its description

30
        nonConsumableLabel.text = secondProd.localizedDescription + "\nfor just \(price2Str!)"
31
        // ------------------------------------

32
    }
33
}
34
    

In the function above we first have to check if there are any products registered in iTunes Connect and set our iapProducts array accordingly. Then we can initialize the two SKProducts and print their description and price on the labels.

Before getting to the core of the In-App Purchase code, we need a couple more functions:

1
// MARK: - MAKE PURCHASE OF A PRODUCT
2
func canMakePurchases() -> Bool {  return SKPaymentQueue.canMakePayments()  }
3
func purchaseMyProduct(product: SKProduct) {
4
    if self.canMakePurchases() {
5
        let payment = SKPayment(product: product)
6
        SKPaymentQueue.default().add(self)
7
        SKPaymentQueue.default().add(payment)
8
        
9
        print("PRODUCT TO PURCHASE: \(product.productIdentifier)")
10
        productID = product.productIdentifier
11
        
12
        
13
    // IAP Purchases dsabled on the Device
14
    } else {
15
        UIAlertView(title: "IAP Tutorial",
16
        message: "Purchases are disabled in your device!",
17
        delegate: nil, cancelButtonTitle: "OK").show()
18
    }
19
}

The first one checks if our device is able to make purchases. The second function is the one that we call from the two buttons. It starts the payment queue and changes our productID variable into the selected productIdentifier

Now we've finally arrived at the last delegate method, the one that handles payment results:

1
// MARK:- IAP PAYMENT QUEUE

2
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
3
    for transaction:AnyObject in transactions {
4
            if let trans = transaction as? SKPaymentTransaction {
5
                switch trans.transactionState {
6
                    
7
                case .purchased:
8
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
9
                    
10
                    // The Consumable product (10 coins) has been purchased -> gain 10 extra coins!

11
                   if productID == COINS_PRODUCT_ID {
12
                        
13
                        // Add 10 coins and save their total amount

14
                        coins += 10
15
                        UserDefaults.standard.set(coins, forKey: "coins")
16
                        coinsLabel.text = "COINS: \(coins)"
17
                        
18
                        UIAlertView(title: "IAP Tutorial",
19
                        message: "You've successfully bought 10 extra coins!",
20
                        delegate: nil,
21
                        cancelButtonTitle: "OK").show()
22
                        
23
                        
24
                        
25
                    // The Non-Consumable product (Premium) has been purchased!

26
                    } else if productID == PREMIUM_PRODUCT_ID {
27
                        
28
                        // Save your purchase locally (needed only for Non-Consumable IAP)

29
                        nonConsumablePurchaseMade = true
30
                        UserDefaults.standard.set(nonConsumablePurchaseMade, forKey: "nonConsumablePurchaseMade")
31
                    
32
                        premiumLabel.text = "Premium version PURCHASED!"
33
                        
34
                        UIAlertView(title: "IAP Tutorial",
35
                        message: "You've successfully unlocked the Premium version!",
36
                        delegate: nil,
37
                        cancelButtonTitle: "OK").show()
38
                    }
39
                    
40
                    break
41
                    
42
                case .failed:
43
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
44
                    break
45
                case .restored:
46
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
47
                    break
48
                    
49
                default: break
50
    }}}
51
}

This function has a switch statement that checks every state of the payment. The first case gets called if the purchase has been successfully made and completes its transaction. 

Inside this block we have to check what product ID we've selected and perform the necessary actions to update our app—so if we the user bought 10 extra coins, we'll add 10 to our coins variable, save its value with UserDefaults, display the new amount of coins we gained, and fire an alert about it. 

Please note that you can make this purchase multiple times with no limits since it's a consumable IAP, and there's no need for a restore purchase function.

Similarly, if we bought the non-consumable premium product, the app sets our nonConsumablePurchaseMade variable to true, saves it, changes the text of the premiumLabel, and fires an alert to notify you that the purchase has been successful. 

The other two cases handle the payment results for failure and restoring. The app will fire custom alerts on its own if your transaction fails for some reason or if you've restored a non-consumable purchase.

That's it! Now just make sure you're logged in with your Sandbox Tester credentials and run the app to test it. The first time, you'll get an alert like this:

App Store Sign In alertApp Store Sign In alertApp Store Sign In alert

Choose Use Existing Apple ID and enter your Sandbox Tester's username and password again to sign in. This happens because the app can recognize only a real user from the iTunes & App Store settings, not a Sandbox one.

Once you've logged in, you'll be able to perform purchases of both products.

10 Coins purchase made10 Coins purchase made10 Coins purchase made

Non-Consumable Premium purchase madeNon-Consumable Premium purchase madeNon-Consumable Premium purchase made

CodeCanyon Templates

If you work with iOS and want to get deeper into Swift language and apps development, check some of my iOS app templates on CodeCanyon

There are hundreds of other iOS app templates on the Envato Market as well, ready to be reskinned and sure to speed up your workflow. Go check them out! You might just save hours of work on your next app.

Conclusion

In this tutorial, we've covered all the steps needed to create In-App Purchase products on iTunes Connect and how to write the code to enable them in your app. I hope you're able to put this knowledge to use in your next iOS app!

Thanks for reading, and I'll see you next time! Please check out our other courses and tutorials about iOS app development with Swift.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.