### Description
This moves the offline backend integration tests to run on M1 machines
in CircleCI.
Since this causes tests to be much more flaky, this includes a
cherry-pick of the changes in the 5.0 branch that improves those:
https://github.com/RevenueCat/purchases-ios/pull/3902
---------
Co-authored-by: Josh Holtz <me@joshholtz.com>
Co-authored-by: Will Taylor <wtaylor151@gmail.com>
Co-authored-by: Andy Boedo <andresboedo@gmail.com>
Follow up to #3599 and #3600.
These `@MainActor` annotations don't work anymore because of this:
> Main actor-isolated class '...' has different actor isolation from
nonisolated superclass 'TestCase'; this is an error in Swift 6
They were necessary with an older version of Xcode (likely 13.x), but
not anymore.
---------
Co-authored-by: Mark Villacampa <m@markvillacampa.com>
The existing implementation was posting them in sequence, which meant
that it was always going to make N requests.
With this new implementation we can ensure that only one request is made
for all transactions.
This extracts the purchasing code into a new `ExternalPurchasesManager`
and `ObserverModeManager`, which can be used to perform purchases
without using the SDK.
I've also bumped the deployment target of integration tests to 15.0 to
simplify the code.
This is a similar change to #2532.
I noticed yet another source of crashes in our integration tests.
This is what [the
result](https://app.circleci.com/pipelines/github/RevenueCat/purchases-ios/13247/workflows/52fd2014-7a19-442f-a572-64116393abec/jobs/94728)
was:
```
failed - Message 'Finishing transaction' should not have been logged
expected to not find object in collection that satisfies predicate
```
That came from this code:
```swift
self.logger.verifyMessageWasNotLogged("Finishing transaction")
let info1 = try await Purchases.shared.customerInfo()
```
After that assertion failure, `XCTest` continued execution, while it had
already tear down `Purchases`, which lead to this crash:
```
Thread 0 Crashed:
0 libswiftCore.dylib 0x103b84e81 _assertionFailure(_:_:file:line:flags:) + 353
1 RevenueCat 0x1332516d0 static Purchases.shared.getter + 352 (Purchases.swift:65)
2 BackendIntegrationTests 0x130f7048a OfflineStoreKit1IntegrationTests.testPurchaseWhileServerIsDownPostsReceiptWhenForegroundingApp() + 362 (OfflineStoreKitIntegrationTests.swift:289)
3 BackendIntegrationTests 0x130f75ee1 @objc closure #1 in OfflineStoreKit1IntegrationTests.testPurchaseWhileServerIsDownPostsReceiptWhenForegroundingApp() + 1
4 BackendIntegrationTests 0x130f77d91 partial apply for @objc closure #1 in OfflineStoreKit1IntegrationTests.testPurchaseWhileServerIsDownPostsReceiptWhenForegroundingApp() + 1
```
To prevent this, instead of using `Purchases.shared` this replaces that
with a throwing `self.purchases`.
Just like we don't use force-unwraps in tests and instead use
`XCTUwnrap`, this now will fail the test instead of crashing it.
This is the last piece needed after #2612. Transactions handled from
`StoreKit.Transaction.updates` were ignoring the result `CustomerInfo`.
On example where this was noticed is when redeeming promo codes: we
posted the transaction, but `PurchasesDelegate` was not notified of the
change.
I noticed that `BaseBackendIntegrationTests` sets the `StoreKit2Setting`
to `.default`, but in these `StoreKit 1` tests we weren't overriding
that setting.
When the default changes to `StoreKit 2`, this would have unknowingly
changed and we'd have stopped testing `StoreKit 1`.
Turns out this test was only passing because it was waiting for the
receipt to be posted during a renewal, not the initial purchase!
This updates the test to reflect the correct expectation (disabling
renewals), and added the `XCTExpectFailure` so we can detect if it
starts passing in a future iOS version.
Also changed the test to not use `customerInfo()` because of #2563.
With #2069 this file grew up a bit too much.
This splits up `BaseStoreKitIntegrationTests` and
`StoreKitObserverModeIntegrationTests` to make them more maintainable.