在 Xcode 12 使用 StoreKit 測試 App 內購 讓你加速開發進度


雖然 WWDC20 結束至今已經過了五個月,我們還在討論今年發佈的新框架、API、以及改善。在這麼多新功能中,有一些會對我們實作 App 內購 (In-App Purchase) 時有重大影響,那就是我們可以在 Xcode 12 中本地測試 StoreKit 的新功能。

現在,我們必須停止原本的開發流程,並到 App Store Connect 才能夠創建必要的 App 內購紀錄,以及最少一個的沙盒 (sandbox) 使用者,以作測試用途。完成我們前一篇教學文章的步驟之後,開發者就可以繼續撰寫 App 內購的相關程式碼。很明顯地,這麻煩而無可避免的步驟其實已經減慢了開發進度,更何況在後續的測試過程中,還需要多次訪問 App Store Connect,來創建更多的測試使用者。

不過好消息是,在 Xcode 12 及 StoreKit 本地測試的功能出現之後,再也不會出現這減慢開發進度的情況了。我們不僅不再需要為了訪問 App Store Connect 而停下原本的開發工作,而且只需要透過 Xcode 及模擬器,就能夠快速地實作並測試 App 內購功能。這樣一來,開發者就能夠擺脫 App 內購紀錄與測試使用者的擔憂,而專注於程式碼實作的部分,讓 App 內購功能更加完美。StoreKit 本地測試的另一個好處,是它可以離線運作,因此就算在沒有網路的環境之下,我們也能夠繼續開發及測試 App 內購功能。

在文章之後的部分,你將會了解到使用 StoreKit 進行本地端測試的優點,更令人驚訝的是,它可以讓簡化 App 內購功能的整合、並加速測試實作的邏輯及使用者介面。

在繼續進行下一部分之前,還有一件你必須知道的事情:現在有一個新的 StoreKitTest 框架,能夠允許撰寫單元及使用者介面測試,還能夠自動化 App 內購的測試過程。你是不是等不及想要快點一探究竟了呢?

關於範例 App

如同我們其他教學一樣,我們準備了範例專案。不過,與平常不同的是,我們這次幾乎不會撰寫程式碼,只在最後寫一些單元測試的程式碼。我們會專注於使用 StoreKit 進行本地端測試時必要的設置檔案,以及 Xcode 內用以測試 App 內購不同狀態及條件的可用選項。

本篇文章的範例專案是一個 SwiftUI App,它非常簡單,透過列表展示三個可以購買的食譜,購買後才可以解鎖食譜。雖然實際上在購買後不會發生任何事,不過那也不是我們關心的部分,我們只專注於在不同情況下購買時的 App 行為。

storekit-testing-sample-app

我們以一個 JSON 檔案 Recipes.json,來描述這三個食譜(App 內購的產品),與其他的專案檔案一樣,你可以在專案導覽列中找到它。以程式設計的角度而言,每個食譜是由 RecipeInfo.swift 檔案中 RecipeInfo 類別的實例來呈現的,然後所有食譜會由 RecipesModel 類別來處理,RecipesModel 類別裡面包含了 recipes 陣列(RecipeInfo 物件的集合),同時也負責讀取 JSON 檔案及解碼食譜資料。

RecipesModel 類別也負責觸發 App 內購的動作,例如載入 App 內購的產品及發起購買。請注意,所有 App 內購的相關程式碼都是在 IAPManager.swift 檔案的 IAPManager 類別中實作,我們在之前關於 App 內購的文章中,一步一步介紹並實作過這個類別,你也可以閱讀那篇文章作參考。

IAPManager 類別的要求,是所有可用於 App 內購的產品識別碼,都應該存放在名為 IAP_ProductIDs.plist屬性列表 (.plist) 檔案中。你也會在專案導覽列中找到它,打開它就可以查看我們將測試的三個產品所對應的識別碼。

