鍍金池/ 教程/ iOS/ 產(chǎn)品交付
恢復(fù)購買記錄
內(nèi)置購買
介紹
準(zhǔn)備應(yīng)用程序?qū)彶?/span>
創(chuàng)建和配置產(chǎn)品
獲取產(chǎn)品信息
支付請求
使用訂閱
如何測試應(yīng)用程序內(nèi)置購買
產(chǎn)品交付

產(chǎn)品交付

In the final part of the purchase process, your app waits for the App Store to process the payment request, stores information about the purchase for future launches, downloads the purchased content, and then marks the transaction as finished, as shown in Figure 4-1.

購買過程的最后一部分是你的應(yīng)用程序等待應(yīng)用商店處理支付請求,存儲本次購買的信息以便將來啟動,下載購買的內(nèi)容,然后標(biāo)記交易結(jié)束,如圖4-1。

Figure 4-1 Stages of the purchase process—delivering products http://wiki.jikexueyuan.com/project/in-app-purchase/images/7.png" alt="" />

Waiting for the App Store to Process Transactions

一、等待應(yīng)用商店處理交易

The transaction queue plays a central role in letting your app communicate with the App Store through the Store Kit framework. You add work to the queue that the App Store needs to act on, such as a payment request that needs to be processed. When the transaction’s state changes—for example, when a payment request succeeds—Store Kit calls the app’s transaction queue observer. It’s your decision which class acts as the observer. In very small apps, you could handle all the Store Kit logic in the app delegate, including observing the transaction queue. In most apps, you create a separate class that handles this observer logic along with the rest of your app’s store logic. The observer must conform to the SKPaymentTransactionObserver protocol.

交易隊列通過商店 Kit 框架在你的應(yīng)用和應(yīng)用商店的交流過程中起著核心作用。你把應(yīng)用商店需要處理的工作添加到隊列,比如一個需要被處理的支付請求。 當(dāng)交易狀態(tài)改變時---比如,當(dāng)一個支付請求成功時---商店 Kit 調(diào)用應(yīng)用的交易隊列觀察者(observer)。 你需要決定哪個類作為觀察者(observer)。 在很少的應(yīng)用中,你可以在應(yīng)用委托中處理所有的商店 Kit 邏輯,包括觀察交易隊列。 在大多數(shù)應(yīng)用中,你創(chuàng)建一個單獨的類來處理該觀察者邏輯和其余的應(yīng)用程序商店邏輯。 觀察者必須遵循 SKPaymentTransactionObserver 協(xié)議。

Using an observer means your app doesn’t constantly poll the status of its active transactions. In addition to using the transaction queue for payment requests, your app also uses the transaction queue to download Apple-hosted content and to find out that subscriptions have been renewed.

使用一個觀察者意味著應(yīng)用程序不會不斷地查詢其活動交易的狀態(tài)。 除了使用交易隊列來處理支付請求,應(yīng)用程序還使用它來下載蘋果托管的內(nèi)容并找出已經(jīng)更新的訂閱。

Register a transaction queue observer when your app is launched, as shown in Listing 4-1. Make sure that the observer is ready to handle a transaction at any time, not just after you add a transaction to the queue. For example, consider the case of a user buying something in your app right before going into a tunnel. Your app isn’t able to deliver the purchased content because there’s no network connection. The next time your app is launched, Store Kit calls your transaction queue observer again and delivers the purchased content at that time. Similarly, if your app fails to mark a transaction as finished, Store Kit calls the observer every time your app is launched until the transaction is properly finished.

當(dāng)你的應(yīng)用啟動時注冊一個交易隊列,如列表4-1. 確保觀察者已經(jīng)隨時準(zhǔn)備好處理交易,而不只在你添加一個交易到隊列后處理。 舉個例子,考慮一個用戶在他進入一個隧道(tunnel)之前正好在你的應(yīng)用中購買了一些東西。 你的應(yīng)用不能傳遞被購的內(nèi)容,因為沒有網(wǎng)絡(luò)連接。當(dāng)你的應(yīng)用下次啟動時,商店 Kit 再次調(diào)用你的交易隊列觀察者并在那時傳遞被購的內(nèi)容。 類似地,如果你的應(yīng)用程序標(biāo)記結(jié)束一個交易失敗,每次你的應(yīng)用啟動時,商店 Kit 都會調(diào)用觀察者直到交易被正確地結(jié)束。

