I was trying to write unit test of ask to buy for SKDemo app.
Even if I set SKTestSession.askToBuyEnabled = true, I got transaction state as .purchased after I call SKTestSession.buyProduct(identifier:).
import StoreKitTest
import XCTest
final class SKDemoTests: XCTestCase {
private var session: SKTestSession!
override func setUp() async throws {
session = try .init(SKTestSession(configurationFileNamed: "Products"))
session.disableDialogs = true
session.resetToDefaultState()
session.clearTransactions()
}
func test() async throws {
session.askToBuyEnabled = true
try await session.buyProduct(identifier: "consumable.fuel.octane89")
XCTAssertEqual(session.allTransactions().first!.state, .deferred) // Gets error here. The actual state I get is .purchased
}
}
I was using Xcode 15.0.1 (15A507), Simulator iPhone 15 Pro iOS 17.0.1 (21A342)
I couldn't find the problem so I'm happy to hear any solutions.
Post
Replies
Boosts
Views
Activity
I am trying to test Ask to Buy for consumable product.
When the transaction was approved, I can get verified transaction in Transaction.updates, meaning the process stops at a breakpoint in Transaction.updates. However, when I call Transaction.currentEntitlements after that, I cannot handle transaction as consumable.
Am I missing anything?
Here is my code.
func newTransactionListenerTask() -> Task<Void, Never> {
Task(priority: .background) {
for await verificationResult in Transaction.updates {
guard
case .verified(let transaction) = verificationResult,
transaction.revocationDate == nil
else { return }
// Breakpoint stops here
await refreshPurchasedProducts()
await transaction.finish()
}
}
}
func refreshPurchasedProducts() async {
for await verificationResult in Transaction.currentEntitlements {
guard case .verified(let transaction) = verificationResult else { return }
switch transaction.productType {
case .consumable:
// This code is not called
updateConsumablePurchaseCount(productID: transaction.productID)
default:
break
}
}
}
(This is the duplicate of https://developer.apple.com/forums/thread/718948 because I accidentally marked it as completed.)
I am trying to test Ask to Buy for consumable product.
When the transaction was approved, the process stops at a breakpoint in Transaction.updates. However, when I call Transaction.currentEntitlements after that, I cannot handle transaction of consumables.
Here is my code.
func newTransactionListenerTask() -> Task<Void, Never> {
Task(priority: .background) {
for await verificationResult in Transaction.updates {
guard
case .verified(let transaction) = verificationResult,
transaction.revocationDate == nil
else { return }
// Breakpoint stops here
await refreshPurchasedProducts()
await transaction.finish()
}
}
}
func refreshPurchasedProducts() async {
for await verificationResult in Transaction.currentEntitlements {
guard case .verified(let transaction) = verificationResult else { return }
switch transaction.productType {
case .consumable:
// This code is not called
updateConsumablePurchaseCount(productID: transaction.productID)
default:
break
}
}
}
I do not call transaction.finish() if the purchase result is .pending as shown in the code below.
func purchase(_ info: ConsumableProductInfo) async throws {
let result: Product.PurchaseResult
switch info {
case .coffee:
guard let coffeeProduct else { return }
result = try await coffeeProduct.purchase()
}
switch result {
case let .success(verificationResult):
switch verificationResult {
case let .verified(transaction):
await refreshPurchasedProducts()
await transaction.finish()
case let .unverified(_, verificationError):
throw verificationError
}
case .pending, .userCancelled:
break
@unknown default:
break
}
}
When I test Ask to Buy I'm not calling transaction.finish() so I should be able to get the consumable transaction in Transaction.currentEntitlements, however I couldn't.
I found a blog post that is similar to this problem: https://iosexample.com/implementing-and-testing-in-app-purchases-with-storekit2-in-xcode-13-swift-5-5-and-ios-15/
It says that "In tests I've done transactions for consumables do not remain in the receipt, even if you omit to call finish()."
So I wonder if this is a bug or problem in my coding.