Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating content when using Apple-hosted content for In-App Purchase

I am implementing In-App Purchase using Apple-hosted content. Previously I was using Urban Airship to host my content, and using their SDK to provide the in-app store UI and purchase functionality.

Since Urban Airship are discontinuing their IAP support this July, I need to replace it.

The Urban Airship SDK had support for updating purchased content. I see that StoreKit provides this functionality (at least to an extent) but am unsure on how to properly implement the content update itself.

SKProduct has a downloadContentVersion which specifies the version of the content that is available to download from Apple. I keep track of what version is currently downloaded within the app - so I know when an update is available.

The part I am stuck with is how to actually download the updated content?

My first thought was to do a Restore for that particular purchase, but there seems to be no way to restore individual products (only all products). Second thought was to just re-purchase the product, but this presents an alert to the user indicating that they will be charged again -- they will not be charged, however, since another alert is displayed after re-purchasing informing the user that the content is already purchased and will be re-downloaded for free -- but to me this feels like a bad user experience, they may not necessarily know that they won't be charged again until after agreeing to the purchase.

The documentation says not to create SKDownload instances myself, so I can't just create one and add it to the download queue.

How am I supposed to implement content updates for In-App Purchase when using Apple-hosted content?

like image 950
Jasarien Avatar asked Jun 09 '13 12:06

Jasarien


People also ask

How do I integrate Apple in app purchases?

First, you need to create an App ID. This will link together your app to your in-app purchaseable products. Login to the Apple Developer Center, then select Certificates, IDs & Profiles. Next, select Identifiers > App IDs, and click + in the upper right corner to create a new App ID.


2 Answers

Thanks to Jasarien for contacting Apple on this one. Here is some example code for anyone who finds this topic trying to work out how to update in-app content.

First, when you get your response from SKProductsRequest check the downloadContentVersion against the version for your previously downloaded content. You can get the content version for your existing content with code like this:

NSString *pathToYourContent = @""; // Put your path here
NSString *contentInfoPath = [pathToYourContent stringByAppendingPathComponent:@"ContentInfo.plist"];
NSDictionary *contentInfo = [NSDictionary dictionaryWithContentsOfFile:contentInfoPath];

NSString *contentVersion = [contentInfo objectForKey:@"ContentVersion"];
NSString *iapProductIdentifier = [contentInfo objectForKey:@"IAPProductIdentifier"];

Then if you spot that version has changed you can prompt the user to update. If they agree, then kick off a restore.

[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];

Then when the transactions start coming in you need to decide which ones to accept and which to ignore. In this example from my app, which downloads song content, I have a YHSongVersion class which stores the ID and version for all the songs we have downloaded, and these are stored in the array self.ownedSongVersions. This example will accept any restores where either the version numbers are different, or we haven't got the content.

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStateFailed:
                break;

            case SKPaymentTransactionStatePurchased:
                [self provideContentForTransaction:transaction];
                break;

            case SKPaymentTransactionStatePurchasing:
                break;

            case SKPaymentTransactionStateRestored:
                [self restorePurchaseIfRequired:transaction];
                break;
        }
    }

}

/// On a restore download the content if either we don't have the content or the version number has changed.
- (void)restorePurchaseIfRequired:(SKPaymentTransaction *)transaction {
    BOOL haveSong = NO;
    SKDownload *download = [transaction.downloads objectAtIndex:0];
    for (YHSongVersion *ownedSongVersion in self.ownedSongVersions) {
        BOOL isSongForThisDownload = [ownedSongVersion.iapProductIdentifier isEqualToString:download.contentIdentifier];
        if (isSongForThisDownload) {
            haveSong = YES;
            BOOL hasDifferentVersionNumber = ![ownedSongVersion.contentVersion isEqualToString:download.contentVersion];
            if (hasDifferentVersionNumber) {
                [self provideContentForTransaction:transaction];
            }
            else {
                // Do nothing
                [self completeTransaction:transaction];
                NSLog(@"Ignoring restore for %@", ownedSongVersion.iapProductIdentifier);
            }
        }
    }

    if (!haveSong) {
        [self provideContentForTransaction:transaction];
    }
}
like image 78
John Holcroft Avatar answered Oct 04 '22 05:10

John Holcroft


I spoke to some Store Kit engineers at WWDC and asked them this question.

Their response wasn't as positive as I'd hoped.

They confirmed the absence of an API for restoring a single product as a bug, and encouraged me to file two bugs (one for a enhancement request for that API in store kit and another for the documentation being too sparse around this area of upgrading products).

As for the immediate problem, their suggestion was to either implement an "update all" feature, in which all products are restored in order to get their transactions and their download objects (the option I ultimately chose), or similarly, restore all products, but ignore all products except the one you're trying to update (pointless, ultimately, since you're restoring everything multiple times if you follow this approach for each product).

I will update this answer with the Radar numbers for the bugs I file once I do so, so that others may duplicate them if necessary (remember, dupes bump bugs!)

Edit

I have reported the bug and its number is rdar://14174034 - feel free to duplicate.

like image 43
Jasarien Avatar answered Oct 04 '22 06:10

Jasarien