I'm using Transaction.environment to determine server behavior.
https://developer.apple.com/documentation/storekit/apptransaction/environment
https://developer.apple.com/documentation/storekit/appstore/environment
I gather this information on the app using StoreKit and then send it to the server:
originalTransactionId = transaction.originalID
originalTransactionEnvironment = transaction.environment
When testing within Xcode, on a simulator, the value sent to the server for originalTransactionEnvironment is Xcode - as expected.
When testing on a device using a TestFlight build, the value sent to the server for originalTransactionEnvironment is undefined/nil. I expected it to be Sandbox - and later in production it should be Production.
Most importantly, the value sent to the server for originalTransactionId in the TestFlight version is not undefined/nil - it is the value I expected it would be. The transaction was originally for a subscription purchase, if that makes a difference.
So the transaction is available, and information like originalID is also available. Why is transaction.environment not available? What is the behavior in production?
Post
Replies
Boosts
Views
Activity
The documentation mentions the following:
Verify your receipt first with the production URL; then verify with the sandbox URL if you receive a 21007 status code. This approach ensures you don’t have to switch between URLs while your app is in testing, in review by App Review, or live in the App Store.
This way, you can use one server environment to handle both Sandbox and Production environments. It is necessary to pass App Review.
However, I'm not manually hitting these URLs - I'm using Apple's libraries.
Specifically, the environment is used in SignedDataVerifier and AppStoreServerAPIClient.
(I can't link to these because, for some reason, the domain apple.github.io is not allowed. The documentation for these is only found there. You can find it quickly by searching these terms and the domain.)
Here is how SignedDataVerifier is being used:
const verifier = new SignedDataVerifier(
appleRootCertificates,
APPLE_ENABLE_ONLINE_CHECKS,
APPLE_ENVIRONMENT,
APPLE_BUNDLE_ID,
APPLE_APP_ID
)
const verifiedNotification: ResponseBodyV2DecodedPayload = await verifier.verifyAndDecodeNotification(signedPayload)
if (!verifiedNotification)
{
// Failure
return
}
Here is how AppStoreServerAPIClient is being used:
const appStoreServerAPIClient = new AppStoreServerAPIClient(
SIGNING_KEY,
APPLE_IAP_KEY_ID,
APPLE_IAP_ISSUER_ID,
APPLE_BUNDLE_ID,
APPLE_ENVIRONMENT
)
const statusResponse: StatusResponse = await appStoreServerAPIClient.getAllSubscriptionStatuses(originalTransactionId, [Status.ACTIVE])
In the source code for SignedDataVerifier.verifyAndDecodeNotification, I can see that it throws a VerificationException(VerificationStatus.INVALID_ENVIRONMENT) error .
So for SignedDataVerifier is it as simple as wrapping my code in a try/catch and checking that the error's status code is 21007? I'm unsure about this because if you scroll to the bottom of the linked source code file, you can see the enumeration VerificationStatus, but it's unclear if this member has a value of 21007.
The source code for AppStoreServerAPIClient only says that it throws an APIException if a response could not be processed, so I'm not too sure about how to handle this one.
Background
I have a SwiftData Model specified like this, within a SwiftUI app:
@Model
class MyModel
{
// Model information.
}
It's been working as expected. I can add it to my app's model container as follows:
WindowGroup
{
ContentView()
}
.modelContainer(for: [MyModel.self])
And have been able to create, persist, and fetch data as expected.
Problem
I created a function to delete the persisted data:
func deleteData(modelContext: ModelContext)
{
modelContext.delete(MyModel.self)
}
But the compiler is throwing this error:
Type 'MyModel.Type' cannot conform to 'PersistentModel'.
I'm attempting to do this based of the documentation which specifies that only a model may be provided to delete all instances:
Warning If you don’t provide a predicate, the context will remove all
models of the specified type from the persistent storage.
Other Information
Is this a bug in Xcode or the Swift compiler? It makes no sense at all, for the following reasons.
MyModel is explicitly PersistentModel
I can adjust my model to the following without any errors:
@Model
class MyModel: PersistentModel
{
// Model information.
}
So clearly MyModel does conform to PersistentModel, but it doesn't clear the error.
(Note: There are no compiler errors that pop up, but there actually will be a Preview error that says Redundant conformance of 'MyModel' to protocol 'PersistentModel', which is helpful as more confirmation that it conforms either way.)
.modelContainer(for:) accepts PersistentModel
I'm calling .modelContainer(for: [MyModel.self]) without any issues, and the documentation for this function confirms that it accepts a PersistentModel for parameter.
The documentation for ModelContainer has these initializers:
init(for: Schema, migrationPlan: (any SchemaMigrationPlan.Type)?, configurations: [ModelConfiguration]) throws
convenience init(for: any PersistentModel.Type..., migrationPlan: (any SchemaMigrationPlan.Type)?, configurations: ModelConfiguration...) throws
convenience init(for: Schema, migrationPlan: (any SchemaMigrationPlan.Type)?, configurations: ModelConfiguration...) throws
One of them explicitly accepts any PersistentModel.Type, and the other two accept Schema, whose documentation shows the initializer that I seem to be using, which also accepts any PersistentModel.Type:
init([any PersistentModel.Type], version: Schema.Version)
Conclusion
Overall, it seems like this might be a bug since the class can be explicitly labeled as PersistentModel without errors, and it's already being used with functions that require a PersistentModel parameter.
What can I do about this?
I found this answer from about a year ago, but none of the solutions seem applicable. One of them mentions a bug that was supposedly fixed. I'm on Xcode 15.4.