在結束本章節之前,我要先作出一項重要聲明:

為了適當地在 Xcode 中測試 StoreKit,並充分使用它,我推薦大家使用本地收據驗證的方式。但在本篇文章中,我們不會使用這種方式來進行,本地收據驗證是一個漫長的討論,因此我們在這裡會跳過這個話題。我們會以不需要驗證 Xcode 創造的假收據為前提,盡力做到最好,但是針對某些主題(例如訂閱),我們不會做太深入的討論。我們會專注在配置本地端測試的所須步驟,但實際上不會做任何測試。假如你已經實作了自己的本地收據驗證機制,可以參考我後續提供連結中的指南來進行操作。為了讓 UI 與此專案的購買狀態收據一致,我們會使用 User Defaults 字典,來標註食譜是否已經被購買。

除此之外,在看完這麼長的描述之後,你可以花點時間看看我們的範例專案,也可以參考之前的教學文章,來瞭解我們如何在這個專案中使用 IAPManager 類別。準備好之後,就可以繼續閱讀,一起看看如何在 Xcode 之中使用 StoreKit 進行本地端測試吧!

準備在本地端測試 App 內購

為了要在 Xcode 裡測試本地端的 App 內購,我們需要執行幾個標準動作。首先,我們要建立一個含有 App 內購紀錄的配置檔案,來模擬 App Store。根據 App 所提供的內購類型,同一個 App 內可以有多個配置檔案的情況。舉例來說,一個可以用來測試非消耗性的內購項目,另一個用來測試訂閱服務。然而,請記住在同一時間只能激活並使用其中一個。

為了要創建配置檔案,在 Xcode 打開 File > New > File… 選單,或是直接在鍵盤按下 Cmd+N。彈出的視窗會出現所有可用的檔案模板,滾動到下方的 Other 部分,你會看到 StoreKit Configuration File,或是在搜尋列輸入 “storekit” 直接找到它。

img

點選 Next 並為檔案命名,我們會先在這裡測試非消耗性的產品,因此將它命名為 NonConsumables,然後點擊 Create。你也可以任意為它命名,只是命名為 NonConsumables 能夠清楚地指出配置檔案的內容。

檔案出現在專案導覽列之後,如果它沒有自動打開,你可以點擊它來打開。目前它的內容是空的,讓我們之後加入 App 內購或是訂閱服務。我們先進行 App 內購的部份,點擊視窗左下角的加號 (+) 按鈕。

img

接著會出現一個內容選單,提供了可以加入的選項:

  1. A consumable in-app purchase (一個消耗性的 App 內購項目)
  2. A non-consumable in-app purchase(一個非消耗性的 App 內購項目)
  3. An auto-renewable subscription(一個自動更新的訂閱服務)

我們想測試非消耗性的 App 內購,因此點擊選單中的第二個選項,你會看到這樣的畫面:

adding-storekit-xcode-12

Reference Name 應該包含 App 內購的簡短名字,以我們的範例專案來說,範例食譜是沙拉、義大利麵和蛋糕。讓我們先以第一個內購項目為例,在欄位中輸入 Salad

Product ID 就是產品識別碼,我們也會在 App Store Connect 中提供它。如同我先前所說,你可以在 IAP_ProductIDs.plist 檔案中,找到所有 App 內使用的產品識別碼。在這裡,我們使用 com.appcoda.storekitlocaldemo.salad 取代原本的預設值 “com.temporary.id”。

下一個欄位是 App 內購的 Price,雖然在 App Store Connect 有價格等級,但是在這裡我們還是可以以文字提供任意數值。這個價格單純是為了作測試,它並不會是實際的 App 內購價格,當然也不會有收費的動作,所以可以放心地設定任何價格。對於我們正在設定的 App 內購項目,設定價格為 0.99 即可,而價格的貨幣會因應模擬器設定的地區而調整,或是你也可以手動設定任何地區(稍後我們會討論這一點)。所以 0.99 的單位可以是美元、歐元或是日圓等等。

