iOS 6 中对 StoreKit 的更改

iOS 6 引入对商店工具包 API 的两项更改:可以在应用中显示 iTunes(应用商店/iBookstore),以及 Apple 会托管你下载文件的新应用内购买选项。 本文档介绍如何使用 Xamarin.iOS 实现这些功能。

在 iOS 6 中对商店工具包的主要更改为以下两项新功能:

  • 应用内内容显示和购买 - 用户无需离开应用,即可购买和下载应用、音乐、图书和其他 iTunes 内容。 你也可以关联自己的应用以促进购买,或鼓励评论和评分。
  • 应用内购买托管内容 - Apple 会存储和交付与你的应用内购买产品关联的内容,让你无需单独架设服务器来托管你的文件,自动支持后台下载,让你编写更少的代码。

有关 StoreKit API 详细覆盖范围,请参阅应用内购买指南。

要求

本文档中讨论的应用商店工具包功能需要 iOS 6 和 Xcode 4.5 以及 Xamarin.iOS 6.0。

应用内内容显示和购买

iOS 中全新的应用内购买功能让用户可以从自己的应用中查看产品信息,购买或下载产品。 以前,应用必须触发 iTunes、App Store 或 iBookstore,这会导致用户离开最开始的应用。 完成操作后,此新功能会自动将用户返回到应用。

Automatically returning to an app after purchase

如何使用此方法的示例包括:

  • 鼓励用户为你的应用评分 - 可以打开 App Store 页面,这样用户不需要离开应用即可为你的应用评分和撰写评论。
  • 跨推广应用 - 允许用户查看你发布的其他应用,做到立即购买/下载。
  • 帮助用户查找和下载内容 - 帮助用户购买你应用查找、管理或聚合内容(例如,与音乐相关的应用可以提供歌单,并允许在应用内单独购买每首歌)。

显示 SKStoreProductViewController 后,用户可以像在 iTunes、App Store 或 iBookstore 中一样与产品信息进行互动。 用户可以:

  • 查看屏幕截图(适用于应用),
  • 示例歌曲或视频(适用于音乐、电视节目和电影),
  • 阅读(和编写)评论,
  • 购买和下载,两者完成发生在视图控制器和商店工具包中。

SKStoreProductViewController 中的一些选项仍然会强制用户离开应用并打开相关商店应用,例如单击“相关产品”或应用的“支持”链接。

SKStoreProductViewController

用于在任何应用内显示某款产品的 API 很简单:只需要创建和显示 SKStoreProductViewController。 按照以下步骤创建和显示产品:

  1. 创建一个 StoreProductParameters 对象,将参数传递给视图控制器(包括构造函数中的 productId)。
  2. 实例化 SKProductViewController。 将其分配给类级别字段。
  3. 将处理程序分配给视图控制器的 Finished 事件,这样应该会关闭视图控制器。 当用户按下取消时会调用该事件;否则,在视图控制器内完成事务。
  4. 调用 LoadProduct 方法传入 StoreProductParameters 和完成事件处理器。 完成事件处理器应检查产品请求是否成功,如果成功,则以模式方式呈现 SKProductViewController。 如果无法检索产品,应添加适当的错误处理。

示例

本文 StoreKit 示例中的 ProductView 项目实现接受任何产品的 Apple ID 并显示 SKStoreProductViewControllerBuy 方法。 以下代码显示任何给定 Apple ID 的产品信息:

void Buy (int productId)
{
    var spp = new StoreProductParameters(productId);
    var productViewController = new SKStoreProductViewController ();
    // must set the Finished handler before displaying the view controller
    productViewController.Finished += (sender, err) => {
        // Apple's docs says to use this method to close the view controller
        this.DismissModalViewControllerAnimated (true);
    };
    productViewController.LoadProduct (spp, (ok, err) => { // ASYNC !!!
        if (ok) {
            PresentModalViewController (productViewController, true);
        } else {
            Console.WriteLine (" failed ");
            if (err != null)
                Console.WriteLine (" with error " + err);
        }
    });
}

