WatchKit - Check in-app purchase made on iOS

Hello,


This is a follow up question to another question I asked. I have added a pro version in-app purchase to my iOS that will unlock some features on the Watch app. Currently I sent an updateContext to the Apple Watch to tell it that the pro version was purchased. I also use NSUserDefaults on both devices to save the purchase (as a bool == true)


This works fine but has some limitations:

- The Watch has to be paired when the purchase is made

- Deleting the Watch app will require the user to restore the inapp purchase on his iOS app for the context to get resent

- A few more scenarios but I wont go into detail


So I'm thinking to check the in-app purchases on Watch app launch. I can still keep the updateContext functionality as it causes no real harm but I would like to check at launch for in-app purchases.


Is the correct way of doing this pretending to restore purchases on Watch app and doing what needs to be done in the paymentQueueRestoreCompletedTransactionsFinished()


So basically in my extensionDelegate I would do:


func restorePurchases() {


SKPaymentQueue.defaultQueue().addTransactionObserver(self)

SKPaymentQueue.defaultQueue().restoreCompletedTransactions()

}


func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {

print("transactions restored")

var purchasedItemsIDs = []

for transaction in queue.transactions {

let tran: SKPaymentTransaction = transaction

let prodID = tran.payment.productIdentifier as String

switch prodID {

case "xxxxx.iap.proversion":

print("restored - pro version")

proVersionPurchased()

default:

print("IAP not setup")

}

}

}

Replies

There is no need to restore the purchase each time you want to check to see if the user has purchased the IAP. In the updatedTransaction method which will be run only once in the life of the app just store some variable in a file (like NSUserDefault or the file system) to indicate that the purchase was made. Then the watch app just needs to check the value of that variable - just like any other setting in tghe app. Your idea of storing "true" in NSUserDefault is almost good enough. But it is easily hacked. It would be more secure to store the value of the identifierForVendor in NSUserDefault if the purchase is made. That way a hacker can't set a simple variable to "True" and can't copy the value in NSUserDefaults from one device to another.

Thanks for the reply.


The pro version IAP purchase happens on the iOS app, but the functionality is for the Apple WAtch. Currently i use updateContext to send the information over to the Apple WAtch (by telling the Apple Watch to set the NSUserDefault to true. This works but dosnt account for a few scenarios.


For example if the user deletes the Apple Watch app his Apple Watch NSUserDefaults get deleted with it. So when he reinstalls the app, I can imagine that he would need to restore purchase on iOS to cause the iOS app to resend the context and update the Apple Watch NSUserDefault key.

I actually never thought of jailbreaks being able to set the NSUserDefault and circumvent my check. Thanks for point that out and I'll look into another way. I have a few questions regarding identifierForVendor


1. Is the identifierForVendor device/account specific?

2. If I send that to my Apple Watch, will I be able to check against it?

The identifierForVendor for the iOS device/app is surely different from the Apple Watch, how would I check against it.

3. What if the user restores from another device with a new identifierForVendor (same itunes account), how will I handle that?

It certainly is complicated but to simplify you segment the problem. There are two different issues: 1) how do you communicate variables between the iOS app and the Watch and 2) how does the iOS app know whether or not the IAP was purchased. The first is something that you have to handle using one of a variety of techniques. I am sure you have mastered that. If not, consider it a challenge separate from the IAP question. The second - getting the IAP status - is done when the iOS app is operating. It has nothing to do with the watch. If the user deletes and reinstalls the iOS App (not the Watch App) then you can either - restoreCompletedTransactions or use the Keychain to store the IAP status (it survives delete/reinstall) or use the iCloud key/value file.

Regarding 1)

I'm already communicating upon purchase (on iOS) to the Apple Watch the pro version was purchased using updateContext and on Apple Watch I'm setting the NSUserDefault to true.


My concern is that when the user deleted the Apple Watch app, their Apple Watch userDefaults go too. therefore the user will need to restore purchases on their iOS device to trigger the updateContext again.


I get the status and go a full restore of the IAPs when the user clicks the restore button on their iOS app. But thats not the point. What if the user deletes just the Apple Watch app, he still needs to restore on iOS for the app to communicate with the Apple WAtch

I do not think you need to Jailbreak to get access to files stored on a device. It can be done from a Mac through the files stored by iTunes.


The identifierForVendor is specific to the device and the app. It changes during a delete/resinstall process.


Your watch (WatchKit Extension) has access to the iPhone's identifierForVendor for that app (I think in WatchOS2 the Watch has access to identifierForVendor - but I am not certain)


In updatedTransactions, in each device, you detect the IAP purchase. At that point you add something to the NSUserDefaults, or another file, that is device specific indicating the IAP was purchased.

Then your logic is faulty. The app should be able to set variables on the iPhone that can be detected by the watch app. The fact that the iphone app has purchased an IAP is just one, or two, of those variables. If you delete the watch app and then reinstall it but don't delete the iphone app then the IAP is, still, one of those variables. If you delete the iphone app and reinstall it then the iphone may be required to restore the iap - or you could, as stated above, rely on the keychain or the icloud key-value file to reset that variable.

I'll look into the identifierForVendor topic in more detail later.


I just found out tht my updateApplicationContext only works for iOS 9 or later


so my question, what are the options of telling the Apple Watch the pro version was purchased. Ie what options do I have to set the NSUserDefault on Apple Watch?

Yea I'm fairly sure my approach is faulty especially because I also use updateApplicationContext to send the information to Apple Watch and set the NSUserDefault on there through that.


I have actually opened a seperate discussion on this, more related to how to sync in-app purchases. https://forums.developer.apple.com/thread/23032


You can see both my approaches and why its still a problem. iCloud keychain is not available on Apple Watch