Listing 4-1 Registering the transaction queue observer


- (BOOL)application:(UIApplication *)application
 didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    /* ... */

    [[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
}

Implement the paymentQueue:updatedTransactions: method on your transaction queue observer. Store Kit calls this method when the status of a transaction changes—for example, when a payment request has been processed. The transaction status tells you what action your app needs to perform, as shown in Table 4-1 and Listing 4-2. Transactions in the queue can change state in any order. Your app needs to be ready to work on any active transaction at any time.

在你的交易隊列觀察者中實現(xiàn) paymentQueue:updatedTransactions: 方法。 交易狀態(tài)改變時,商店 Kit 調(diào)用該方法--- 比如,當(dāng)一個支付請求被處理時。 交易狀態(tài)告訴你的應(yīng)用該執(zhí)行什么動作,如表 4-1 和列表 4-2. 隊列中的交易可以以任何順序改變狀態(tài)。 你的應(yīng)用需要準(zhǔn)備好在任何時候處理任何活動交易。

Table 4-1 Transaction statuses and corresponding actions表 4-1 交易狀態(tài)和相應(yīng)的動作

http://wiki.jikexueyuan.com/project/in-app-purchase/images/8.png" alt="" />

Listing 4-2 Responding to transaction statuses

列表 4-2 相應(yīng)交易狀態(tài)

 (void)paymentQueue:(SKPaymentQueue *)queue
 updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            // Call the appropriate custom method.
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [self restoreTransaction:transaction];
            default:
                break;
        }
    }
}

To keep your user interface up to date while waiting, the transaction queue observer can implement optional methods from the SKPaymentTransactionObserver protocol as follows. The paymentQueue:removedTransactions: method is called when transactions are removed from the queue—in your implementation of this method, remove the corresponding items from your app’s UI. The paymentQueueRestoreCompletedTransactionsFinished: or paymentQueue:restoreCompletedTransactionsFailedWithError: method is called when Store Kit finishes restoring transactions, depending on whether there was an error. In your implementation of these methods, update your app’s UI to reflect the success or error.

為了在等待時保持用戶界面最新,交易隊列觀察者可以實現(xiàn)SKPaymentTransactionObserver 協(xié)議的以下可選方法:當(dāng)交易被從隊列中移除時,調(diào)用paymentQueueRestoreCompletedTransactionsFinished: 方法--- 在該方法的實現(xiàn)中,從你的應(yīng)用的 UI 移除相應(yīng)的產(chǎn)品。 當(dāng)商店 Kit 結(jié)束恢復(fù)交易時,根據(jù)是否有 error 發(fā)生調(diào)用paymentQueueRestoreCompletedTransactionsFinished: 或 paymentQueue:restoreCompletedTransactionsFailedWithError: 方法。 在這些方法的實現(xiàn)中,更新你的應(yīng)用的 UI 來反映成功或 error。

Persisting the Purchase

二、保留購買記錄

After making the product available, your app needs to make a persistent record of the purchase. Your app uses that persistent record on launch to continue to make the product available. It also uses that record to restore purchases, as described in “Restoring Purchased Products.” Your app’s persistence strategy depends the type of products you sell and the versions of iOS.

產(chǎn)品有效之后,你的應(yīng)用需要做購買的一個持久記錄。 當(dāng)啟動時,你的應(yīng)用使用該持久記錄讓產(chǎn)品變得有效。 它還使用該記錄來恢復(fù)購買,正如“Restoring Purchased Products.”中所述。 你的應(yīng)用的持久化策略取決于你出售的產(chǎn)品類型以及 iOS 的版本。

  • For non-consumable products and auto-renewable subscriptions in iOS 7 and later, use the app receipt as your persistent record.

