如何下载应用内托pipe内容?

我遵循http://www.raywenderlich.com/21081/introduction-to-in-app-purchases-in-ios-6-tutorial来设置苹果托pipe的应用内购买。 它列出了产品。 当我想从苹果下载产品时,我会这样做

-(void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction * transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: { [[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads]; .... } -(void) paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads { NSLog(@"paymentQues"); for (SKDownload *download in downloads) { switch (download.downloadState) { case SKDownloadStateActive: { NSLog(@"%f", download.progress); break; } ... } -(void) paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions { } 

我在updatedTransactions中开始下载,然后更新下载由apple用downloadState == Active调用。 接下来,苹果公司调用removedTransaction,而没有真正开始下载。 下载进度总是0%,updatedDownloads永远不会用downloadState ==完成调用。

我不知道为什么我的下载从未开始,为什么我的交易在下载完成之前被删除。 任何人都有一个工作样本?

问题是我忘了明确closures交易。 供参考我的完整代码如下。 它还有其他的东西,比如在下载时显示进度条,但是它是100%的工作。 不用担心Utility.h,它只是定义了一些macros,比如SAFE_RELEASE_VIEW。

本质上,我通过定义两种购买和下载方法将样本扩展到了raywenderlich。

密切关注updatedDownloads。 下载完成后,我将内容复制到用户的文档目录。 当你从苹果下载,你的目录是这样的:

    • ContentInfo.plist
      • 内容
        • 你的文件

苹果公司只给你下载文件夹的path。 您使用该path来读取ContentInfo.plist。 在我的应用程序中,我在ContentInfo.plist中有一个属性“Files”,它在Contents文件夹中列出了我的文件。 然后我将这些文件复制到Documents文件夹。 如果你不这样做,你必须猜测你的内容文件夹中有哪些文件,或者只是简单地拷贝一切。

这是SmallChess(http://www.smallchess.com)的实际应用内购买代码。

 #import <StoreKit/StoreKit.h> #import "MBProgressHUD/MBProgressHUD.h" #import "Others/Utility.h" #import "Store/OnlineStore.h" NSString *const ProductPurchasedNotification = @"ProductPurchasedNotification"; @implementation StoreTransaction @synthesize productID, payment; @end @interface OnlineStore () <SKProductsRequestDelegate, SKPaymentTransactionObserver, MBProgressHUDDelegate> @end @implementation OnlineStore { NSSet *_productIDs; MBProgressHUD *_progress; NSMutableSet * _purchasedIDs; SKProductsRequest * _productsRequest; RequestProductsCompletionHandler _completionHandler; } -(id) init { if ([SKPaymentQueue canMakePayments] && (self = [super init])) { [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; } return self; } #pragma mark MBProgressHUDDelegate -(void) hudWasHidden:(MBProgressHUD *)hud { NSAssert(_progress, @"ddd"); [_progress removeFromSuperview]; SAFE_RELEASE_VIEW(_progress); } #pragma end #pragma mark SKProductsRequestDelegate -(void) request:(NSSet *)productIDs handler:(RequestProductsCompletionHandler)handler { _completionHandler = [handler copy]; _productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIDs]; _productsRequest.delegate = self; [_productsRequest start]; } -(void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { _productsRequest = nil; _completionHandler(YES, response.products); _completionHandler = nil; } -(void) request:(SKRequest *)request didFailWithError:(NSError *)error { NSLog(@"Failed to load list of products."); _productsRequest = nil; _completionHandler(NO, nil); _completionHandler = nil; } #pragma end #pragma mark Transaction -(void) provideContentForProduct:(SKPaymentTransaction *)payment productID:(NSString *)productID { [_purchasedIDs addObject:productID]; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:productID]; [[NSUserDefaults standardUserDefaults] synchronize]; StoreTransaction *transaction = [[StoreTransaction alloc] init]; [transaction setPayment:payment]; [transaction setProductID:productID]; [[NSNotificationCenter defaultCenter] postNotificationName:ProductPurchasedNotification object:transaction userInfo:nil]; } -(void) completeTransaction:(SKPaymentTransaction *)transaction { #ifdef DEBUG NSLog(@"completeTransaction"); #endif [self provideContentForProduct:transaction productID:transaction.payment.productIdentifier]; [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } -(void) restoreTransaction:(SKPaymentTransaction *)transaction { #ifdef DEBUG NSLog(@"restoreTransaction"); #endif [self provideContentForProduct:transaction productID:transaction.originalTransaction.payment.productIdentifier]; [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } -(void) failedTransaction:(SKPaymentTransaction *)transaction { #ifdef DEBUG NSLog(@"failedTransaction"); #endif if (transaction.error.code != SKErrorPaymentCancelled) { NSLog(@"Transaction error: %@", transaction.error.localizedDescription); } [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; } -(void) restoreCompletedTransactions { #ifdef DEBUG NSLog(@"restoreCompletedTransactions"); #endif [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; } #pragma end #pragma mark Buy & Download -(BOOL) purchased:(NSString *)productID { return [_purchasedIDs containsObject:productID]; } -(void) buy:(SKProduct *)product { SKPayment * payment = [SKPayment paymentWithProduct:product]; [[SKPaymentQueue defaultQueue] addPayment:payment]; } -(void) download:(StoreTransaction *)transaction { NSAssert(transaction.payment.transactionState == SKPaymentTransactionStatePurchased || transaction.payment.transactionState == SKPaymentTransactionStateRestored, @"The payment transaction must be completed"); if ([transaction.payment.downloads count]) { [[SKPaymentQueue defaultQueue] startDownloads:transaction.payment.downloads]; } } #pragma end #pragma mark SKPaymentTransactionObserver -(void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue { NSLog(@"RestoreCompletedTransactions"); } -(void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction * transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: { #ifdef DEBUG NSLog(@"SKPaymentTransactionStatePurchased"); #endif [[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads]; break; } case SKPaymentTransactionStateFailed: { NSLog(@"Failed"); [self failedTransaction:transaction]; break; } case SKPaymentTransactionStateRestored: { NSLog(@"Restored"); [self restoreTransaction:transaction]; break; } case SKPaymentTransactionStatePurchasing: { #ifdef DEBUG NSLog(@"SKPaymentTransactionStatePurchasing"); #endif break; } } } } -(void) paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error { #ifdef DEBUG NSLog(@"restoreCompletedTransactionsFailedWithError"); #endif } -(void) paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions { #ifdef DEBUG NSLog(@"removedTransactions"); #endif } -(void) paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads { for (SKDownload *download in downloads) { switch (download.downloadState) { case SKDownloadStateActive: { #ifdef DEBUG NSLog(@"%f", download.progress); NSLog(@"%f remaining", download.timeRemaining); #endif if (download.progress == 0.0 && !_progress) { #define WAIT_TOO_LONG_SECONDS 60 #define TOO_LARGE_DOWNLOAD_BYTES 4194304 const BOOL instantDownload = (download.timeRemaining != SKDownloadTimeRemainingUnknown && download.timeRemaining < WAIT_TOO_LONG_SECONDS) || (download.contentLength < TOO_LARGE_DOWNLOAD_BYTES); if (instantDownload) { UIView *window= [[UIApplication sharedApplication] keyWindow]; _progress = [[MBProgressHUD alloc] initWithView:[[UIApplication sharedApplication] keyWindow]]; [window addSubview:_progress]; [_progress show:YES]; [_progress setDelegate:self]; [_progress setDimBackground:YES]; [_progress setLabelText:@"Downloading"]; [_progress setMode:MBProgressHUDModeAnnularDeterminate]; } else { NSLog(@"Implement me!"); } } [_progress setProgress:download.progress]; break; } case SKDownloadStateCancelled: { break; } case SKDownloadStateFailed: { [Utility showAlert:@"Download Failed" message:@"Failed to download. Please retry later" cancelTitle:@"OK" otherTitle:nil delegate:nil]; break; } case SKDownloadStateFinished: { NSString *source = [download.contentURL relativePath]; NSDictionary *dict = [[NSMutableDictionary alloc] initWithContentsOfFile:[source stringByAppendingPathComponent:@"ContentInfo.plist"]]; if (![dict objectForKey:@"Files"]) { [[SKPaymentQueue defaultQueue] finishTransaction:download.transaction]; return; } NSAssert([dict objectForKey:@"Files"], @"The Files property must be valid"); for (NSString *file in [dict objectForKey:@"Files"]) { NSString *content = [[source stringByAppendingPathComponent:@"Contents"] stringByAppendingPathComponent:file]; NSAssert([Utility isFileExist:content], @"Content path must be valid"); // Copy the content to the Documents folder, don't bother with creating a directory for it DEFINE_BOOL(succeed, [Utility copy:content dst:[[Utility getDocPath] stringByAppendingPathComponent:file]]); NSAssert(succeed, @"Failed to copy the content"); #ifdef DEBUG NSLog(@"Copied %@ to %@", content, [[Utility getDocPath] stringByAppendingPathComponent:file]); #endif } if (download.transaction.transactionState == SKPaymentTransactionStatePurchased && _progress) { [Utility showAlert:@"Purchased Complete" message:@"Your purchase has been completed. Please refer to the FAQ if you have any questions" cancelTitle:@"OK" otherTitle:nil delegate:nil]; } [_progress setDimBackground:NO]; [_progress hide:YES]; [[SKPaymentQueue defaultQueue] finishTransaction:download.transaction]; break; } case SKDownloadStatePaused: { #ifdef DEBUG NSLog(@"SKDownloadStatePaused"); #endif break; } case SKDownloadStateWaiting: { #ifdef DEBUG NSLog(@"SKDownloadStateWaiting"); #endif break; } } } } #pragma end @end 

接受这不是你的特定问题的答案

我在这方面遇到了一些其他的问题

  1. 这种方法在恢复/购买周期中被多次调用,而不仅仅是一次。
  2. 也可以在不需要调用的情况下重新启动应用程序来调用它(继续中断恢复/下载)。
  3. 如果您只想恢复一个产品标识符,则必须过滤掉所有其他产品标识符。
  4. 如果下载状态不是等待/暂停,则不应调用startDownloads
  5. 有时候,如果你打电话给startDownloads,你可能会得到另一个对SAME事务的updatedTransaction的调用,而download.downloadState仍然在等待 – 如果你第二次调用startDownloads,你可以得到两次下载进度/完成通知。 如果您的downloadSuccess处理程序在复制文件之前清除目标位置,这可能会导致间歇性问题。 因为第二次下载实际上并没有下载到caching,所以你没有什么可以复制第二次通知。 我已经解决了这个问题,通过录制本地下载数组,我知道我已经调用了startDownloads,并完成了。

我把广泛的debugging语句放到我的商店处理程序和每个case语句来certificate这种行为,并确保我不是多次调用队列。 我build议其他人开始做同样的 – 放入诊断。

SKDownload,SKPaymentTransaction没有足够的描述方法,你不得不推出你自己的事实。

或者在github上使用别人的商店处理程序。