Spiri
Spiri

Reputation: 1303

iOS In App Purchases: only one payment added to the SKPaymentQueue but a SKPaymentTransactionObserver method is getting called multiple times

I'm testing the IAP using a test user (in the sandbox), I have a class that implements the SKPaymentTransactionObserver protocol and when the user selects a certain table view cell, I initiate the payment:

SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];

This is done only once and I've checked: the code gets called once. The problem is that '- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions' is sometimes called multiple times and I can't figure out why. This doesn't always happen but it should never happen.

Has anyone encountered this behavior while sandbox testing (it would be a big problem if this would happen in a real scenario)?

Upvotes: 4

Views: 2459

Answers (3)

Gregory Furmanek
Gregory Furmanek

Reputation: 364

The reason you may be getting multiple callbacks is that you have inadvertently registered multiple observers. If you are instantiating view controllers that are registered as observers in the payment queue you need to ensure they are removed when the transaction is completed.

A better way of handling such situation may be to have a singleton observer that sends notifications back to your controller.

@implementation MyViewController : SomeParentController<SKPaymentTransactionObserver>

- (void)startPayment:(SKPayment *)payment {
  SKPaymentQueue *queue = [SKPaymentQueue defaultQueue];

  /* Unless MyViewController is a singleton this line of code will add a new observer
   * to the default queue. The observer is retained and every time a transaction is added
   * to the queue the callback code will be executed on all registered observers.
   */
  [queue addTransactionObserver:self];
  [SKPaymentQueue addPayment:payment];
}

- (void)paymentQueue:(SKPaymentQueue *)queue 
   updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
   // Your transaction processing code here
}

@end

Upvotes: 0

Spiri
Spiri

Reputation: 1303

I usually 'assign' my delegates but this time the delegate to the class that handles all the transaction processing was retained. Both the delegate (a view controller) and the in app purchases class were leaked. Because of that, every time I presented the view controller, another instance was created and another delegate set. When the transaction was being handled, there were as many instances of the in app purchases class roaming free :) as the number of times the view was presented. So, it wasn't a IAP problem - it was a problem of attention and memory management.

Upvotes: 5

Tommy
Tommy

Reputation: 100632

It's normal behaviour and is the reason that you use transactionState to get the state of any ongoing transactions. One of the possible transaction states is SKPaymentTransactionStatePurchasing — i.e. the system is telling you that there has been some advance in the transaction but that it isn't finished yet.

I think the point is that the delegate may not always be the same actor that started the transaction. So the delegate can post a busy indicator upon SKPaymentTransactionStatePurchasing without you having to come up with some way to wire whoever posts the request to whoever manages the display.

Upvotes: 0

Related Questions