iOS 7 以及之后的版本,對于非耗材產(chǎn)品和自動再生訂閱,使用應(yīng)用收據(jù)作為你的持久記錄。

  • For non-consumable products and auto-renewable subscriptions in versions of iOS earlier than iOS 7, use the User Defaults system or iCloud to keep a persistent record.

iOS 7 之前的版本,對于非耗材產(chǎn)品和自動再生訂閱,使用用戶默認系統(tǒng)或 iCloud 來保留一個持久記錄。

  • For non-renewing subscriptions, use iCloud or your own server to keep a persistent record.

對于非再生訂閱,使用 iCloud 或你自己的服務(wù)器來保留一個持久記錄。

For consumable products, your app updates its internal state to reflect the purchase, but there’s no need to keep a persistent record because consumable products aren’t restored or synced across devices. Ensure that the updated state is part of an object that supports state preservation (in iOS) or that you manually preserve the state across app launches (in iOS or OS X). For information about state preservation, see “State Preservation and Restoration” in iOS App Programming Guide.

對于耗材產(chǎn)品,你的應(yīng)用更新它的內(nèi)部狀態(tài)來反映購買,但是沒有必要保留一個持久記錄因為耗材產(chǎn)品不能恢復(fù)或不能跨設(shè)備同步。 確保被更新狀態(tài)是一個支持狀態(tài)保留(in iOS)對象的一部分,或者是一個你手動保留整個應(yīng)用啟動狀態(tài)的對象(in iOS 或者 OS X). 關(guān)于狀態(tài)保留的信息,請看iOS App Programming Guide 中的“State Preservation and Restoration” 。

When using the User Defaults system or iCloud, your app can store a value, such as a number or a Boolean, or a copy of the transaction receipt. In OS X, the user can edit the User Defaults system using the defaults command. Storing a receipt requires more application logic, but prevents the persistent record from being tampered with.

當(dāng)你使用用戶默認系統(tǒng)(User Defaults system)或 iCloud 時,你的應(yīng)用可以存儲一個值,可以時一個數(shù)字或布爾值,或者備份交易收據(jù)。 在 OS X 中,用戶可以使用 defaults 命令編輯用戶默認系統(tǒng)。 存儲一個收據(jù)除了防止持久記錄被篡改外,還要求更多的應(yīng)用邏輯。

When persisting via iCloud, note that your app’s persistent record is synced across devices, but your app is responsible for downloading any associated content on other devices.

當(dāng)你通過 iCloud 保留記錄時,請注意應(yīng)用程序的持久記錄是夸設(shè)備同步的,但是在別的設(shè)備上也是有你的應(yīng)用負責(zé)下載任何相關(guān)內(nèi)容。

Persisting Using the App Receipt 1.使用應(yīng)用收據(jù)來保留記錄

The app receipt contains a record of the user’s purchases, cryptographically signed by Apple. For more information, see Receipt Validation Programming Guide.

應(yīng)用記錄包括了用戶購買的一個記錄,它由蘋果公司加密簽名。更多詳情,請看 Receipt Validation Programming Guide.

Information about consumable products and non-renewing subscriptions is added to the receipt when they’re paid for and remains in the receipt until you finish the transaction. After you finish the transaction, this information is removed the next time the receipt is updated—for example, the next time the user makes a purchase.

關(guān)于耗材產(chǎn)品和無需更新訂閱的產(chǎn)品信息在它們被支付后加入收據(jù),并保留該信息直到你結(jié)束這個交易。 當(dāng)你結(jié)束了這個交易后,該信息將被刪除,下一次的收據(jù)被更新---比如,下次用戶做個一個購買。

Information about all other kinds of purchases is added to the receipt when they’re paid for and remains in the receipt indefinitely.

所有其它類型的購買信息在它們被支付時加入收據(jù),并且永久保留該收據(jù)。

Persisting a Value in User Defaults or iCloud

2.在用戶默認系統(tǒng)或 iCloud 中保留一個值

To store information in User Defaults or iCloud, set the value for a key.

要想在用戶默認系統(tǒng)或 iCloud 中保留信息,把該值設(shè)置為一個關(guān)鍵字(key)。