应用在运行时类似于以下屏幕截图 - 下载或购买完全发生在 SKStoreProductViewController 之内:

The app looks like this when running

支持较旧的操作系统

示例应用程度包括演示如何在早期版本的 iOS 中打开 App Store、iTunes 或 iBookstore 的代码。 使用 OpenUrl 方法打开一个正确制作的 itunes.com URL。

可以实施版本检查来确定要运行的代码,如下所示:

if (UIDevice.CurrentDevice.CheckSystemVersion (6,0)) {
    // do iOS6+ stuff, using SKStoreProductViewController as shown above
} else {
    // don't do stuff requiring iOS 6.0, use the old syntax
    // (which will take the user out of your app)
    var nsurl = new NSUrl("http://itunes.apple.com/us/app/angry-birds/id343200656?mt=8");
    UIApplication.SharedApplication.OpenUrl (nsurl);
}

错误

如果使用的 Apple ID 无效,则会发生以下错误,这可能令人困惑,因为它暗示存在某种网络或身份验证问题。

Error Domain=SKErrorDomain Code=5 "Cannot connect to iTunes Store"

参阅 Objective-C 文档

在 Apple 的开发人员门户参阅与商店工具包相关内容的开发人员会看到协议 - SKStoreProductViewControllerDelegate - 与此新功能相关的讨论。 委托协议只有一种方法 - productViewControllerDidFinish - 已在 Xamarin.iOS 中的 SKStoreProductViewController 上公开为 Finished 事件。

确定 Apple ID

SKStoreProductViewController 需要的 Apple ID 是一串数字(不要与“com.xamarin.mwc2012”这样的捆绑 ID 混淆了)。 可以通过多种不同方式找到想要显示的产品的 Apple ID,如下所示:

iTunesConnect

如果是你发布的应用,很容易在 iTunes Connect 中找到它的 Apple ID

Finding the Apple ID in iTunes Connect

搜索 API

Apple 提供动态搜索 API,用于查询 App Store、iTunes 和 iBookstore 中的所有产品。 可在 Apple 的关联资源中找到与如何访问搜索 API 相关的信息,尽管该 API 向任何人公开(不只是已注册的附属公司)。 可以分析生成的 JSON 发现要与 SKStoreProductViewController 一起使用的 Apple ID trackId

结果还将包括其他元数据,包括可用于在应用中呈现产品的显示信息和插图 URL。

以下是一些示例:

企业合作伙伴源

Apple 以可下载的数据库就绪平面文件的形式,为已批准的合作伙伴提供其所有产品的完整数据转储。 如果你有资格访问企业合作伙伴源,则可以在该数据集中找到任何产品的 Apple ID。

企业合作伙伴源的许多用户都是联盟计划的成员,该计划允许从产品销售额中获得佣金。 SKStoreProductViewController 不支持关联 ID(在撰写时)。

可以从产品的 iTunes 预览 URL 链接推断出产品的 Apple ID。 在任何 iTunes 产品链接中(适用于应用、音乐或图书),打开以 id 开头的 URL 的部分,并使用其之后的一串数字。

例如,指向 iBooks 的直接链接为

http://itunes.apple.com/us/app/ibooks/id364709193?mt=8

Apple ID 为 364709193。 与 MWC2012 应用类似,直接链接为

http://itunes.apple.com/us/app/mwc-2012-unofficial/id496963922?mt=8

Apple ID 为 496963922

应用内购买托管内容