在剛剛提及的三個欄位下方,有一個選項可以讓你測試家庭共享功能,不過這不在本次的討論範圍內,你可以直接將它取消勾選即可。

最後,來到 Localizations 的部分。像在 App Store Connect 一樣,我們需要在這裡為 App 內購提供顯示名稱與描述。點選預設的 Localization 欄位,視窗會跳出表單,讓我們將下列數值填入:

  • Display name: Ceasars Salad
  • Description: The most popular salad everybody loves!

現在,App 內購的配置看起來會像是這樣:

storekit-in-app-purchase

上述步驟適用於只有一個 App 內購項目的情況,然而,我們的範例專案中有三個食譜作為 App 內購項目,因此讓我們以同樣的方式設定其他兩個項目。

依照上述所說的步驟,按下面的設定,加入並設置另外兩個非消耗性的 App 內購項目:

1. 義大利麵的 App 內購項目:

  • Reference name: Spaghetti
  • Product ID: com.appcoda.storekitlocaldemo.spaghetti
  • Price: 1.19
  • Localizations > Display Name: Spaghetti & Meatballs
  • Localizations > Description: A tasty meal that makes you happy!

2. 蛋糕的 App 內購項目:

  • Reference name: Cake
  • Product ID: com.appcoda.storekitlocaldemo.cake
  • Price: 1.49
  • Localizations > Display Name: Chocolate Cake
  • Localizations > Description: The most delicious dessert ever!

完成之後,你應該可以看到配置檔案內有 3 個 App 內購項目:

storekit-in-app-purchase-1

如果你有不想使用的 App 內購項目,可以點選它們(使用 Ctrl 鍵可以點選多個項目),然後點擊視窗左下角的減號 (-) 按鈕來刪除。

最後,完成 App 內購的配置後,按下 Cmd+S 來儲存變更。

使用 StoreKit 的配置檔案

創建了 NonConsumables.storekit 檔案並設定好之後,下一步就要告訴我們的 App,我們要使用它而不是 App Store。為了達到這個目的,在 Xcode 工具列點擊 StoreKitLocalDemo 方案,並選擇 Edit Scheme

xcode-edit-scheme

首先,在左側欄位選取 Run,接著在主視窗選擇 Options 頁籤。你會看到下方有一個叫做 StoreKit Configuration 的選項,目前的值應該是 None。點擊彈出按鈕,你應該可以找到剛剛創建的 NonConsumables.storekit 檔案,選擇它之後即可關閉編輯視窗。

img

請記住,其他 .storekit 檔案都會像 NonConsumables.storekit 檔案一樣,在這裡一起被列出,但就如你所見,我們每次只能選擇一個來使用。

測試 App 內購

現在,我們可以開始進行第一次的 App 內購本地端測試。請確認你有跟著我們上面兩個段落的所有步驟操作,然後在模擬器中執行 App。你會看到列出了三個範例食譜,而每個食譜旁邊都有一個鎖頭圖示,表示它們讓未被購買。

點擊列表中第一個(或是其它)食譜,會跳出一個提示訊息,顯示 App 建議你購買這個食譜。有趣的是,這個訊息裡包含的名稱、描述以及價錢,正是我們剛剛在 NonConsumables.storekit 檔案中所定義的。如果你在 storekit 檔案中改變這些值,你將會在這裡看到對應的改變。

在跳出的提示訊息中,點擊 Get recipe for XXX 按鈕,就可以選擇購買食譜。為了模擬購買體驗,出現在 App Store 正常購買流程的系統介面也會在這裡出現,而你只需要點擊 Confirm 按鈕來繼續交易。當然這裡並不會被索取任何費用,介面中 “Details” 欄位中也會特別註明是為了測試用途。

