Purchasing

The purchase process is generally a little bit more involving than most people would expect. Why is it not just: purchase → on success unlock the feature?

Several reasons:

  • In-app purchases can be initiated outside the app
  • In-app purchases can be deferred, pending parental approval
  • Apple wants to be sure you delivered the product before charging the user

That is why the process looks like so:

  • being ready to handle purchase events from app startup
  • finalizing transactions when product delivery is complete
  • sending purchase request, for which successful doesn’t always mean complete

Initiating a purchase

To initiate a purchase, use the InAppPurchase.purchase() function with the productIdentifier.

Important: Do not process the purchase here, we’ll handle that later!

Note: This function is asynchronous and takes a callback function, called when the purchase has been processed. From this callback, you can for example unlock the UI by hiding your loading indicator and display a message to the user.

Example

self.loaderView.show()
InAppPurchase.purchase(
  productIdentifier: "my_product_id",
  callback: { _ in
    self.loaderView.hide()
})

This simple example locks the UI with a loader when the purchase is in progress. We’ll see later how the purchase has to be processed by your application.

The callback also gives more information about the outcome of the purchase, you might want to use it to update your UI as well. Note that some events are useful for analytics. So here’s a more complete example.

self.loaderView.show()
InAppPurchase.purchase(
  productIdentifier: "my_product_id",
  callback: { result in
    self.loaderView.hide()

    switch result.state {
    case .purchased:
      // Product successfully purchased
      // Reminder: Do not process the purchase here, only update your UI.
      //           that's why we do not send data to analytics.
      openThankYouScreen()

    case .failed:
      // Purchase failed
      // - Human formated reason can be found in result.localizedDescription
      // - More details in either result.skError or result.iapError
      showError(result.localizedDescription!)

    case .deferred:
      // The purchase is deferred, waiting for the parent's approval
      openWaitingParentApprovalScreen()

    case .cancelled:
      // The user canceled the request, generally only useful for analytics.
      break
  }
})

If the purchase fails, result will contain either .skError, a SKError from StoreKit, or .iapError, an IAPError.

Tip: After a successful purchase, you should see a new transaction in Fovea’s dashboard.