如果你的应用内购买由可下载内容(例如,图书或其他媒体、游戏等级艺术和配置,或其他大型文件)构成,则这些文件之前托管在你的 Web 服务器上,并且应用必须包含代码才能在购买后安全地下载它们。 从 iOS 6 开始,Apple 将在其服务器上托管文件,从而不再需要单独的服务器。 此功能仅适用于非易耗型产品,不适用于易耗型或订阅。 使用 Apple 托管服务的优点包括以下几点:

  • 节省托管和带宽成本。
  • 可能比你当前使用的任何服务器主机的可缩放性都更强。
  • 编写的代码更少,因为无需生成任何服务器端处理。
  • 为你实现后台下载。

注意:不支持在 iOS 模拟器中测试托管应用内购买内容。因此,必须使用真实设备进行测试。

托管内容基础知识

在 iOS 6 之前,有两种方法可以提供产品(Xamarin 的应用内购买文档中有更加详细的介绍):

  • 内置产品 - 通过购买“解锁”的功能,但内置在应用内(要么以代码,要么以嵌入的资源的形式)。 内置产品的示例包括解锁的照片滤镜或游戏内奖励。
  • 服务器交付的产品 - 购买后,应用必须从你运营的服务器下载内容。 此内容在购买期间下载,存储在设备上,然后作为提供产品的一部分进行呈现。 示例包括由背景艺术和配置文件组成的图书、杂志期刊或游戏级别。

在 iOS 6 中,Apple 提供各种服务器交付的产品:它们会在其服务器上托管你的内容文件。 这使得生成服务器交付的产品要简单得多,因为你不需要运行单独的服务器,商店工具包提供以前必须自行编写的后台下载功能。 若要利用 Apple 的托管,请为新的应用内购买产品启用内容托管,并修改商店工具包代码以利用它。 然后,使用 Xcode 生成产品内容文件,并将其上传到 Apple 的服务器以供审核和发布。

The build and deliver process

使用 App Store 为托管内容提供应用内购买需要进行以下设置和配置:

  • iTunes Connect - 必须向 Apple 提供你的银行和税务信息,以便 Apple 能够代表你接收汇款。 然后,可以配置要销售的产品,并设置要测试购买的沙盒用户帐户。 此外,还必须为希望 Apple 托管的非易耗型产品配置托管内容
  • iOS 预配门户 - 创建捆绑识别符并允许 App Store 访问你的应用,就像对任何支持应用内购买的应用所进行的操作一样。
  • 商店工具包 – 向应用添加代码,用于显示产品、购买产品和还原交易。 在 iOS 6 中,商店工具包还会在后台管理产品内容的下载,并会更新下载进度。
  • 自定义代码 – 跟踪客户进行的购买,并提供他们购买的产品或服务。 利用新的 iOS 6 应用商店工具包类(如 SKDownload)检索 Apple 托管的内容。

以下部分使用本文的示例代码介绍如何实现托管内容,从创建和上传包,到管理购买和下载过程。

代码示例

示例项目 HostedNonConsumables(在 StoreKitiOS6.zip 中)使用托管内容。 该应用提供两个“图书章节”进行销售,其内容托管在 Apple 的服务器上。 该内容由文本文件和图片组成,尽管在实际应用中使用的内容会复杂得多。

应用在购买之前、期间和之后,如下所示:

The app looks like this before, during and after a purchase

文本文件和图片下载并复制到应用的文档目录中。 有关应用存储可以使用的不同目录的详细信息,请参阅文件系统文档

iTunes Connect

创建将会使用 Apple 的内容托管功能的新产品时,请务必选择“非易耗型”产品类型。 其他产品类型不支持内容托管。 此外,不应为销售的现有产品启用内容托管;请仅为新产品启用内容托管。

Select the Non-Consumable product type

输入“产品 ID”。 稍后创建此产品的内容时,将需要此 ID。

Enter a Product ID

在“详细信息”部分中设置内容托管。 在应用内购买上线之前,如果希望取消(即使已经上传了一些测试内容),请取消选中“通过 Apple 托管内容”复选框。 但是,应用内购买一旦上线就无法删除内容托管。

Hosting content with Apple