在確認購買之後,你將會看到系統提示訊息,顯示交易已經成功。在關閉提示訊息後,原本在食譜旁邊的鎖頭圖示也會消失,那就代表範例專案中的食譜已經被購買。

storekit-in-app-purchase-testing

交易管理器 (Transactions Manager)

像我們剛剛所創建的交易一樣,所有使用 storekit 檔案創建的交易,都可以在 Xcode 裡的特別視窗中看到,這就是交易管理器。你可以到 Debug > StoreKit > Manage Transactions… 選單中顯示交易管理器,或是在 App 執行時,點擊 Xcode 狀態列上的 Manage StoreKit Transactions 按鈕:

storekit-transactions-manager

當交易管理員的視窗出現之後,你會看到之前所有的交易。

storekit-in-app-purchase

本地端測試的好處,就是我們可以刪除到目前為止的任何交易,並重零開始測試 App 內購。如果是使用 App Store,要達到相同的目標,我們就每次都需要創建新的測試使用者,才能重新進行測試。

現在來實際操作看看,在交易管理器視窗中選擇你看到的交易,並點擊垃圾桶圖示按鈕,或是右鍵點擊後選擇 Delete Transaction 來刪除它。

img

再次執行 App,在重新購買同樣的食譜之前,請先確認你有點擊重新整理的按鈕,讓在 User Defaults 字典裡的食譜重新被標記為未購買的狀態,並且更新相對應的 UI。如此一來,你就會再次看到鎖頭圖示,點擊購買同一個食譜,就會執行與之前相同的步驟。只透過幾次點擊,我們就能將 App 回復到原本的狀態,像什麼都沒有發生過一樣,重新測試 App 內購。

再次回到交易管理器當中,你會看到一個新的交易被列出,不過這次帶有不同的識別碼。除了可以將交易刪除之外,你還可以在工作列中點擊相應的按鈕或是右鍵點擊來進行退款。我們現在就可以來試試,不過範例專案的 UI 並不會有任何更新,如同我們一開始所說,我們將不會進行任何的收據驗證,而退款只能透過收據來進行確認。但是如果你自己有本地收據驗證,你可以很輕易地在收據中確認該交易是否被標記為取消,並相對應地在範例專案或是其他 App 中更新 UI。

測試購買中斷的情況

上述我們所測試的情境,是大家希望在 App 會發生的情境,但是事情不是每次都會如同我們所想的那麼順利,所以我們也需要測試某些異常情況的處理,這樣我們稱它為購買中斷的狀況,也就是說因為使用者必須在 App 外執行動作,而無法完成購買。舉例來說,使用者可能需要更新 App Store 的付款資訊才能完成購買。

為了測試購買中斷的情況,首先在專案導覽列點擊 NonConsumables.storekit 檔案,並到選單中點擊 Editor > Enable Interrupted Purchases。如此一來,現在開始進行的所有購買都會被視為中斷來處理,因此測試完成之後,請記得從選單中點擊 Editor > Disable Interrupted Purchases 來切換回正常狀態。

範例專案提供了一個包含 App 內購相關實作的 IAPManager 類別,這個類別會在購買中斷的情況發生時回傳錯誤。這個錯誤會是 SKErrorDomain with value 0,也就代表著是一個未知錯誤。通常我們都只想通知使用者交易無法完成,這也是範例專案中會發生的情況。當然,你也可以在自己的 App 中用另一個方式,更好地處理錯誤。

開啟交易中斷的功能後,我們可以執行 App 並試著購買一個未購買的食譜。假如你都已經購買了所有食譜,可以開啟交易管理器將紀錄刪除,並點擊重新整理的按鈕來更新 UI。接著開始購買食譜的流程,讓我們看看確認購買後系統 UI 會發生什麼事。你會看到一則警示訊息跳出,告知你交易無法完成,同時你也可以在控制台中看到錯誤訊息被印出。

interrupted-inapp-purchase

