Xcode 15 beta 8: try await .purchase() consistently throws StoreKitError.Unknown during XCTest on iOS 17 simulator

In my Xcode 15 beta 8 setup, I'm encountering an issue with the iOS 17 simulator where StoreKit.Product.purchase() consistently throws StoreKit Error.Unknown while running XCTest.

  • Inside XCTest, I have declared SKTestSession(configuration: "").
  • I'm using try await StoreKit.Product.purchase(options: []).
  • However, it always throws StoreKit Error.Unknown. There's no such problem with the iOS 16.4 simulator, where I can retrieve the result and handle it appropriately. This issue is only present in the iOS 17 simulator.
  • Is there any necessary workaround or fix for this?
  • I've also included the console log output for your reference:
デフォルト	10:06:45.981812+0900	storekitd	AMSURLRequest: [597e_SK2] Failed to fetch client ID domains from bag. Defaulting to not including analytics cookies. error = { Error domain=AMSErrorDomain, code=204 | URL = http://localhost:XXXXX/inApps/history?REDACTED
Post not yet marked as solved Up vote post of Kazuya_Ito Down vote post of Kazuya_Ito
1k views
  • The issue mentioned above persists even after updating to Xcode Version 15.0 (15A240d).

Add a Comment

Replies

Same here. Any solutions?

I'm having the same problem. Is there any solution?

Have you tried adding session.resetToDefaultState() to the setupError?

    override func setUpWithError() throws {
        session = try SKTestSession(configurationFileNamed: "Configuration")
        session.resetToDefaultState()
        session.clearTransactions()
        session.disableDialogs = true 
    }

However, in some cases (like mine), you may need to add this line for each test block.

I don't know if this issue is caused by my logic or a bug in StoreKitTest.

    override func setUpWithError() throws {
        session = try SKTestSession(configurationFileNamed: "Configuration")
        // Even if I add this in setUpWithError, a test will always return an unexpected error.
        session.resetToDefaultState()
        session.clearTransactions()
        session.disableDialogs = true 
    }

    func testExample() async throws {
        // you'd need to insert this here but it depends on your code
        session.resetToDefaultState()
        session.clearTransactions()
        session.disableDialogs = true
        
        let products = try await Product.products(for: [productID])
        let value = try await products[0].purchase()
        switch value {
        case .success(let verificationResult):
            switch verificationResult {
            case .verified(let signedType):
                XCTAssertEqual(signedType.productID, productID)
                
            case .unverified:
                XCTFail()
            }

        case .userCancelled, .pending:
            XCTFail()
        @unknown default:
            XCTFail()
        }
    }

The WWDC2023 session mentioned using setSimulatedError(nil) to disable the previous setting, but currently, only setting nil didn't work, as far as I know.

Refer to: WWDC2023 Session

Additionally, once you set an error with setSimulatedError, a unit test always returns the error that you set previously (Configuration, as SKSession is shared).

I have (possibly) good news.

SKTestSession.failTransactionsEnabled has been depreacted since iOS17, but it behaves strangely on iOS17.

(lldb) po session.failTransactionsEnabled
false
(lldb) po session.failTransactionsEnabled = false
0 elements
(lldb) po session.failTransactionsEnabled
true

We may work around this issue by not setting a value for this variable.

// before
session.failTransactionsEnabled = false
// after
if #unavailable(iOS 17.0) {
    session.failTransactionsEnabled = false
}

In iOS17 we probably need to use simulatedError(forAPI:).

Any progress here? Still not working as of Xcode 15.2. The 16.4 simulator isn't working for me either. I'm getting:

Error enumerating unfinished transactions: Error Domain=ASDErrorDomain Code=500 "(null)" UserInfo={NSUnderlyingError=0x600000c5b780 {Error Domain=AMSErrorDomain Code=301 "Invalid Status Code" UserInfo={NSLocalizedDescription=Invalid Status Code, AMSURL=http://localhost:50398/inApps/history?REDACTED, AMSStatusCode=400, NSLocalizedFailureReason=The response has an invalid status code}}, storefront-country-code=USA, client-environment-type=XcodeTest(file:///Users/usrname/Library/Developer/XCTestDevices/AACF3F51-FA80-45B6-B9F1-A47574CE548F/data/Containers/Shared/AppGroup/749C8FFE-AF64-4A2D-A041-E19F365027A2/Documents/Persistence/Octane/com.apple.dt.xctest.tool/)}
 Missing transaction data for purchase
 Purchase did not return a transaction: unknown

If I set the simulated error to nil it's not working either. If I use let p = try await session.buyProduct(identifier: products.first!.id) I'm getting:

Failed to complete off-device buy for appstore.xyz. The purchase was successul, but it didn't return the expected transaction JWS.
error Error Domain=SKErrorDomain Code=0 "(null)"

Edit: Looks like it requires a host app. Then it works