打开内容托管后,产品会进入“等待上传”状态,并会显示以下这条消息:

The product will enter Waiting for Upload status and show this message

内容包应使用 Xcode 创建,并使用存档工具上传。 下个章节“创建 .PKG 文件”会介绍关于创建内容包的说明。

创建 .PKG 文件

上传到 Apple 的内容文件必须满足以下限制:

  • 大小不能超过 2 GB。
  • 不能包含指向内容之外的可执行代码(或符号链接)。
  • 格式设置必须正确(包含一个 .plist 文件),且具有 .pkg 文件扩展名。 如果使用 Xcode 按照这些说明操作,则会自动执行此操作。

只要它们满足这些限制,就可以添加许多不同的文件和文件类型。 在交付给应用之前会先对内容进行压缩,交付后先由商店工具包进行解压缩,然后你的代码再进行访问。

上传内容包后,可将其替换为较新的内容。 必须通过正常流程上传和提交新内容进行审核/批准。 递增更新内容包中的 ContentVersion 字段以显示其是较新的内容。

Xcode 应用内购买内容项目

为应用内购买产品创建内容包当前需要 Xcode。 不需要 OBJECTIVE-C 编码;Xcode 为仅包含文件和 plist 的这些包提供了一个新的项目类型。

我们的示例应用程序包含用于销售的黑乎乎章节 - 每个章节内容包将会包含:

  • 文本文件,以及
  • 代表该章节的一张图片。

首先,从菜单中选择“文件>新建项目,然后选择“应用内购买内容”:

Choose In-App Purchase Content

输入“产品名称”和“公司识别符”,让“捆绑标别符”匹配在 iTunes Connect 中为该产品输入的“产品 ID”。

Enter the Name and Identifier

现在,你会有一个空白的应用内购买内容项目。 可以右键单击“添加文件…”或将文件拖到“项目导航器”中。 确保 ContentVersion 正确(应以 1.0 开始,但如果之后选择更新内容,请记得递增它)。

此屏幕截图显示了 Xcode,其中包含项目中包含的内容文件和主窗口中可见的 plist 条目:

This screenshot shows Xcode with the content files included in the project and the plist entries visible in the main window

添加所有内容文件后,可以保存此项目,稍后再对其进行编辑,或开始上传过程。

上传 .PKG 文件

上传内容包的最简单方法是使用 Xcode 存档工具。 从菜单中选择“产品>存档”即可开始:

Choose Archiven

然后,内容包将显示在存档中,如下所示。 显示此行的存档类型和图标为“应用内购买内容存档”。 单击“验证…”,在不实际执行上传的情况下检查你的内容包是否存在错误。

Validate the package

使用 iTunes Connect 凭据登录:

Login with your iTunes Connect credentials

选择正确的应用和应用内购买,将此内容与以下进行关联:

Choose the correct application and in-app purchase to associate this content with

应会看到类似于以下屏幕截图的消息:

An example no issues message

现在,请完成类似的过程,但单击 分发...”会实际上传内容。

Distribute the app

选择第一个选项,上传内容:

Upload the content

重新登录:

Login in

选择正确的应用程序和应用内购买记录,将内容上传到:

Choose the application and in-app purchase record

等待文件上传:

The content upload dialog

上传完成后,将显示一条消息,告知你内容已提交到 App Store。

An example successful upload message

完成后,当你返回到 iTunes Connect 的产品页时,会显示包的详细信息且已处于“准备提交”状态。 当产品处于此状态时,可以在沙盒环境中开始测试。 无需“提交”产品即可在沙盒中进行测试。

iTunes Connect it will show the package details and be in Ready to Submit status

上传存档和 iTunes Connect 状态更新之间可能需要一些时间(例如几分钟)。 可以单独提交产品进行审核,也可以将其与应用程序二进制文件一起提交。 只有在 Apple 正式批准该内容后,才能在生产应用商店中购买你的应用。