在我們正在測試的環境中,我們可以解決導致交易中斷的問題,並看看 App 的行為。現在回到 Xcode 打開交易管理器視窗,你會找到剛剛的交易標記為 Failed。接著,選擇它並使用工具列中的 Resolve Issues 按鈕,或右鍵點擊它,然後在選單中選擇 Resolve Issues 選項。

img

這個假的問題將會被解決,交易也會恢復到正常。如果回到 App 當中,你會看到購買已經完成。同時,交易管理器中也會出現一個新的交易紀錄,代表被選擇的食譜已經標註為被購買。

img

測試詢問購買 (Ask To Buy)

當你在 Xcode 選擇 NonConsumables.storekit 檔案的時候,你可以在 Editor 選單中找到一個 Enable Ask to Buy 的選項。如果啟用了這個功能,就可以測試未成年使用者的裝置在 App 中啟動購買過程的情況。

為了試試實際操作,請先在專案導覽列中打開 NonConsumables.storekit 檔案,然後到選單中選擇 Editor > Enable Ask to Buy

備註:記得要先關掉上一段落的中斷購買功能。

再次啟動 App 並開始購買食譜的流程,在確認購買後,你應該會收到以下的系統提示:

inapp-purchase-test-ask

選擇 Ask 選項後,你應該會在控制台看到一個詳細的錯誤訊息,說明付款已經完成但有錯誤。等到發起購買的未成年使用者的父母或是監護人同意或拒絕該購買,我們才需要更新 UI。然而,我們必須確認購買獲得同意後,使用者就可以獲取食譜。

測試可以透過 Xcode 的交易管理器繼續進行,再次打開交易管理器,你會看到剛剛的交易被標註為 Pending Approval

img

選擇該交易後,使用工具列或是右鍵點擊它並選擇 Approve Transaction 來同意這筆交易。切換回模擬器中正在執行的 App,你會看到該食譜已經是解鎖的狀態。

你可以在這裡瞭解更多關於詢問購買功能的細節。

變更商店介面

在 Xcode 12 中測試本地 App 內購時,我們可以變更預設的商店介面,並確保 App 可以使用正確的格式與貨幣來顯示商品的價格。IAPManager 類別中的 getPriceFormatted(for:) 方法,就是負責在範例專案中以正確的格式顯示價格。因為我們已經在 RecipesView 使用過它,因此我們只需要測試它是否能如預期般運作即可。記得之前我們在 storekit 檔案中設置 App 內購時,只有設定價格的數值,而沒有設定貨幣單位嗎?顯示的貨幣就取決於商店介面。

回到 Xcode 打開 NonConsumables.storekit 檔案,然後到選單中 Editor > Default Storefront > Greece 的選項,這將會模擬希臘 App store 的使用情境,而現在當我們購買食譜時,價格後面就會出現歐元的符號,而非原本的美元符號。

在完成變更之後,執行 App 並發起一個新的購買,這次在價格旁邊,你會看到歐元貨幣的符號。

img

變更預設的本地化設置 (Localization)

就如預設的商店介面一樣,你也可以變更預設的本地化設定,這會改變題示的產品數據資料。首先,打開 NonConsumables.storekit 檔案,到 Editor > Default Localization > Greek 選單(或是任何其他想要的本地化選項)。

不過,單單改變預設的本地化設置並不足夠,我們必須在配置檔案中提供對應的顯示名稱與描述,以用於本地化的設置檔案中。讓我們先對沙拉的購買項目來做設定。

備註:假如你沒有提供本地化顯示名稱與產品描述,就直接執行 App 的話,在購買提示訊息中就不會出現標題以及產品描述。

NonConsumables.storekit 檔案中選擇沙拉的購買項目,點擊 Localizations 部分中的加號 (+) 按鈕。在本地化的設置視窗當中,首先選擇希臘(或是其他想要選擇的地區),然後使用你想要的語言提供本地化的顯示名稱與產品描述,或是直接使用我們的範例,以希臘文為例就是這樣:

  • Display name: Σαλάτα του Καίσαρα
  • Description: Η διάσημη σαλάτα που όλοι λατρεύουν!