#if USE_ICLOUD_STORAGE
NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore];
#else
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
#endif

[storage setBool:YES forKey:@"enable_rocket_car"];
[storage setObject:@15 forKey:@"highest_unlocked_level"];

[storage synchronize];

Persisting a Receipt in User Defaults or iCloud

  1. 在用戶默認系統(tǒng)或 iCloud 中保留一個收據(jù)

To store a transaction’s receipt in User Defaults or iCloud, set the value for a key to the data of that receipt.

要想在用戶默認系統(tǒng)或 iCloud 中存儲一個交易收據(jù),把值設(shè)置為一個關(guān)鍵字(key)賦值給收據(jù)。

#if USE_ICLOUD_STORAGE
NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore];
#else
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
#endif

NSData *newReceipt = transaction.transactionReceipt;
NSArray *savedReceipts = [storage arrayForKey:@"receipts"];
if (!receipts) {
    // Storing the first receipt
    [storage setObject:@[newReceipt] forKey:@"receipts"];
} else {
    // Adding another receipt
    NSArray *updatedReceipts = [savedReceipts arrayByAddingObject:newReceipt];
    [storage setObject:updatedReceipts forKey:@"receipts"];
}

[storage synchronize];

Persisting a Receipt in User Defaults or iCloud

3.在用戶默認系統(tǒng)或 iCloud 中保留一個收據(jù)

To store a transaction’s receipt in User Defaults or iCloud, set the value for a key to the data of that receipt.

要想在用戶默認系統(tǒng)或 iCloud 中存儲一個交易收據(jù),把值設(shè)置為一個關(guān)鍵字(key)賦值給收據(jù)。

#if USE_ICLOUD_STORAGE
NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore];
#else
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
#endif

NSData *newReceipt = transaction.transactionReceipt;
NSArray *savedReceipts = [storage arrayForKey:@"receipts"];
if (!receipts) {
    // Storing the first receipt
    [storage setObject:@[newReceipt] forKey:@"receipts"];
} else {
    // Adding another receipt
    NSArray *updatedReceipts = [savedReceipts arrayByAddingObject:newReceipt];
    [storage setObject:updatedReceipts forKey:@"receipts"];
}

[storage synchronize];

Persisting Using Your Own Server

  1. 用自己的服務(wù)器保留

Send a copy of the receipt to your server along with some kind of credentials or identifier so you can keep track of which receipts belong to a particular user. For example, let users identify themselves to your server with an email or user name, plus a password. Don’t use the identifierForVendor property of UIDevice—you can’t use it to identify and restore purchases made by the same user on a different device, because different devices have different values for this property.

把收據(jù)的副本和某些憑據(jù)和識別碼一起發(fā)送到你的服務(wù)器,這樣你可以隨時查看某個用戶的收據(jù)。 比如,讓用戶使用 email 或用戶名密碼登陸。不要使用 UIDevice 類的 identifierForVendor 特性---你不能用它來認證和恢復(fù)不同設(shè)備上同一用戶的購買記錄,因為不同的設(shè)備的該特性有不同的值。

Unlocking App Functionality

三、解鎖應(yīng)用功能

If the product enables app functionality, set a Boolean value to enable the code path and update your user interface as needed. To determine what functionality to unlock, consult the persistent record that your app made when the transaction occurred. Your app needs to update this Boolean value whenever a purchase is completed and at app launch.

如果產(chǎn)品開啟應(yīng)用功能,給開啟代碼設(shè)置一個布爾值并根據(jù)需要更新你的界面。為了確認解鎖什么功能,當(dāng)交易發(fā)生時咨詢應(yīng)用程序做的持久記錄。你需要在一個購買完成以及應(yīng)用啟動時更新該布爾值。

For example, using the app receipt, your code might look like the following:

舉個例子,使用應(yīng)用收據(jù),你的代碼應(yīng)該類似以下代碼:

NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];

// Custom method to work with receipts
BOOL rocketCarEnabled = [self receipt:receiptData
        includesProductID:@"com.example.rocketCar"];

Or, using the User Defaults system:

或者,使用用戶默認系統(tǒng):

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL rocketCarEnabled = [defaults boolForKey:@"enable_rocket_car"];

Then use that information to enable the appropriate code paths in your app.

然后使用該信息來開啟應(yīng)用程序中的相應(yīng)代碼路徑。

if (rocketCarEnabled) {
    // Use the rocket car.
} else {
    // Use the regular car.
}

Delivering Associated Content

四、傳遞相關(guān)內(nèi)容

If the product has associated content, your app needs to deliver that content to the user. For example, purchasing a level in a game requires delivering the files that define that level, and purchasing additional instruments in a music app requires delivering the sound files needed to let the user play those instruments.

如果產(chǎn)品有相關(guān)內(nèi)容,你的應(yīng)用程序需要傳遞該內(nèi)容給用戶。 比如,購買了游戲中的一個關(guān)卡需要傳遞定義了該關(guān)卡的文件,在音樂應(yīng)用中購買額外的樂器需要傳遞那些樂器需要的聲音文件。

You can embed that content in your app’s bundle or you can download it as needed—each approach has its advantages and disadvantages. If you include too little content in your app bundle, the user must wait for even small purchases to be downloaded. If you include too much in your app bundle, the initial download of the app takes too long, and the space is wasted for users who don’t purchase the corresponding products. Additionally, if your app is too large, users won’t be able to download it over cellular networks.

你可以把這些內(nèi)容整合到你的應(yīng)用程序束中或者你可以根據(jù)需要下載它--每種方法都有它的優(yōu)勢和劣勢。 如果你的應(yīng)用束中包含了太少的內(nèi)容,即使用戶購買再少的內(nèi)容也必須等待下載。 如果你的應(yīng)用束中包含了太多的內(nèi)容,應(yīng)用程序的初始下載太耗時,對于那些不想購買相應(yīng)產(chǎn)品的用戶來說太浪費內(nèi)存了。此外,如果你的應(yīng)用程序太大,用戶將無法通過蜂窩網(wǎng)絡(luò)(cellular networks)下載它。

Embed smaller files (up to a few megabytes) in your app, especially if you expect most users to buy that product. Content in your app bundle can be made available immediately when the user purchases it. However, to add or update content in your app bundle, you have to submit an updated version of your app.

在你的應(yīng)用中嵌入少量的文件(最多幾兆),特別是如果你期望大多數(shù)用戶可以購買該產(chǎn)品時。 應(yīng)用束中的內(nèi)容在用戶購買時可以立即提供。然而,要想添加或更新應(yīng)用束中的內(nèi)容,你必須提交應(yīng)用程序的更新版本。

Download larger files when needed. Separating content from your app bundle keeps your app’s initial download small. For example, a game can include the first level in its app bundle and let users download the rest of the levels when they’re purchased. Assuming your app fetches its list of product identifiers from your server, and not hard-coded in the app bundle, you don’t need to resubmit your app to add or update content that is downloaded by your app.

需要時下載大量的文件。把內(nèi)容從應(yīng)用束中分離可以讓你的應(yīng)用在初次下載時小。比如,游戲可以在應(yīng)用束中包含第一個關(guān)卡,并讓用戶在購買時下載剩余的關(guān)卡。 假設(shè)應(yīng)用程序從你的服務(wù)器獲取它的產(chǎn)品識別碼列表,而不是硬性編碼在應(yīng)用束中,你就不需要重復(fù)提交你的應(yīng)用程序來添加或更新應(yīng)用程序需要下載的內(nèi)容。

In iOS 6 and later, most apps should use Apple-hosted content for downloaded files. You create an Apple-hosted content bundle using the In-App Purchase Content target in Xcode and submit it to iTunes Connect. When you host content on Apple’s servers you don’t need to provide any servers—your app’s content is stored by Apple using the same infrastructure that supports other large-scale operations, such as the App Store. Additionally, Apple-hosted content automatically downloads in the background even if your app isn’t running.