PKG 文件格式

使用 Xcode 和存档工具创建和上传托管内容包意味着你永远不会看到包本身的内容。 为示例应用创建的包中的文件和目录类似于下面的屏幕截图,其中 plist 文件位于根目录下,产品文件位于“内容”子目录下:

The plist file in the root and the product files in a Contents subdirectory

请注意包的目录结构(尤其是 Contents 子目录中文件的位置),因为需要了解此信息才能从设备上的包中提取文件。

更新包内容

更新内容的流程在获得批准之后:

  • 在 Xcode 中编辑应用内购买内容项目。
  • 向上凸起版本号。
  • 再次上传到 iTunes Connect。 后续购买者将会自动获取最新版本,但已有旧版本的用户不会收到任何通知。
  • 你的应用负责通知用户并鼓励他们检索较新版本的内容。 应用还必须使用应用商店工具包的“还原”功能生成一个可下载新版本的函数。
  • 若要确定是否存在较新版本,可以在应用中生成一项功能来提取 SKProducts(例如,用于检索产品价格的相同过程),并比较 ContentVersion 属性。

购买概述

阅读本章节之前,请查看现有的应用内购买文档

购买并下载包含托管内容的产品时发生的事件序列,如下图所示:

The sequence of events that occurs when a product with hosted content is purchased and download

  1. 可以在启用了托管内容的 iTunes Connect 中创建新产品。 实际内容在 Xcode 中单独构造(就像将文件拖动到文件夹中一样),然后存档并上传到 iTunes(无需编写代码)。 然后,将提交每个产品以供审批,之后产品才可以购买。 在示例代码中,这些产品 ID 是硬编码的,但如果将可用产品列表存储在远程服务器上,则使用 Apple 托管内容更加灵活,在将新产品和内容提交到 iTunes Connect 时更新它。
  2. 当用户购买产品时,交易将置于付款队列中进行处理。
  3. 商店工具包将购买请求转发到 iTunes 服务器进行处理。
  4. 事务在 iTunes 服务器上完成(例如,对客户收费),并且收据将返回到应用,附加了产品信息,包括是否可下载(如果是,文件大小和其他元数据)。
  5. 你的代码应检查产品是否可下载,如果是,则发出也放置在付款队列中的内容下载请求。 商店工具包将此请求发送到 iTunes 服务器。
  6. 服务器将内容文件返回到商店工具包,该工具包提供一个回叫,用于返回代码的下载进度和剩余时间估算值。
  7. 完成后,你会收到通知,并在缓存文件夹中传递了文件位置。
  8. 代码应复制文件并对其进行验证,并保存需要记住产品已购买的任何状态。 利用这一机会,在新文件上正确设置备份标志(提示:如果文件来自服务器,并且用户从未编辑过,则应跳过备份,因为用户将来始终可以从 Apple 的服务器中检索它们)。
  9. 调用 FinishTransaction。 此步骤非常重要,因为它会从付款队列中删除事务。 在将内容从缓存目录复制出来之前,不要调用 FinishTransaction。 调用 FinishTransaction 后,缓存的文件可能会快速清除。

实现托管内容购买

应将以下信息与完整的应用内购买文档一起阅读。 本文档中的信息侧重于托管内容与上一实现之间的差异。

已添加或更改以下类以支持 iOS 6 中的托管内容:

  • SKDownload – 表示正在下载的新类。 该 API 允许表示多个产品的下载,但最初只实现一个。
  • SKProduct – 添加的新属性:DownloadableContentVersionContentLengths 阵列。
  • SKPaymentTransaction - 添加的新属性:Downloads,如果此产品具有可以下载的托管内容,则其中会包含一系列 SKDownload 对象。
  • SKPaymentQueue – 添加的新方法:StartDownloads。 调用包含 SKDownload 对象的此方法提取其托管的内容。 下载可以在后台进行。
  • SKPaymentTransactionObserver – 新方法:UpdateDownloads。 商店工具包调用此方法,其中包含有关当前下载操作的进度信息。

