Commit Graph
49 Commits
Author SHA1 Message Date
Toni Rico b6c78c60c1 [Diagnostics] Fix store kit error description tracking (#4799) 2025-02-21 12:01:41 +01:00
Antonio Pallares 6c07039715 UI preview mode: disable log in and log out (#4804)
* fail when making log in or log out calls in UI preview mode

* Add unit tests for log in and log out in UI preview mode
2025-02-20 18:27:16 +01:00
Mark Villacampa 926018921b Use #file instead of #fileID to avoid including the full path in the compiled binary (#4605) 2024-12-17 18:42:02 +01:00
Toni Rico 793b6f6f1e Handle new backend error codes that may show in the redemption endpoint (#4592) 2024-12-16 12:33:27 +01:00
Toni Rico c24de73950 WebPurchaseRedemption: Rename alreadyRedeemed result to purchaseBelongsToOtherUser (#4542) 2024-12-05 17:44:08 +01:00
98e4a33225 Support anonymous web purchase redemptions (#4439)
Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
Co-authored-by: andy boedo <andresboedo@gmail.com>
Co-authored-by: Cesar de la Vega <cesarvegaro@gmail.com>
2024-11-25 16:14:32 +01:00
Will Taylor 6de169a23e support fetching eligible win-back offers for a product 2024-11-06 15:46:04 -06:00
Cesar de la Vega 93e3f3f477 [Diagnostics] Add apple_purchase_attempt event (#4253) 2024-09-25 16:41:05 +02:00
NachoSoto 2bcd0ce328 5.0.0 changes: support SK2 by default 2024-07-01 14:37:21 -05:00
Mark Villacampa 679767adf9 Enable explicit_init lint rule and fix issues (#3418)
This rule will warn when calling `init` with the explicit type where it
can be inferred, or where the `.init` part can be omitted.
2023-11-16 00:20:00 +01:00
NachoSoto ea952f0108 ErrorUtils.purchasesError(withSKError:): handle URLErrors (#3346)
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.
2023-10-25 20:25:09 -07:00
NachoSoto 833524314d HTTPClient: extracted HTTPRequestPath protocol (#2986)
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
2023-08-10 15:23:13 -07:00
NachoSoto 2727d7ed2f Trusted Entitlements: added link to docs in ErrorCode.signatureVerificationFailed (#2783)
Same as https://github.com/RevenueCat/purchases-android/pull/1132
2023-07-11 09:48:55 -07:00
NachoSoto 37f9c005d1 ErrorUtils: handle PurchasesError to avoid creating unknown errors (#2686) 2023-06-22 06:54:02 -07:00
NachoSoto 6986a2f95e NetworkError.signatureVerificationFailed: added status code to error userInfo (#2657) 2023-06-16 07:55:21 -07:00
NachoSoto e2534f0172 ErrorUtils.missingReceiptFileError: added receipt URL userInfo context (#2650)
Added `rc_receipt_url` and `rc_receipt_file_exists`. This is useful when
debugging issues like #2558.
This goes from this:
> [Purchases] - ERROR: 💰 Product purchase for 'rc_1099_1w' failed with
error: Error Domain=RevenueCat.ErrorCode Code=9 "The receipt is
missing." UserInfo={NSLocalizedDescription=The receipt is missing.,
readable_error_code=MISSING_RECEIPT_FILE,
source_function=handlePurchasedTransaction(_:data:completion:),
source_file=RevenueCat/TransactionPoster.swift:96}

To this:
> [Purchases] - ERROR: 💰 Product purchase for 'rc_1099_1w' failed with
error: Error Domain=RevenueCat.ErrorCode Code=9 "The receipt is
missing." UserInfo={NSLocalizedDescription=The receipt is missing.,
readable_error_code=MISSING_RECEIPT_FILE,
source_function=handlePurchasedTransaction(_:data:completion:),
rc_receipt_url=file:///.../Build/Products/Debug/...app/Contents/_MASReceipt/receipt,
rc_receipt_file_exists=true,
source_file=RevenueCat/TransactionPoster.swift:96}

In this case, we can see the file does exist, so it must be empty.
2023-06-14 12:36:31 -07:00
NachoSoto a566b29dbf ErrorCode.networkError: improved description (#2610) 2023-06-12 07:08:10 -07:00
NachoSoto 50ffbfb3e9 Performance: new check to ensure serialization / deserialization is done from background thread (#2496) 2023-05-19 11:58:12 -07:00
NachoSoto 99c8a34e62 Offline Entitlements: use offline-computed CustomerInfo when server is down (#2368)
### Changes:
- Created `PurchasedProductsFetcherType` to be able to mock and hold
fetchers, even on older devices
- Created `ProductEntitlementMappingFetcher` to abstract injecting
`ProductEntitlementMapping`
- Added logic to `CustomerInfoResponseHandler` to compute offline
`CustomerInfo` after a server error (see #2367). **This is used by all
`CustomerInfo` requests as well as `PostReceiptDataOperation`**.
- Wrote comprehensive `CustomerInfoResponseHandler` tests

### TODO:
- [x] Don't cache offline computed `CustomerInfo`: #2378
- [x] Integration tests: #2501.
2023-05-19 10:27:44 -07:00
Andy BoedoandNachoSoto 618ff121d2 Custom Entitlements Computation mode (#2439)
### Changes:

- Added `Purchases.configureInCustomEntitlementsComputationMode`
- Added `Purchases.switchUser(to:)`
- Disabled parts of the SDK in this new mode
- New example app for testing `customEntitlementsComputation` mode

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
2023-04-25 13:21:46 -07:00
NachoSoto dbe93dbbf4 PurchasesDiagnostics: added step to verify signature verification (#2267)
⚠️ 🎉 This also changes integration tests to use
`EntitlementVerificationLevel.enforced` so that integration tests fail
if signatures are invalid.

#### Depends on:
- https://github.com/RevenueCat/khepri/pull/5191
- https://github.com/RevenueCat/khepri/pull/5204
- #2214
- #2215 
- #2216
- #2272

_Marking this as `feat`ure because it contains a new error in
`PurchasesDiagnostics.Error`_
2023-02-16 10:27:04 -07:00
NachoSoto f05aabd176 HTTPClient: added signature validation and introduced ErrorCode.signatureVerificationFailed (#2272)
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
2023-02-16 03:06:59 -08:00
NachoSoto 7c268db1cf NetworkError: added underlying error to description (#2263)
### Before:
> [Purchases] - ERROR: 😿‼️ Error performing request.

### After:
> [Purchases] - ERROR: 😿‼️ Error performing request. The operation
couldn’t be completed. (NSURLErrorDomain error -1.)
2023-02-08 10:38:52 -08:00
NachoSoto 15bd24351e ErrorUtils.purchasesError(withUntypedError:): handle PublicErrors (#2165)
See https://github.com/RevenueCat/purchases-hybrid-common/pull/278.
A valid error ended up as:
> failed: caught error: "Unknown error."

This change ensures that calling this method with an `NSError` does not
return `ErrorCode.unknownError`, and instead we extract the correct
error.
2022-12-21 08:47:47 -08:00
NachoSoto 8e32400c4e Fixed purchasing with PromotionalOffers using StoreKit 2 (#2020)
Fixes [SDKONCALL-160], [SDKONCALL-182], #2114, and
https://github.com/RevenueCat/react-native-purchases/issues/455.
Depends on #2118 and https://github.com/RevenueCat/khepri/pull/4843 /
https://github.com/RevenueCat/khepri/pull/4852.

## Changes:
- Base64decode `signature`
- Fixed signature on backend for SK2:
https://github.com/RevenueCat/khepri/pull/4843 /
https://github.com/RevenueCat/khepri/pull/4852
- Added tests
- Added specific error if signature can't be decoded:
> [Purchases] - ERROR: 😿‼️ The information associated with this
PromotionalOffer is not valid.
See https://rev.cat/ios-subscription-offers for more info. The signature
generated by RevenueCat could not be decoded: signature 914

[SDKONCALL-160]:
https://revenuecats.atlassian.net/browse/SDKONCALL-160?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

[SDKONCALL-182]:
https://revenuecats.atlassian.net/browse/SDKONCALL-182?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-12-09 15:04:29 -08:00
NachoSoto 74c29936b8 ErrorCode.unknownBackendError: include original error code (#2032) 2022-11-11 08:02:28 -08:00
NachoSoto a69cb94418 Added BackendErrorCode.purchasedProductMissingInAppleReceipt (#2033)
See https://github.com/RevenueCat/khepri/pull/4716
This makes that error `ErrorCode.invalidReceiptError` instead of
`.unknownBackendError`.

I've also added #2032 to improve error messages when these errors are
unknown in the future.
2022-11-08 09:14:41 -08:00
NachoSoto c2a4df78a6 Fixed ErrorUtils.purchasesError(withUntypedError:) on iOS 12 (#1982)
Fixes [CSDK-491].
#1973 introduced an iOS-12 only regression:

<img width="1201" alt="image"
src="https://user-images.githubusercontent.com/685609/195917395-5368485a-acf1-41e2-b236-8bfc4f59e65c.png">

For some reason the Swift runtime on iOS 12 fails to get the value from
the `Error` existential, and gets a null pointer instead.

This workaround works, and it's equivalent on later iOS versions, just a
bit redundant.

[CSDK-491]:
https://revenuecats.atlassian.net/browse/CSDK-491?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-10-14 14:02:36 -07:00
NachoSoto ae22f6e295 ErrorUtils: improved logging and localizedDescription to include underlying errors (#1974)
## Changes:
- `Error.localizedDescription` now matches the message being logged.
Improvements introduced in #1776, for example, now apply to the
description too.
- Logged `BackendError` messages now include underlying error.

## Examples:

The following errors are improved:

### `BackendError`:
- Before:
>  😿‼️ There was an unknown backend error.

- After:
> 😿‼️ There was an unknown backend error. Page not found

### API key error:
- Before:
> 😿‼️ There was a credentials issue. Check the underlying error for more
details.

- After:
> 😿‼️ There was a credentials issue. Check the underlying error for more
details. Invalid API key

### Attribute errors:
- Before:
> ["$email": "invalid"]

- After:
> One or more of the attributes sent could not be saved. ["$email":
"invalid"]
2022-10-11 12:56:15 -07:00
NachoSoto 6a87aa4ca8 ErrorUtils.purchasesError(withUntypedError:) handle PurchasesErrorConvertible (#1973)
This allows methods throwing something like `BackendError` and
converting it to `PurchasesError` automatically.
2022-10-11 12:50:23 -07:00
NachoSoto 12cf786fd6 Purchases.beginRefundRequest: ensured errors are PublicError (#1913)
This was missed in #1879. Without this, we might have been forwarding
some private errors instead of ensuring that we only sent `CodeError`s
2022-09-21 09:36:15 -07:00
NachoSoto 1bd1bd3dd4 CustomerInfoManager/OfferingsManager: improved display of underlying errors (#1888)
Improved these 2 logs:

> 🍎‼️ Error fetching offerings - The operation couldn’t be completed. (RevenueCat.OfferingsManager.Error error 0.)
Underlying error: The operation couldn’t be completed. (RevenueCat.BackendError error 0.)

> ⚠️ Attempt to update CustomerInfo from network failed.
The operation couldn’t be completed. (RevenueCat.BackendError error 0.)

These 2 now will display the underlying `NetworkError` instead of the opaque "(RevenueCat.BackendError error 0.)".
2022-09-07 11:58:46 -05:00
NachoSoto d4ea390970 Introduced type-safe PurchasesError errors and fixed some incorrect returned error types (#1879)
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.
2022-09-05 13:46:07 -07:00
NachoSoto 56246ae101 ErrorResponse: don't add attribute errors to message if empty (#1844)
Follow up to #1776. That PR added a lot of tests to cover error logging, and #1778 introduced a small error that wasn't covered: if attribute errors are empty, the `localizedDescription` could end up becoming `"[:]"`. This fixes that.

## Examples:

This was the underlying error returned (the output of `error.localizedDescription`):

#### Before:
> [:]
#### After:
> There was a credentials issue. Check the underlying error for more details.

And this is what was logged (notice that I also fixed `ErrorCode.invalidCredentialsError` to not log as an "Apple Error"):

#### Before:
> 🍎‼️There was a credentials issue. Check the underlying error for more details. [:]
#### After:
> 😿‼️ There was a credentials issue. Check the underlying error for more details.
2022-08-17 11:26:10 -07:00
NachoSoto 233e3a30b6 OfferingsManager: expose underlying error when ProductsManager returns an error (#1792)
This will help figure out the reason behind #1765.
We were currently ignoring the error returned by `ProductsManager`.

In the example below, we can see the underlying issue is `SKErrorDomain` code 5.

### Before:
> Error fetching offerings - The operation couldn’t be completed. (RevenueCat.OfferingsManager.Error error 1.)

### After:
> Error fetching offerings - The operation couldn’t be completed. (RevenueCat.OfferingsManager.Error error 1.)
_**Underlying error: The operation couldn’t be completed. (SKErrorDomain error 5.)**_
ERROR: 😿‼️ There is an issue with your configuration. Check the underlying error for more details. There's a problem with your configuration. None of the products registered in the RevenueCat dashboard could be fetched from App Store Connect (or the StoreKit Configuration file if one is being used). 
More information: https://rev.cat/why-are-offerings-empty
2022-08-02 11:19:32 -07:00
NachoSoto e58a00b4be CustomerInfoResponseHandler: return CustomerInfo instead of error if the response was successful (#1778)
Fixes [SDKONCALL-62].
The behavior in version 3.x was that we would return a `CustomerInfo` AND the attribute errors. [The test in that version](https://github.com/RevenueCat/purchases-ios/blob/3.14.2/PurchasesTests/SubscriberAttributes/BackendSubscriberAttributesTests.swift#L370-L404) however only verified that the error was returned.

When we converted `(Value, Error)` callbacks to using `Result` (#1387), I assumed this was an error. Because of that incomplete test, we didn't notice this wasn't the desired behavior.

If the backend returns a 200, it's assumed that the errors aren't more important than the correct `CustomerInfo`.

The new implementation simply logs the attribute errors (which is now tested), and returns the `CustomerInfo`:
```
😿‼️ One or more of the attributes sent could not be saved. ["$email": "email is not in valid format"]
```

[SDKONCALL-62]: https://revenuecats.atlassian.net/browse/SDKONCALL-62?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-07-29 10:04:26 -07:00
NachoSoto f8113a7f14 Error logging: logErrorIfNeeded no longer prints message if it's the same as the error description (#1776)
Working on [SDKONCALL-62] I realized that there are many instances where we end up logging the same message twice.

This PR not only improves the logic, but adds much needed test coverage to `ErrorUtils.logErrorIfNeeded` and error logging in general.

[SDKONCALL-62]: https://revenuecats.atlassian.net/browse/SDKONCALL-62?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-07-28 08:11:57 -07:00
NachoSoto 829b69041d logErrorIfNeeded: also log message if present (#1754)
### Before:

For errors with a custom `message` it turns out that we were never logging it.
This was extra confusing for `OfferingsManager.Error.configurationError`, which ended up displaying this:

> 😿‼️ There is an issue with your configuration. Check the underlying error for more details.

With no underlying details.

### After:

> 😿‼️ There is an issue with your configuration. Check the underlying error for more details.
> There's a problem with your configuration. None of the products registered in the RevenueCat dashboard could be fetched from App Store Connect (or the StoreKit Configuration file if one is being used).
> More information: https://rev.cat/why-are-offerings-empty
2022-07-11 10:43:58 -07:00
Joshua Liebowitz 31f611aaa0 Remove create alias (#1695)
* Remove create alias
Resolves [CSDK-152]
2022-06-14 12:39:41 -07:00
NachoSoto c531814dc6 Purchases.customerInfo(): added overload with a new CacheFetchPolicy (#1608)
Fixes [CF-19].
This allows specifying `CustomerInfoManager` fetching behavior, both as a public API for users, and also internally.
Some future uses of `CustomerInfoManager` might want to ensure that we don't rely on cached data, for example.

Thanks to the `APITester` we know that the existing methods still work, this is purely an additive change.

This is the core of the change:
```swift
/// Specifies the behavior for a caching API.
@objc(RCCacheFetchPolicy)
public enum CacheFetchPolicy: Int {
    /// Returns values from the cache, or throws an error if not available.
    case fromCacheOnly

    /// Always fetch the most up-to-date data.
    case fetchCurrent

    /// Returns the cached data if available and not stale, or fetches up-to-date data.
    case notStaleCachedOrFetched

    /// Default behavior: returns the cached data if available (even if stale), or fetches up-to-date data.
    case cachedOrFetched

    /// Default ``CacheFetchPolicy`` behavior.
    public static let `default`: Self = .cachedOrFetched
}
```

The new `CustomerInfoManagerGetCustomerInfoTests` check all the different combinations of fetching behavior.
I've taken some of the existing tests and refactored them to be `async`, as well as added a bunch more tests.

> **Note**: 
There is also one big hidden bug fix:

`Purchases.getCustomerInfo` now calls `BackendError.asPurchasesError`, so we don't end up returning an internal error type.
This was missed because we always return generic `Error` types, so we have no way to know at compile time that we only return `ErrorCode`s.

[CF-19]: https://revenuecats.atlassian.net/browse/CF-19?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-05-31 13:04:16 -07:00
NachoSoto 29c034dfc8 Converted CustomerInfo and related types to use Codable (#1496)
### Depends on:
- #1537
- #1540
- #1541
- #1543
- #1546
- #1547
- #1550
- #1551,
- #1565

### Changes

- Removed all custom deserialization code thanks to #1537, #1541, and #1543.
- The format of the customer info response is now very concise and only in one place (`CustomerInfoResponse`) instead of being passed through dictionaries and magic strings everywhere.
- `CustomerInfo` was combining the dictionaries inside of `"subscriptions"` and `"non_subscriptions"`, despite them having different formats. This is now made explicit through type conversions between the two, (see `CustomerInfoResponse.allTransactionsByProductId`).
- Removed all custom `CustomerInfo` errors since deserialization is automatic, and the underlying `Error` information will be provided thanks to `ErrorUtils.logDecodingError`.
- `CustomerInfo` deserialization always wraps errors into `ErrorCode.customerInfoError`, which is now also covered in a new test.
- Added a new snapshot test that covers `CustomerInfo` serialization by storing the result in a JSON file.

### Other changes:
- `CustomerInfo` equality is now automatic through the `CustomerInfoResponse` `Equatable` conformance.
- Simplified `schemaVersion` handling
- All tests still use `CustomerInfo(data: [String: Any])`, but that method is only visible for tests now, and it uses the underlying `Decodable` implementation (see `CustomerInfo+TestExtensions.swift`).
- Added extra tests for `CustomerInfoResponse` that use a fixture `CustomerInfo.json`, which is easier to maintain than having JSON in code.
- `RawDataContainer` implementation is changed in #1565.

### TODO:
- [x] Fix all tests
- [x] Update `RawValueContainer`
- [x] Consider recovering `CustomerInfoError`
- [x] Look at all previously logged errors
- [x] Finish `CustomerInfoResponseTests`
- [x] Handle all TODOs
- [x] Make `RawDataContainer` actually store all the original data: #1565
2022-05-24 14:22:11 -07:00
NachoSoto 3a03d147c8 Added new ErrorCode.offlineConnectionError (#1507)
This is a special enough error that it's worth distinguishing from other `ErrorCode.networkError`s.

I see a couple benefits:
- When opening an app that has the SDK, this used to print lots of errors that just said "Error performing request". Now it's more clear what's happening
- Users can respond to failed calls to the SDK more appropriately, whereas before it might not be clear why a request failed.

Also fixed `logErrorIfNeeded` not passing the file/function/line parameters to the log functions.
2022-04-15 15:54:18 +02:00
NachoSoto ea7230de38 NetworkError.errorResponse: added status code to userInfo (#1480)
This could help debug these errors.

Thanks to the error refactor this only had to be put in one place.
2022-04-12 15:14:15 -07:00
NachoSoto cf0a1bdb98 SKError, StoreKitError, and Product.PurchaseError conform to ErrorCodeConvertible and added tests (#1473)
Follow up to #1437.
Fixes [CF-323]
2022-04-12 15:13:28 -07:00
NachoSoto 23f63c5ef7 HTTPClient now handles response deserialization + Introduced NetworkError and BackendError (#1425)
Closes #695 and finishes [CF-195]. 
Depends on ~~#1431, #1432, #1433, #1437~~.

## Goals:

- [x] Handle all requests / response / deserialization errors in `HTTPClient`. Clients of `HTTPClient` will only have to handle the "happy" path: #1431
- [x] `HTTPClient` shouldn't return `Result.success` with failed responses, forcing clients to verify the response is actually a failure: https://github.com/RevenueCat/purchases-ios/pull/1431
- [x] Abstract error response deserialization / error creation: #1427
- [x] Abstract attribute error parsing: #1427 
- [x] Move response deserialization to `HTTPClient` based on a `ResponseType: Decodable` type, so the completion block will simply return a `Result<HTTPResponse<ResponseType>, Error>`
- [x] `ETagManager` should store response `Data` instead of a deserialized `[String: Any]`
- [x] Improved error types in `HTTPClient` to fix tests: #1437
- [x] Dealt with non-backwards compatible `ETagManager: #1438

## Changes:
- Replaced `HTTPResponse`'s body from `[String: Any]` to a generic `HTTPResponseBody`.
- Created `HTTPResponseBody` to abstract `Decodable` and provide some default implementations for types like `Data,` `[String: Any]` (for backwards compatibility to types that aren't `Decodable` yet), and `Decodable` itself.
- New `HTTPResponse.Result` typealias (`Result<HTTPResponse<HTTPResponseBody>, Error>`) used everywhere. This will allow replacing `Error` with a more specific `Error` so we can forward known typed errors, and make sure that we don't end up with the wrong error type, or with a very complex error hierarchy and the details buried in `underlyingError`.
- Each layer (only a few for now) has its own error type: `NetworkError`, `BackendError`, `OfferingsManager.Error`.
- `HTTPClient` for example has to produce `NetworkError`, operations produce `BackendError`
- The parent `BackendError` can have specific errors like `.missingAppUserID`, but also be simply a child error `case networkError(NetworkError)`
- All of these conform to a `ErrorCodeConvertible`, so there is a single point of code that converts from simple and readable errors (like `BackendError.emptySubscriberAttributes`, `.unexpectedBackendResponse(.loginResponseDecoding)`) into errors with all the context using `ErrorUtils`
- Converted `DNSError` into `NetworkError.dnsError`. Its functionality remains unchanged.
- Removed `Backend.RCSuccessfullySyncedKey` and `ErrorDetails.finishableKey` in favor of tested properties on `NetworkError`

### Leftovers

There's a few things I've decided to not finish for now:
- [ ] `CustomerInfo` still does't conform to `Decodable` (manual deserialization is still supported by the current system): #1496
- `SubscriberAttributeDict` could also be `Codable`
- [x] Replace `OfferingsFactory` with `Decodable`: #1435

[CF-195]: https://revenuecats.atlassian.net/browse/CF-195?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-04-12 15:11:22 -07:00
NachoSoto e0f508cba6 Added workaround for StoreKit 1 incorrectly reporting purchase cancellations (#1450)
Fixes #1445.
Similar solution to #965, this attempts to introspect the unknown `SKError` to detect if it actually corresponds to a failed purchase.
2022-04-05 13:20:04 -07:00
NachoSoto a6de3a054c SKError.toPurchasesError: fixed mapping to make it consistent with StoreKitError.toPurchasesError (#1451)
Our mapping between SK1/SK2 errors and `ErrorCode` was inconsistent, so that test was only passing for SK2.

This PR addresses that. It also makes use of `ErrorUtils` methods to ensure that the underlying error is added to the returned errors.
2022-04-05 11:54:01 -07:00
NachoSoto 87e03489e3 Added BackendErrorCode.subscriptionNotFoundForCustomer (#1444)
I was debugging this, so I guess it makes sense to at least add it to the `enum` even if we don't do anything special with it.
2022-04-01 13:40:43 -07:00
aboedo f5697de730 renamed Purchases folder to Sources (#1416)
* renamed `Purchases` folder to `Sources`

* temporarily enabling installation test jobs

* re-disabling installation test jobs
2022-03-28 11:53:24 -03:00