在 iOS 6 和以上版本中,大多數(shù)應(yīng)用程序都應(yīng)該使用蘋果托管的內(nèi)容作為下載文件。 你在 Xcode 中的 In-App Purchase Content target(內(nèi)置購買內(nèi)容目標(biāo))來創(chuàng)建一個蘋果托管的內(nèi)容束,并把它遞交到 iTunes Connect 中。當(dāng)你吧內(nèi)容托管到蘋果的服務(wù)器后,你就不需要在提供任何服務(wù)區(qū)---你的應(yīng)用內(nèi)容由蘋果來存儲,它使用相同的支持其他大型經(jīng)營相同的基礎(chǔ)設(shè)施(infrastructure),比如蘋果商店。 另外,蘋果托管的內(nèi)容即使應(yīng)用沒有在運行也能自動在后臺下載。

You might choose to host your own content if you already have server infrastructure, if you need to support older versions of iOS, or if you share your server infrastructure across multiple platforms.

如果你已經(jīng)有服務(wù)器基礎(chǔ)設(shè)施, 如果你需要支持 iOS 老版本,或者如果你跨平臺共享你的服務(wù)器基礎(chǔ)設(shè)施,或許你會選擇自己托管內(nèi)容。

Note: You can’t patch your app binary or download executable code. Your app must contain all executable code needed to support all of its functionality when you submit it. If a new product requires code changes, submit an updated version of your app.

注意:你不能修補你的應(yīng)用的二進制或下載可執(zhí)行代碼。 當(dāng)你遞交時,你的應(yīng)用必須包含支持其所有功能所需的可執(zhí)行代碼。 如果一個新產(chǎn)品要求的代碼發(fā)生了改變,遞交一個應(yīng)用程序的更新版本。

Loading Local Content

1.加載本地內(nèi)容

Load local content using the NSBundle class, just as you load other resources from your app bundle.

使用 NSBundle 類加載本地內(nèi)容,就像你從應(yīng)用束中加載其它資源一樣。

NSURL *url = [[NSBundle mainBundle] URLForResource:@"rocketCar"
                                     withExtension:@"plist"];
[self loadVehicleAtURL:url];

Downloading Hosted Content from Apple’s Server

2.從蘋果服務(wù)器下載托管內(nèi)容

When the user purchases a product that has associated Apple-hosted content, the transaction passed to your transaction queue observer also includes an instance of SKDownload that lets you download the associated content.

當(dāng)用戶購買了跟蘋果托管內(nèi)容相關(guān)的一個產(chǎn)品時,交易被傳遞給交易隊列觀察者同時包含一個 SKDownload 類實例,它讓你下載相關(guān)的內(nèi)容。

To download the content, add the download objects from the transaction’s downloads property to the transaction queue by calling the SKPaymentQueue:method of SKPaymentQueue. If the value of the downloads property is nil, there’s no Apple-hosted content for that transaction. Unlike downloading apps, downloading content doesn’t automatically require a Wi-Fi connection for content larger than a certain size. Avoid using cellular networks to download large files without an explicit action from the user.

要想下載內(nèi)容,通過調(diào)用SKPaymentQueue類的SKPaymentQueue: 方法,從交易的download特性中把下載對象添加到交易隊列。如果 downloads 特性的值為 nil, 就表示該交易沒有蘋果托管內(nèi)容。 不像下載應(yīng)用程序,當(dāng)內(nèi)容超出一個特定大小時,下載內(nèi)容不會自動請求一個 Wi-Fi 連接。如果沒有用戶的明確操作避免使用蜂窩網(wǎng)絡(luò)來下載大文件。

Implement the paymentQueue:updatedDownloads: method on the transaction queue observer to respond to changes in a download’s state—for example, by updating progress in your UI. If a download fails, use the information in its error property to present the error to the user.

在交易隊列觀察者里實現(xiàn)paymentQueue:updatedDownloads: 方法來響應(yīng)下載狀態(tài)的改變---比如,通過在你的 UI 里更新進程。 如果下載失敗,把 error 特性設(shè)置為該失敗信息呈現(xiàn)給用戶。

Ensure that your app handles errors gracefully. For example, if the device runs out of disk space during a download, give the user the option to discard the partial download or to resume the download later when space becomes available.