點擊完成按鈕,新的本地化設置就會出現在 Localizations 清單當中。

img

現在執行 App 並發起購買程序,你就會看到出現的提示訊息,已經翻譯成我們所選擇的本地化語言,同時價格的貨幣也與本地化設置相符。

storekit-localization

當然,在真正的 App 當中,UI 也全部都需要翻譯。不過我們這裡只專注討論 App 內購的部份,所以不用在意 UI 的語言。

建立一個配置檔案來測試訂閱功能

因為訂閱功課需要收據驗證才能測試,所以我們不會在這篇文章測試訂閱功能,但我們還是會簡單地介紹如何創建包含訂閱紀錄的 storekit 配置檔案。

備註:請記得要在 StoreKit 設定視窗(Run 選項中的 Options 頁籤)中編輯 App 的方案,並選擇想使用的 storekit 配置檔案來做測試。

現在來到 File > New > File… 選單,搜尋 “storekit” 檔案模板,選擇它並取名為 Subscriptions。看到 Subscriptions.storekit 檔案出現在左側的專案導覽列後,點擊將它打開。

我們需要先在配置檔案中,加入一個可以自動更新的訂閱紀錄。點擊視窗左下角的加號 (+) 按鈕,並從選單中選擇 Add Auto-Renewable Subscription

因為這是我們第一個加入的訂閱,你會看到視窗跳出訊息要求你創建一個訂閱群組,一個群組可以包含多個訂閱,但是使用者每次只能訂閱其中一個,但是隨時都可以在同一組訂閱之中做切換。

img

就算只有一種訂閱,你還是要在這裡創建一個群組。讓我們把群組命名為 MySubscriptions,你也可以任意命名,完成後點擊 Done 來繼續。

如你所看到的訂閱配置,有幾個欄位可以讓我們來提供數值,要提供哪些數值,就取決於我們想測試什麼。

img

Auto-Renewable Subscription 中的第一部分,與我們先前在 NonConsumables.storekit 檔案中所填寫的部分非常相似。每個訂閱都應該會有產品名稱、識別碼以及更新訂閱時使用者需要支付的價格。此外,這裡還有一個 Subscription Duration 欄位,讓我們指定要測試的訂閱期限。

img

再下面是 Introductory Offer 迎新優惠的部分。如果你想為使用者提供一次性的迎新優惠,並加以測試,就可以在這裡做設定。要設置迎新優惠,先點擊 Offer Type 彈出按鈕,從以下三項訂閱種類中挑選一個(除了 None 以外):

  • Pay as you go
  • Pay up front
  • Free

不論你選擇哪一個訂閱種類,都需要指定優惠的有效期限。而如果選擇前兩個種類,就還需要提供優惠的價格。

img

接著在測試迎新優惠時,提供的價格(或是免費)會在系統表單的右上角顯示,以便確認付款。請記住,任何交易都會在交易管理器視窗中被列出,假如你想要重新測試優惠,就可以從那裡剛除舊的交易紀錄。

inapp-purchase-demo-storekit

下一步,我們要測試訂閱是否會在迎新優惠結束時正確地作更新。回到 Xcode 的 Subscriptions.storekit 檔案,到 Editor > Time Rate 選單中選擇你想要的時速,一般來說如果你想要快速做測試,「一秒一天」這種時速就差不多了。一旦訂閱期限到期,我們就可以切換回到慢一點的時速,並查看食譜的相關欄位,來測試 App 是否有正確地更新訂閱。

除了可以測試迎新優惠之外,還有一個 Promotional Offers 促銷優惠的部分,你可以在這裡定義想要對每個訂閱的優惠,你可以在訂閱過期的時候顯示優惠,又或是自己按特定條件選出最佳的時機來顯示優惠。

