Xcode 12.1中SKTestSession内SKProductsRequest请求失败求助
I’ve run into this exact frustrating issue before when testing StoreKit flows with SKTestSession—that vague "Unhandled exception" error almost always traces back to configuration mismatches or timing issues with the test session. Let’s break down the fixes that resolved it for me:
1. Validate Your Configuration Plist File
The most common culprit is a missing or malformed configuration file. Double-check these details:
- Make sure your
Configuration.plistis added to your test target (verify in Xcode’s "Target Membership" inspector) - The file must contain a
productsdictionary that includes every product ID you’re passing toSKProductsRequest - Each product entry needs required fields like
productIdentifier,price, andlocalizedTitle. Here’s a minimal valid example:<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>products</key> <dict> <key>com.yourapp.test.product</key> <dict> <key>productIdentifier</key> <string>com.yourapp.test.product</string> <key>price</key> <string>0.99</string> <key>localizedTitle</key> <string>Test Premium Item</string> </dict> </dict> </dict> </plist>
Even a tiny typo in a product ID will trigger this error—triple-check for case sensitivity or missing prefixes/suffixes.
2. Ensure SKTestSession is Ready Before Making Requests
Timing is critical here. If your buyProduct() function fires before the test session is fully initialized, you’ll hit this unhandled exception. Adjust your test code to:
- Initialize and configure the session at the very start of your test (or in the
setUp()method) - Reset the session state to clear leftover data from previous tests
- Use
XCTestExpectationto properly wait for asynchronous requests (this eliminates flaky tests)
Example test setup:
func testProductRequestSucceeds() throws { // Initialize session FIRST, before any requests let session = try SKTestSession(configurationFileNamed: "Configuration") session.disableDialogs = true session.resetToDefaultState() // Wipe prior session data let expectation = XCTestExpectation(description: "Product request completes") // Test delegate to handle request results class TestRequestDelegate: NSObject, SKProductsRequestDelegate { let completionExpectation: XCTestExpectation init(expectation: XCTestExpectation) { self.completionExpectation = expectation } func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { // Add assertions for product data here (e.g., check product count) XCTAssertFalse(response.products.isEmpty) completionExpectation.fulfill() } func request(_ request: SKRequest, didFailWithError error: Error) { XCTFail("Product request failed unexpectedly: \(error.localizedDescription)") completionExpectation.fulfill() } } let delegate = TestRequestDelegate(expectation: expectation) let request = SKProductsRequest(productIdentifiers: ["com.yourapp.test.product"]) request.delegate = delegate request.start() // Wait for the async operation to finish wait(for: [expectation], timeout: 5.0) }
3. Clean Up Session State Between Tests
If you’re running multiple StoreKit tests, leftover transactions or session state can cause unexpected errors. Add cleanup in your test class’s tearDown() method:
override func tearDown() { super.tearDown() SKTestSession.resetDefaultTestSession() // Reset global session state }
4. Add Extra Session Configuration (If Needed)
In some cases, disabling dialogs alone isn’t enough. Try clearing pending transactions right after initializing the session:
session.clearTransactions() // Remove any leftover transaction data
This can eliminate hidden state that’s triggering the unhandled exception.
内容的提问来源于stack exchange,提问作者Ivan Smirnov

