My app supports in-app purchases and auto-renewing subscriptions, so it is important that the SKPaymentTransactionObserver
is properly notified whenever the renewal notification is published by the system.
I prefer to keep my StoreKit code separate from the AppDelegate, so I created a singleton for it, with a reference in the AppDelegate.
class AppDelegate: UIResponder, UIApplicationDelegate {
let storeKitManager = DFStoreKitManager.shared
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
storeKitManager.register()
//...
return true
}
func applicationWillTerminate(_ application: UIApplication) {
storeKitManager.deregister()
}
}
I add the shared instance as an observer for the SKPaymentQueue
.
class DFStoreKitManager: NSObject {
static let shared = DFStoreKitManager()
private override init() { //This prevents other classes from using the default '()' initializer for this class.
super.init()
}
func register() {
SKPaymentQueue.default().add(self)
}
func deregister() {
SKPaymentQueue.default().remove(self)
}
//...
}
extension DFStoreKitManager: SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for tx in transactions {
switch tx.transactionState {
case .purchasing: print("SKPaymentTransaction purchasing")
case .deferred: print("SKPaymentTransaction deferred")
case .failed: print("SKPaymentTransaction failed")
//...
queue.finishTransaction(tx)
case .restored: print("SKPaymentTransaction restored")
//...
queue.finishTransaction(tx)
case .purchased: print("SKPaymentTransaction purchased")
//...
queue.finishTransaction(tx)
}
}
}
func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
print("Removed \(transactions.count) StoreKit transactions")
}
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
print("SKPaymentTransaction failed to restore through SKPaymentQueue: \(error.localizedDescription)")
}
}
My logs and analytics show that renewals are consistently being handled through the observer queue, but it seems like the number is significantly lower than the actual number of renewals shown in iTunes Connect.
Starting with iOS 9, NSNotificationCenter will automatically stop sending notifications to observers that have been deallocated (source).
So I'm wondering, is it possible that my singleton is being deallocated by the system, so that the SKPaymentTransactionObersver
is also removed?
Or is this singleton (and it's observer status) guaranteed to be retained until the application terminates?