* Use offerings cache on (most) network errors
* Add unit tests for `NetworkError`'s `shouldFallBackToCachedOfferings`
* Add unit tests for `shouldFallBackToCachedOfferings`
* Fix lint
* Add some more unit tests
* Add `isLoadShedderResponse` and `isFallbackURLResponse` properties to `VerifiedHTTPResponse`
Keeps the values also when returned by the ETagManager
* lint
* Add some tests for ETagManager
* Rename `isFallbackURLResponse` to `isFallbackUrlResponse` to prevent decoding issues
* Add tests for actual source of original cached response of ETagManager
* Add tests to HTTPClientTests regarding the new booleans
* lint
* Add `originalSource` to `CustomerInfo`
* Adds `isLoadedFromCache` boolean to CustomerInfo
* Add docs
* Unit tests for new customer info properties
* Make `OriginalSource.CustomerInfo` a `String` enum
* Add decoding tests for Customer Info with new original source field
* Add `.loadShedder` check for Customer Info obtained in Load Shedder integration tests
* Update fixture
* improve internal docs
* Add intermediate object `Offerings.Contents` to wrap `OfferingsResponse` + `OriginalSource` and `loadedFromCache`
* Adapt existing unit tests to changes in `Offerings` underlying objects
* Check Offerings original source in existing integration tests
* Fix StoreKit unit tests
* Fix RevenueCatUI tests
* More Offerings.Contents tests
* Fix test
* Add more tests
* Fix lint
* [skip ci] Generating new test snapshots (#5741)
* [skip ci] Generating new test snapshots (#5742)
* [skip ci] Generating new test snapshots (#5743)
* [skip ci] Generating new test snapshots (#5744)
* [skip ci] Generating new test snapshots (#5745)
* [skip ci] Generating new test snapshots (#5746)
* [skip ci] Generating new test snapshots (#5747)
* [skip ci] Generating new test snapshots (#5748)
* Use enum for `HTTPResponseSource`
* Update tests
* Adapt to changes in `VerifiedHTTPResponse`
* Lint
* Adapt to changes in `VerifiedHTTPResponse`
* update tests
* Add log when a request to the fallback URL was handled by load shedder (which should never happen)
* rename `HTTPResponseSource` to `HTTPResponseOriginalSource`
* Adapt to changes
* fix lint
* Adapt to changes
* Fix CustomerInfo's OriginalSource implementation + fix unit test
* Fix tests compilation
* Add Cached offerings integration test
* Fix RCUI compilation
* Move isFromCache boolean from `Offerings.Contents` to `Offerings`
* Expand `ForceServerErrorStrategy` functionality
* Improve log when faking error response
* Correct implementation of fake error response without making a request (ForceServerErrorStrategy)
* Update tests + more tests
* Fix compilation of `OfferingsManagerTests`
* Fix compilation of `DebugViewSwiftUITests`
* Improve tests
* Fix `OfflineWithNoMappingStoreKitIntegrationTests`
* FIx compilation
---------
Co-authored-by: Toni Rico <toni.rico.diez@revenuecat.com>
Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
Fixes#3343.
`SKPaymentTransaction.error` docs say:
> The error property is undefined except when `transactionState` is set
to `SKPaymentTransactionState.failed`. Your application can read the
error property to determine why the transaction failed. For a list of
error constants, see `SKErrorDomain` in StoreKit Constants.
Which is why [`PurchasesOrchestrator` only considered that
domain](https://github.com/RevenueCat/purchases-ios/blob/4.28.1/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift#L792).
This expands this handling to consider any other network error,
therefore providing more precise errors.
Since offline connection error detection was only in `HTTPClient`, I
also moved that out and removed `NetworkError.offlineConnection`, so
that we detect this when converting `NetworkError.networkError` instead.
This paves the way to add a second set of paths with a separate server
URL to be able to shed requests to offerings and product entitlement
mapping to a separate server.
### Changes:
- New `HTTPRequestPath` with the same properties as `HTTPRequest.Path` +
a new `serverHostURL`
- Moved proxy logic from `SystemInfo`/`HTTPClient` to `HTTPRequestPath`
- Simplified `HTTPRequest` implementation
## Description:
We've gotten several reports with error messages that aren't very
helpful, like #2390.
Turns out that whenever the API returned a non-successful response but
without an error code, we always produced _"unknown error"_.
This improves the behavior to default to showing the status code. One
example that was very obvious is setting up the SDK with an invalid API
key.
## Examples:
### `OfferingsManager.Error`:
- Before:
```
[Purchases] - ERROR: 🍎‼️ Error fetching offerings - The operation couldn’t be completed. (RevenueCat.OfferingsManager.Error error 0.)
Underlying error: The operation couldn’t be completed. (RevenueCat.NetworkError error 6.)
```
- After:
```
[Purchases] - ERROR: 🍎‼️ Error fetching offerings - The operation couldn’t be completed. (RevenueCat.OfferingsManager.Error error 0.)
Underlying error: Unknown error. API request failed with status code 401
```
### `BackendError`:
- Before:
```
[Purchases] - ERROR: 😿‼️ Unknown error.
```
- After:
```
[Purchases] - ERROR: 😿‼️ Unknown error. API request failed with status code 401
```
### Request debug logs:
- Before:
```
[Purchases] - DEBUG: ℹ️ API request failed: GET /v1/subscribers/$RCAnonymousID:988f9b85609b4a57af255750bec1fbb2/offerings: Unknown error.
```
- After:
```
[Purchases] - DEBUG: ℹ️ API request failed: GET /v1/subscribers/$RCAnonymousID:988f9b85609b4a57af255750bec1fbb2/offerings: Unknown error. API request failed with status code 401
```
### `CustomerInfoManager`:
- Before:
```
[Purchases] - WARN: ⚠️ Attempt to update CustomerInfo from network failed.
The operation couldn’t be completed. (RevenueCat.BackendError error 0.)
Underlying error: Unknown error
```
- After:
```
[Purchases] - WARN: ⚠️ Attempt to update CustomerInfo from network failed.
The operation couldn’t be completed. (RevenueCat.BackendError error 0.)
Underlying error: Unknown error. API request failed with status code 401
```
Fixes [CSDK-634].
## Changes:
- Created `HTTPResponseValidationResult`
- Added `HTTPResponseValidationResult` and `HTTPClient.ResponseHeaders`
to `HTTPResponse`
- Added new `ErrorCode.signatureVerificationFailed`
- Extracted `SigningType` to create `MockSigning` to test `HTTPClient`
without bothering with signatures
- Refactored `ETagManager` to use `HTTPResponse`, making it easier to
pass through response headers and verification results
- Stored `HTTPResponseValidationResult` in `ETagManager` cache
- `HTTPClient` handles signatures from response based on
`SystemInfo.responseVerificationLevel`
- Renamed `HTTPRequest.createIntegrityEnforcedRequestRequest` to
`HTTPRequest.createWithResponseVerification`
- Added lots of tests for this new behavior in `HTTPClient`
- Added lots of new tests for `ETagManager`
[CSDK-634]:
https://revenuecats.atlassian.net/browse/CSDK-634?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
This is the last change from what was started with #1437. That PR introduced a few private error types, but for the errors returned in the public `APIs` we were still passing around `Error`s everywhere, which didn't provide any compile-time guarantees.
### Goals:
- Ensure that public APIs *only* return `ErrorCode`s wrapped with the additional `userInfo`, and not accidentally return other types like `SKError`, `BackendError`, `ErrorCode` directly, etc.
- Guarantee type-safety when passing errors around within our internal implementations instead of assuming certain thrown errors are of a particular type.
### Changes:
- Created `PurchasesError` for wrapping an `ErrorCode` with the additional `userInfo`.
- Changed returned errors from `Error` to `NSError`. This is a change in the API, but the new type is a more specific `Error` so it shouldn't lead to any changes in client apps.
- Because `PurchasesError` isn't implicitly convertible to `NSError`, this ensures that we don't return those values directly, and instead use `PurchasesError.asPublicError`.
- Changed `ErrorCodeConvertible` to a now more precise `PurchasesErrorConvertible`.
Thanks to this new type-safety this PR fixes at least 3 bugs where we were returning private errors instead of `ErrorCode`s.
### Testing:
Thanks to #1871 we know that the returned errors are still convertible to `ErrorCode` so users can `switch` over them.