要創建促銷優惠,先點擊加號 (+) 按鈕,並填入必要的數值。在顯示名稱與產品代碼中輸入自訂的數值,並選擇提供優惠種類、持續期限、和價格(若不是免費的話)。同樣地,你可以使用 Editor > Time Rate 選單,來加速或減慢測試時間。

img

最後,別忘記為預設本地化設定名稱與描述,這將會在使用者購買訂閱時顯示,這與我們先前在 storekit 配置檔案中的本地化數值非常相似。

img

請注意,對於任何即將加入到配置檔案中的新訂閱,你都可以選擇將它加入到現有的訂閱群組中,或是創建一個新的群組來另外儲存。

img

我建議你可以參考這篇指南,來瞭解測試訂閱的更多細節。

撰寫單元測試

除了上述在本地 App 內購測試的手動工作之外,我們還可以利用一個叫做 StoreKitTest 的新框架來創建單元與 UI 測試。這個框架由 SKTestSession 類別提供,這個類別還提供了多種方法與屬性,來幫助我們設置測試條件及執行模擬交易。

我們會在這邊撰寫幾個單元測試,來試試 StoreKitTest 框架。先打開 StoreKitLocalDemoTests.swift 檔案,我們將會在這裡撰寫單元測試。接著來到檔案的最上方,在 import XCTest 這行後面匯入 StoreKitTest 框架:

import StoreKitTest

我們的第一個測試先做點簡單的東西,我們會進行購買,並且確保購買能完成。由於這是一個測試,所以環境中不會出錯,但還是一個很好的機會,利用新的 API 來驗證 App 內購程式碼是否能正確運作。

讓我們先定義以下方法:

func testSaladPurchase() throws {

}

一開始,我們先創建一個 SKTestSession 實例,之後用來測試 App 內購:

func testSaladPurchase() throws {
    let session = try SKTestSession(configurationFileNamed: "NonConsumables")
}

可以看到在初始化時,我們提供了想要使用的 storekit 配置檔案名稱。

接下來,為了自動化所有流程,讓我們在下一行取消任何系統對話與 UI:

func testSaladPurchase() throws {
    ...
    session.disableDialogs = true
}

最後,我們將會透過 session 物件,來使用 SKTestSession 裡的 buyProduct(productIdentifier:) 方法進行購買。這是一個會發出例外的方法,也因為這是方法裡的最後一行,我們會在 XCTAssertNoThrow(_:) 方法裡的參數呼叫它:

func testSaladPurchase() throws {
    ...
    XCTAssertNoThrow(try session.buyProduct(productIdentifier: "com.appcoda.storekitlocaldemo.salad"))
}

請注意,我們必須將產品識別碼對應到想要測試的 App 內購項目。完整方法如下:

func testSaladPurchase() throws {
    let session = try SKTestSession(configurationFileNamed: "NonConsumables")
    session.disableDialogs = true
    XCTAssertNoThrow(try session.buyProduct(productIdentifier: "com.appcoda.storekitlocaldemo.salad"))
}

現在讓我們執行測試看看。點擊方法名稱左側的小型播放按鈕,並等待測試結束,它應該會是成功的,如果不成功,請確認是否有跟著上述所有的步驟來操作。

img

為了查看我們剛剛的購買,先按下 Cmd+R 來執行 App,接著打開交易管理器,交易應該會在這裡被列出。

img

我們也可以在每次執行測試前,把先前的交易都刪除掉,在初始化 session 後加入下列這行程式碼:

session.clearTransactions()

讓我們進行第二個測試。這次我們會用新的測試方法,來測試詢問購買的情況:

func testAskToBuy() throws {

}

跟剛剛一樣,我們會先初始化一個測試 session 物件,並做一些基礎設定:

let session = try SKTestSession(configurationFileNamed: "NonConsumables")
session.clearTransactions()
session.disableDialogs = true

為了啟用詢問購買的功能,我們只需要將下列旗標設為 True:

session.askToBuyEnabled = true

現在我們就可以跟之前一樣,購買某些 App 內購項目:

XCTAssertNoThrow(try session.buyProduct(productIdentifier: "com.appcoda.storekitlocaldemo.salad"))

如果以上的判定 (assertion) 是成功,那麼一個新的交易將會被創建,並且等待購買程序被批准。在這個情況中,被創建的交易會維持在 deferred 的狀態中,只有在它獲得批准或拒絕後,才會完成交易流程。為了測試它,我們在後面加入兩個判定。

XCTAssertTrue(session.allTransactions().count == 1)
XCTAssertTrue(session.allTransactions()[0].state == .deferred)

完整方法如下:

func testAskToBuy() throws {
    let session = try SKTestSession(configurationFileNamed: "NonConsumables")
    session.clearTransactions()
    session.disableDialogs = true
    session.askToBuyEnabled = true

    XCTAssertNoThrow(try session.buyProduct(productIdentifier: "com.appcoda.storekitlocaldemo.salad"))

    XCTAssertTrue(session.allTransactions().count == 1)
    XCTAssertTrue(session.allTransactions()[0].state == .deferred)
}

執行這個測試,測試標註為成功後,執行 App 並打開交易管理器,你會找到一個狀態為 Pending Approval 的 App 內購:

img

讓我們再加入最後一個判定到剛剛的測試方法之中,使購買能夠被批准:

func testAskToBuy() throws {
    ...

    XCTAssertNoThrow(try session.approveAskToBuyTransaction(identifier: session.allTransactions()[0].identifier))
}

approveAskToBuyTransaction(identifier:) 方法所需要的識別碼,就是在交易管理器中所顯示的交易識別碼。

再次執行測試,執行 App 並打開交易管理器。這一次,我們可以看到交易已經被標註為 Purchased,因為它已經被剛剛加入的最後一行程式碼所批准。

在剛剛的兩種測試情境當中,我們使用了許多 SKTestSession 類別裡的方法與屬性。我鼓勵你可以閱讀這篇文章,了解更多這個類別與所有可用的 API 的細節,讓你就可以創建不同種類的測試,涵蓋處理 App 內購的所有情況。

總結

當 App 的實作涉及到 App 內購功能時,在本地端測試 StoreKit 可以帶來很大的改變。它可以幫助你節省大量時間,讓你更專注於重要的部分,像是讓 App 提供 App 內購時可以正常運作。當然。這不代表我們完全不需要對 App Store Connect 真實的內購紀錄做測試。相反地,那會是在我們完成本地測試後,確保我們的 App 可以正常運作的下一步。

無論如何,我希望你可以在本篇教學中,找到一些有用又有趣的內容。我也推薦你們要觀看 WWDC20 的 Introducing StoreKit Testing in Xcode 片段。享受使用 StoreKit 來測試的過程吧!

你可以在這裡下載完整的專案作參考。

譯者簡介:HengJay,iOS 初學者,閒暇之餘習慣透過線上 MOOC 資源學習新的技術,喜歡 Swift 平易近人的語法也喜歡狗狗,目前參與生醫領域相關應用的 App 開發,希望分享文章的同時也能持續精進自己的基礎。

LinkedIn: https://www.linkedin.com/in/hengjiewang/
Facebook: https://www.facebook.com/hengjie.wang

原文Testing In-App Purchases Using StoreKit in Xcode 12


資深軟體開發員,從事相關工作超過二十年,專門在不同的平台和各種程式語言去解決軟體開發問題。自2010年中,Gabriel專注在iOS程式的開發,利用教程與世界上每個角落的人分享知識。可以在Google+或推特關注 Gabriel。

blog comments powered by Disqus
Shares
Share This