新增 SKDownload 类的详细信息:

  • 进度 – 一个介于 0-1 之间的值,可用于向用户显示百分比完成指示器。 请勿使用 Progress == 1 检测下载是否已完成,检查状态 == 已完成。
  • TimeRemaining – 估计剩余的下载时间(以秒为单位)。 -1 表示仍在计算估计值。
  • 状态 – 活动、等待、已完成、失败、暂停、已取消。
  • ContentURL – 目录中内容放置在磁盘上的 Cache 文件位置。 仅在下载“完成”后填充。
  • 错误 – 如果状态为“失败”,请检查此属性。

示例代码中的类之间的交互显示在此图中(特定于托管内容购买的代码以绿色显示):

Hosted content purchases is shown in green in this diagram

本章节的剩余部分显示使用这些类的示例代码:

CustomPaymentObserver (SKPaymentTransactionObserver)

更改现有 UpdatedTransactions 替代以检查可下载内容,并在必要时调用 StartDownloads

public override void UpdatedTransactions (SKPaymentQueue queue, SKPaymentTransaction[] transactions)
{
    foreach (SKPaymentTransaction transaction in transactions) {
        switch (transaction.TransactionState) {
        case SKPaymentTransactionState.Purchased:
            // UPDATED FOR iOS 6
            if (transaction.Downloads != null && transaction.Downloads.Length > 0) {
                // Purchase complete, and it has downloads... so download them!
                SKPaymentQueue.DefaultQueue.StartDownloads (transaction.Downloads);
                // CompleteTransaction() call has moved after downloads complete
            } else {
                // complete the transaction now
                theManager.CompleteTransaction(transaction);
            }
            break;
        case SKPaymentTransactionState.Failed:
            theManager.FailedTransaction(transaction);
            break;
        case SKPaymentTransactionState.Restored:
            // TODO: you must decide how to handle restored transactions.
            // Triggering all the downloads at once is not advisable.
            theManager.RestoreTransaction(transaction);
            break;
        default:
            break;
        }
    }
}

新替代的方法 UpdatedDownloads 如下所示。 商店工具包在 UpdatedTransactions 中触发了 StartDownloads 之后调用此方法。 在不确定的间隔多次调用此方法,以为你提供下载进度,然后当下载完成时再次调用。 请注意,此方法接受 SKDownload 对象的数组,因此每一次调用此方法都会提供队列中多个下载的状态。 如下面的实现所示,每次执行相应的操作时,都会检查下载状态。

// ENTIRELY NEW METHOD IN iOS6
public override void PaymentQueueUpdatedDownloads (SKPaymentQueue queue, SKDownload[] downloads)
{
    Console.WriteLine (" -- PaymentQueueUpdatedDownloads");
    foreach (SKDownload download in downloads) {
        switch (download.DownloadState) {
        case SKDownloadState.Active:
            // TODO: implement a notification to the UI (progress bar or something?)
            Console.WriteLine ("Download progress:" + download.Progress);
            Console.WriteLine ("Time remaining:   " + download.TimeRemaining); // -1 means 'still calculating'
            break;
        case SKDownloadState.Finished:
            Console.WriteLine ("Finished!!!!");
            Console.WriteLine ("Content URL:" + download.ContentUrl);

            // UNPACK HERE! Calls FinishTransaction when it's done
            theManager.SaveDownload (download);

            break;
        case SKDownloadState.Failed:
            Console.WriteLine ("Failed"); // TODO: UI?
            break;
        case SKDownloadState.Cancelled:
            Console.WriteLine ("Canceled"); // TODO: UI?
            break;
        case SKDownloadState.Paused:
        case SKDownloadState.Waiting:
            break;
        default:
            break;
        }
    }
}