確保你的應(yīng)用程序能優(yōu)雅地處理 errors。比如,如果設(shè)備下載時磁盤空間不足,讓用戶選擇丟棄本次下載或者在稍后當(dāng)空間充足時再次恢復(fù)下載。

Update your user interface while the content is downloading using the values of the progress and timeRemaining properties. You can use thepauseDownloads:, resumeDownloads:, and cancelDownloads: methods of SKPaymentQueue from your UI to let the user control in-progress downloads. Use the downloadState property to determine whether the download has completed. Don’t use the progress ortimeRemaining property of the download object to check its status—these properties are for updating your UI.

當(dāng)使用 progress 和 timeRemaining 特性值進行下載時,更新你的用戶界面。你可以在你的 UI 中使用 SKPaymentQueue 類的pauseDownloads:, resumeDownloads:, 和 cancelDownloads: 方法來讓用戶控制下載。 使用 [downloadState]()特性來確定下載是否完成。 不要使用 download 對象的 progress timeRemaining特性來檢查它的狀態(tài)---這些狀態(tài)用來更新你的 UI。

Note: Download all Apple-hosted content before finishing the transaction. After a transaction is complete, its download objects can no longer be used.

注意:在交易結(jié)束前下載所有的蘋果托管內(nèi)容。 交易完成后,它的下載對象將不能再使用。

In iOS, your app can manage the downloaded files. The files are saved for you by the Store Kit framework in the Caches directory with the backup flag unset. After the download completes, your app is responsible for moving it to the appropriate location. For content that can be deleted if the device runs out of disk space (and later re-downloaded by your app), leave the files in the Caches directory. Otherwise, move the files to the Documents folder and set the flag to exclude them from user backups.

在 iOS 中,你的應(yīng)用程序可以管理下載的文件。 文件通過商店 Kit 框架被存儲在 Caches 文件夾中,它們都沒有設(shè)置備份標(biāo)記。 下載完成之后,應(yīng)用程序負責(zé)把它們移動到恰當(dāng)?shù)奈恢谩?對于那些可以被刪除的內(nèi)容,比如設(shè)備內(nèi)存不足(并且稍后會由應(yīng)用程序重新下載)的內(nèi)容,則被留在 Caches 文件夾中。否則,把文件移動到 Documents 文件夾并給它們設(shè)置標(biāo)記以防止它們從用戶的備份中丟失。

Listing 4-3 Excluding downloaded content from backups

NSError *error;
BOOL success = [URL setResourceValue:[NSNumber numberWithBool:YES]
                              forKey:NSURLIsExcludedFromBackupKey
                               error:&error];
if (!success) { /* Handle error... */ }

In OS X, the downloaded files are managed by the system; your app can’t move or delete them directly. To locate the content after downloading it, use thecontentURL property of the download object. To locate the file on subsequent launches, use the contentURLForProductID: class method of SKDownload. To delete a file, use the deleteContentForProductID: class method. For information about reading the product identifiers from your app’s receipt, see Receipt Validation Programming Guide.

在 OS X, 下載的文件由系統(tǒng)管理;你的應(yīng)用不能直接移動或刪除它們。 要想在下載完成后定位這些內(nèi)容,使用 download 對象的contentURL 特性。 要想在后續(xù)啟動中定位這些文件,使用 SKDownload類的 contentURLForProductID: 類方法。要想刪除文件,使用 deleteContentForProductID:類方法。 關(guān)于從你的應(yīng)用收據(jù)讀取產(chǎn)品識別碼的更多信息,請看 Receipt Validation Programming Guide.

Downloading Content from Your Own Server

3.從你自己的服務(wù)器下載內(nèi)容

As with all other interactions between your app and your server, the details and mechanics of the process of downloading content from your own server are your responsibility. The communication consists of, at a minimum, the following steps:

正如你的應(yīng)用和服務(wù)器之間的所有其它交互一樣,處理從你自己的服務(wù)器下載內(nèi)容的細節(jié)和過程機制都是你的責(zé)任。該通信至少包括以下步驟:

1.Your app sends the receipt to your server and requests t