- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我关注了http://www.raywenderlich.com/21081/introduction-to-in-app-purchases-in-ios-6-tutorial设置 Apple 托管的应用内购买。它列出了产品。当我想从 Apple 下载产品时,我会这样做
-(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 中开始下载,然后 updatedDownloads 由 Apple 使用 downloadState == Active 调用。然后接下来,Apple 调用 removedTransaction 而没有真正开始下载。下载进度始终为 0%,并且永远不会使用 downloadState == Finished 调用 updatedDownloads。
我不知道为什么我的下载从未开始,以及为什么我的交易在下载完成之前就被删除了。有人有工作样本吗?
最佳答案
问题是我忘了明确关闭交易。作为引用,我的完整代码如下。它还有其他功能,例如在下载时显示进度条,但它 100% 有效。不用担心 Utility.h,它只是定义了一些宏,例如 SAFE_RELEASE_VIEW。
本质上,我通过定义购买和下载两种方法来扩展 raywenderlich 中的示例。
密切关注更新的下载。下载完成后,我将内容复制到用户的文档目录中。当你从 Apple 下载时,你的目录是这样的:
Apple 只为您提供下载文件夹的路径。您使用路径读取 ContentInfo.plist。在我的应用程序中,我在 ContentInfo.plist 中有一个属性“Files”,它在 Contents 文件夹中列出了我的文件。然后我将文件复制到 Documents 文件夹。如果您不这样做,您必须猜测您的 Contents 文件夹中有哪些文件,或者您只需复制其中的所有内容。
这是 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
关于iphone - 如何下载应用内托管内容?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12994550/
我会尝试尽可能详细地描述我的问题,但如果需要更详细的解释,请告诉我。 为了简化,假设我有 3 个 DLL(实际上我有更多,但我猜这不是很重要): managed-1.dll - 托管 DLL(用 C#
我有一个 Tomcat 6 JSF Web 应用程序,我想使用 CDI bean 来设置它。不过,我必须逐渐将项目转换为 CDI。 我的问题是:CDI bean 和传统的 JSF 托管 bean 可以
我计划将 Web 应用程序从使用 JSF 托管 Bean 转换为使用 CDI 托管 Bean。我知道我需要执行以下操作: 在 WEB-INF 中添加一个空 beans.xml 文件。 将所有 JSF
我在裸域上运行我的解析应用程序。 Parse 并没有让我的生活变得轻松。 起初我很难设置它,因为大多数 DNS 托管服务不允许根域上的 CNAME,而 Parse 需要 CNAME。 决定尝试使用 C
如果您使用 Firebase 托管 将请求定向到 云函数 通过重写,“通过”托管的请求流量是否会记入 Firebase 托管 国标转出 ? (忽略 Cloud Function 的计费) 换句话说,
关闭。这个问题是off-topic .它目前不接受答案。 想改善这个问题吗? Update the question所以它是 on-topic对于堆栈溢出。 9年前关闭。 Improve this q
我有一个特定的用例,我正尝试使用 Firebase 托管来解决,它如下: 我正在使用 Gridsome 生成静态网站 在构建之前,Gridsome 使用 Graphql 并从 Headless CMS
您好,我有一个blazor页面,我想在其中显示一个变量。 这个变量从另一个线程(通过Websocket接收数据的另一个线程)获取更新,我想以线程安全的方式显示它: Blazor页面 @page "/n
我想使用 Firebase 托管来托管一个 angular 应用程序,我需要创建一个重定向到另一个 URL 中的一些旧文件。 根据 Firebase 文档,您可以进行基本重定向 "redirects"
我正在尝试找出满足电子学习平台以下要求的最佳方法。 我在其中编辑视频的后端,以便可以将它们彼此链接(例如youtube-comment函数) 用户只能在访问平台时观看视频(不允许下载!) 视频只能通过
已关闭。这个问题是 not about programming or software development 。目前不接受答案。 这个问题似乎不是关于 a specific programming
我想这是一个相当深入的主题,因此任何带有洞察信息的网址也很乐意接受。我一直在使用原生 DirectX,但从未管理过。另一方面,大多数情况下,在开发不需要高级 GPU 渲染的其他类型的应用程序时,我通常
我刚刚将一个网站部署到 Firebase 托管,效果非常好 - 设置非常简单。 但是,我的问题是,有什么方法可以使访问该网站受到身份验证的限制吗?这是一个管理面板,只有我的团队才能访问。有什么方法可以
已关闭。这个问题是 not about programming or software development 。目前不接受答案。 这个问题似乎不是关于 a specific programming
就目前情况而言,这个问题不太适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、民意调查或扩展讨论。如果您觉得这个问题可以改进并可能重新开放,visit
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找书籍、工具、软件库、教程或其他场外资源的问题对于 Stack Overflow 来说是
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 6 年前。
我刚刚将一个网站部署到 Firebase 托管,它运行良好 - 设置非常简单。 但是,我的问题是,有什么方法可以通过身份验证限制对网站的访问?这是一个只有我的团队才能访问的管理面板。有什么方法可以用密
如果我想托管一个对公众用处不大的应用程序(例如,一个将点打印到控制台的程序,它们的数量取决于现在的时间),我喜欢在启动板中的方式不必乱扔全局托管站点的命名空间并用诸如 HourDot 之类的名称填充它
我是一名优秀的程序员,十分优秀!