InAppPurchaseManager (SKProductsRequestDelegate)

此类包含在每个下载成功完成后调用的新方法 SaveDownload

托管内容已成功下载并解压缩到 Cache 目录中。 .PKG 文件的结构要求所有文件都保存在 Contents 子目录中,如此,下方的代码才能提取 Contents 子目录中的文件。

代码循环访问内容包中的所有文件,并将其复制到为 ProductIdentifier 命名的子目录中的 Documents 目录中。 最后调用 CompleteTransaction,即调用 FinishTransaction 删除付款队列中的事务。

// ENTIRELY NEW METHOD IN iOS 6
public void SaveDownload (SKDownload download)
{
    var documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal); // Documents folder
    var targetfolder = System.IO.Path.Combine (documentsPath, download.Transaction.Payment.ProductIdentifier);
    // targetfolder will be "/Documents/com.xamarin.storekitdoc.montouchimages/" or something like that
    if (!System.IO.Directory.Exists (targetfolder))
        System.IO.Directory.CreateDirectory (targetfolder);
    foreach (var file in System.IO.Directory.EnumerateFiles
             (System.IO.Path.Combine(download.ContentUrl.Path, "Contents"))) { // Contents directory is the default in .PKG files
        var fileName = file.Substring (file.LastIndexOf ("/") + 1);
        var newFilePath = System.IO.Path.Combine(targetfolder, fileName);
        if (!System.IO.File.Exists(newFilePath)) // HACK: this won't support new versions...
            System.IO.File.Copy (file, newFilePath);
        else
            Console.WriteLine ("already exists " + newFilePath);
    }
    CompleteTransaction (download.Transaction); // so it gets 'finished'
}

当调用 FinishTransaction 时,下载的文件不再一定位于 Cache 目录中。 在调用 FinishTransaction 之前,应先复制所有文件。

其他注意事项

上面的示例代码演示了托管内容购买的相当简单的实现方法。 必须考虑到以下一些其他要点:

检测更新的内容

虽然可以更新托管的内容包,但商店工具包不提供任何机制用于将这些更新推送到已下载并购买该产品的用户。 若要实现此功能,代码可能会定期检查新的 SKProduct.ContentVersion 属性(如果 SKProductDownloadable),并检测该值是否有递增。 或者,也可以构建推送通知系统。

安装更新的内容版本

上面的示例代码跳过文件复制(如果文件已存在)。 如果希望支持正在下载的内容的较新版本,则这样做并不合适。

另一种方法是将内容复制到名为版本的文件夹,并跟踪其当前版本(例如,在 NSUserDefaults 中或是存储已完成的购买记录的任何位置)。

还原事务

调用 SKPaymentQueue.DefaultQueue.RestoreCompletedTransactions 时,商店工具包会为用户返回所有之前的事务。 如果用户购买过大量物品,或每笔购买具有大量内容包,则还原会产生大量网络流量,因为所有内容要同时排队进行下载。

请考虑跟踪产品是否是从关联的内容包的实际下载中单独购买的。

暂停、重启和取消下载

虽然示例代码未演示此功能,但可以暂停和重启托管内容下载。 SKPaymentQueue.DefaultQueue 具有方法实现 PauseDownloadsResumeDownloadsCancelDownloads

如果代码在下载 Finished 之前调用付款队列中的 FinishTransaction,则会自动取消相应下载。

在下载的内容上设置 SKIP-Backup 标志

Apple 的 iCloud 备份指南建议,应备份可轻松从服务器还原的非用户内容(因为这样会不必要地用光 iCoud 的存储空间)。 有关设置备份属性的详细信息,请参阅文件系统文档。

总结

本文介绍了 iOS6 中商店工具包的两项新功能:从应用中购买 iTunes 和其他内容,并使用 Apple 的服务器托管自己的应用内购买。 应结合现有的应用内购买文档阅读本文,以全面涵盖实现商店工具包功能。