Search code examples
iosobjective-cswiftstorekit

Show an alert when deferring a Promoted IAP request from App Store


We're implementing Apple's new "Promoted In-App Purchases" system, allowing users to click a "Buy" button inside Apple's App Store that triggers a purchase of an IAP.

The system calls for the app to implement the SKPaymentTransactionObserver's paymentQueue:shouldAddStorePayment:forProduct: delegate method, which returns a boolean. The documentation says,

Return true to continue the transaction in your app.

Return false to defer or cancel the transaction.

If we simply return false, the user sees our app come to the foreground and then nothing else happens. By default, the OS doesn't pop up a message saying "Purchase canceled" or anything like that; it leaves that decision up to the app developer, I suppose.

The App Store Promoted IAP purchase request can arrive at any time, including when the user is in the middle of a flow that shouldn't be interrupted. It's a perfect case to return false from this method. When I do that, I'd like to show the user an alert message explaining the problem with UIAlertController.

The problem is, I have no context view controller to use inside my paymentQueue:shouldAddStorePayment:forProduct:. Normally, when I want to show an alert from inside a view controller, I'd call [self presentViewController:alert animated:YES completion:nil];, but self in this delegate method is a SKPaymentTransactionObserver, not a view controller, so that won't work.

I'm not even sure that there is a guaranteed active view controller when this delegate method fires. For all I know, the delegate method could fire prior to the applicationDidBecomeActive event.

What's the best way to show an alert (or any other snippet of UI) when returning false from paymentQueue:shouldAddStorePayment:forProduct:?


Solution

  • You can get the application window's root view controller and use that to present the alert.

    Objective-C

    id *rootController = [[[[UIApplication sharedApplication]delegate] window] rootViewController];
    [rootViewController presentViewController:alertController animated:YES completion:nil];
    

    Swift

    let appDelegate  = UIApplication.sharedApplication().delegate
    let viewController = appDelegate.window!.rootViewController
    viewController.presentViewController(alertController, animated: true, completion: nil)