Commit Graph
68 Commits
Author SHA1 Message Date
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
Greenie 880c441798 Add prepaid as a period type (#4782) 2025-02-18 21:16:52 +00:00
Antonio Pallares 7683d522fa UI Preview Mode: mock CustomerInfo (#4786)
* feat: mock `CustomerInfo` when in UI Preview mode and avoid caching it

* feat: return mock `CustomerInfo` when in UI Preview mode when requesting the cached `CustomerInfo`

* tests: add unit test for `CustomerInfo` mock in UI Preview mode

* fix linter complaints
2025-02-14 16:34:59 +01:00
AJPallares 788b6276dd UI Preview Mode: app user ID (#4725)
* feat: use fix, hardcoded `appUserID` for UI preview mode

* test: add unit tests for user ID in UI preview mode

* fix linter's Trailing Whitespace Violations

* PR #4725 comments

* Change default appUserID for UI Preview mode.
* Add debug log regarding the use of UI Preview mode user ID.
2025-01-30 12:14:30 +01:00
Facundo Menzella 475a72b470 feat: [CustomerCenter] Introduce PurchaseHistory (#4686) 2025-01-21 10:06:06 +01:00
Toni Rico 511ed4ae10 [Trusted Entitlements] Do not clear CustomerInfo upon enabling Trusted Entitlements (#4671)
* [Trusted Entitlements] Do not clear CustomerInfo upon enabling Trusted Entitlements

* improve test

* Improve naming consistency with Android
2025-01-16 16:48:43 +01:00
Cesar de la VegaandAndy Boedo 658782b68d Adds subscriptions to CustomerInfo (#4508)
* adds SubscriptionInfo

* fix nullabilities

* purchaseDate is not nullable

* add isActive

* add store

* add the product identifier

* add willRenew

* private extension

* made storeTransactionId nil

* fix tests and docs

* fix tests

* fix more tests

* fix compilation

* fix test

* API tests and other fixes

* Update Sources/Purchasing/NonSubscriptionTransaction.swift

Co-authored-by: Andy Boedo <andresboedo@gmail.com>

* use ProductIdentifier and subscriptionsByProductIdentifier

* fix lint

* disable file_length

---------

Co-authored-by: Andy Boedo <andresboedo@gmail.com>
2024-12-04 12:00:27 +01:00
Toni Rico 4503a837e6 [Diagnostics] Refactor diagnostics track methods to handle background work automatically (#4270)
* Refactor diagnostics track methods to handle background work automaticaly

* fix swiftlint
2024-09-26 16:33:28 +02:00
NachoSoto 2bcd0ce328 5.0.0 changes: support SK2 by default 2024-07-01 14:37:21 -05:00
Toni Rico bb8066d9b3 Add http_request_performed diagnostics event (#3897)
### Description
This adds a new diagnostics event `http_request_performed` with some
useful information we would like to track about the http requests
performed to our servers.
2024-05-16 15:13:40 +02:00
Toni Rico 0a534c8260 Clear diagnostics file if we detect it's too big during event tracking (#3824)
### Description
This makes it so, if we detect the diagnostics file gets too big while
tracking an event, we just clear it and start over. Additionally, we add
an event tracking that this happened to the diagnostics file.
2024-05-10 17:00:28 +02:00
Cesar de la Vega 5e0bc32867 Add diagnostics event for Customer Info verification (#3823)
Track CustomerInfo verification results
2024-04-25 23:03:45 +02:00
61d66bbc32 Add currentOffering(forPlacement: String) to Offerings (#3707)
### Motivation

New function to get `Offering` by a placement identifier

### Description

New `currentOffering(forPlacement: String): Offering?` method on
`Offerings`
- Added demo button for this in purchase tester app

```swift
// Example usage
if let offering = offerings.currentOffering(forPlacement: "onboarding") {
  showPaywall(offering)
}
```

#### Logic flow

- If a placement identifier explicitly exists within the
`placements.offeringIdsByPlacement` dictionary, the method attempts to
fetch the corresponding `Offering` without resorting to a fallback
- Example: when there is a `nil` / null value for a placement, we don't
want to use the fallback
- If the placement identifier does not exist, the method then attempts
to fetch a fallback offering defined by `placements.fallbackOfferingId`

#### New placements object in offerings response

```js
{
    // other offerings stuff
    "placements": {
        "fallback_offering_id": "offering_a", // can be null
        "offering_ids_by_placement": {
            "onboarding_start": null,
            "onboarding_end": "offering_b",
        }
  }
}
```

---------

Co-authored-by: Guido Torres <guido@revenuecat.com>
Co-authored-by: Josh Holtz <me@joshholtz.com>
Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
2024-02-28 15:11:48 -06:00
NachoSoto b167e526af CustomerInfo: conform to Identifiable (#3619) 2024-02-01 08:02:05 -08:00
NachoSoto 56de917d02 Paywalls: removed presentedPaywallViewMode (#3109)
We're going to track this in a different way in the future.
2023-09-14 18:28:11 -07:00
NachoSoto 8ba761a23f Paywalls: send presented PaywallViewMode with purchases (#2859)
I've moved `PaywallViewMode` from `RevenueCatUI` to `RevenueCat`. This
uses the same approach as #2645. This method is `public` so it can be
used from `RevenueCatUI`, but the documentation stays its purpose.

The behavior is covered with integration tests and a snapshot test.

Depends on https://github.com/RevenueCat/khepri/pull/6364.
2023-09-14 18:28:11 -07:00
NachoSoto 456225ab65 OperationDispatcher: add support for "long" delays (#3168) 2023-09-08 07:28:54 -07:00
NachoSoto 82435936d9 Xcode 13: remove conditional code (#3147)
Clean up thanks to #2421
2023-09-06 10:59:22 -07:00
NachoSoto cdcd1a006f Purchases: don't clear intro eligibility / purchased products cache on first launch (#3067)
These caches are important, especially for `RevenueCatUI`.
Without this fix, launching the app was doing this:
- Pre-warming cache
- Pre-warming cache a second time (to be fixed by a separate PR)
- Clearing cache

Which meant that the cache wasn't really warm when launching paywalls.

To fix that, this only clears the cache after receiving an actual
change.

I've also removed
`CustomerInfoManager.sendCachedCustomerInfoIfAvailable` because it
wasn't used.
2023-08-30 15:38:08 -07:00
NachoSoto 247dc1a9ac CustomerInfo: add VerificationResult to description (#3081) 2023-08-25 09:13:25 -07:00
NachoSoto 5d3263c8e6 TransactionPoster: don't finish transactions for non-subscriptions if they're not processed (#2841) 2023-08-15 07:05:00 -07:00
NachoSoto a31743aaea Integration Tests: added tests for non-renewing and non-consumable packages (#3008)
There were some questions about these in #2841, and realized we had no
test coverage for them.
This also improves the documentation of `CustomerInfo.nonSubscriptions`
and `NonSubscriptionTransaction`.
2023-08-14 15:09:32 -07:00
NachoSoto 581a048308 CustomerInfoManager: post transactions in parallel to POST receipts only once (#2954)
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.
2023-08-11 15:39:44 -07:00
Toni Rico 93510f2411 Refactor method to get product ID including plan ID in android purchases (#2708)
### Description
Small rename from PR review in #2703. That was merged quickly to solve
the issue.
2023-06-26 18:11:31 +02:00
Toni Rico 48127ff7d1 Fix google play purchases missing purchase date (#2703)
### Description 
Reported in https://github.com/RevenueCat/purchases-flutter/issues/738. 

After https://github.com/RevenueCat/purchases-ios/pull/2654, we were
using the new Google Play identifiers that include the plan id in
expiration dates, but we didn't change the system that parses purchase
dates. Later on, when we map purchases, we iterate over the expiration
dates map, and since that id wasn't found in the purchase dates, it was
null, causing the issue in flutter.

This makes the purchase date calculation use the same algorithm that we
use for expiration dates to get the new google play product identifiers.
2023-06-26 11:17:22 +02:00
Josh Holtz dececcba93 Support product_plan_identifier for purchased subscriptions from Google Play (#2654)
### Motivation

Addresses https://github.com/RevenueCat/purchases-flutter/issues/692

### Description

Shows the product id as `<product>:<base_plan>` if the subscription was
purchased from Google Play. This uses the `product_plan_identifier` that
is returned with the subscription.
2023-06-15 17:54:36 -05:00
NachoSoto ea173c0f7e copy(with: VerificationResult): optimization to avoid copies (#2639) 2023-06-14 07:05:37 -07:00
NachoSoto b76548b938 CustomerInfoManager: post all unfinished transactions (#2563) 2023-05-31 13:52:08 -07:00
NachoSoto de47c5771b GetCustomerInfo posts receipts if there are pending transactions (#2533)
This fixes the last edge-case for offline entitlements. Solves the
following scenario:
```swift
func testPurchaseWhileServerIsDownPostsReceiptWhenForegroundingApp() async throws {
    let logger = TestLogHandler()

    // 1. Purchase while server is down
    self.serverDown()
    try await self.purchaseMonthlyProduct()

    logger.verifyMessageWasNotLogged("Finishing transaction")

    // 2. Server is back
    self.serverUp()

    // 3. Request current CustomerInfo
    let info1 = try await Purchases.shared.customerInfo()
    try await self.verifyEntitlementWentThrough(info1)

    // 4. Ensure transaction is finished
    logger.verifyMessageWasLogged("Finishing transaction", level: .info)

    // 5. Restart app
    Purchases.shared.invalidateCustomerInfoCache()
    await self.resetSingleton()

    // 6. To ensure (with a clean cache) that the receipt was posted
    let info2 = try await Purchases.shared.customerInfo()
    try await self.verifyEntitlementWentThrough(info2)
}
```

This race condition happened when fetching `CustomerInfo` after we had
computed an offline `CustomerInfo` with a purchase: if the server is
still not aware of the purchase, then we lose that until some arbitrary
amount of time when `StoreKit` might decide to send the pending
transactions in the queue.
For that reason, instead of waiting, we proactively check now whenever
`CustomerInfoManager` fetches new info.

The refactors #2540 and #2542 where required to break a retain cycle.
Also thanks to #2534 we know for sure that there aren't any cycles.

Note that this is an iOS 15.0+ only feature. It would be useful to
better ensure consistency in older versions, but it's not strictly
required, as those versions don't support offline entitlements either.
2023-05-30 12:38:18 -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
NachoSoto 77e0be299e Perf: CustomerInfoManager.fetchAndCacheCustomerInfoIfStale no longer fetches data if stale (#2508) 2023-05-19 10:14:16 -04:00
NachoSoto 64287a5320 CustomerInfoManager: don't cache offline CustomerInfo (#2378)
This is meant to be a temporary `CustomerInfo` while the backend is
offline.
See also #2368.
2023-05-17 14:36:09 -04:00
Andy Boedo 9e373c36da Update offerings cache when switchUser(to:) is called (#2455)
Added updating of offerings cache when `switchUser(to:)` is called. 
Also added checking that the new appUserID != the current one and no-op
if it is.
2023-04-27 15:19:56 -03: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 8201edfe67 CustomerInfoManager: fixed deadlock caused by reading CustomerInfo inside of observer (#2412)
Fixes #2410 and
https://github.com/RevenueCat/react-native-purchases/issues/579.

This adds 2 previously failing tests that reproduce this issue: one
directly on `CustomerInfoManager` and another one utilizing
`PurchasesDelegate`.
These illustrate the issue: if the observer (through
`CustomerInfoManager.monitorChanges` or `PurchasesDelegate`) called a
method that ended up reading `CustomerInfoManager`'s data inside of the
observation, the SDK would deadlock.

This is because we were invoking the observers while inside the lock.
This was unnecessary, so this fixes the issue by ensuring that the
observers receive updates after the lock is released, using a new
`OperationDispatcher.dispatchAsyncOnMainThread`.
2023-04-19 11:45:43 -07:00
NachoSoto 3246a58978 DeviceCache: workaround for potential deadlock (#2375)
Fixes #2252, #2364, [SDKONCALL-214], [SDKONCALL-241].
See also #2078 and #2079.

### Background

#2078 solved a potential deadlock in `DeviceCache`. This could happen
when a background thread was in the middle of writing to `UserDefaults`
through `DeviceCache`, and at the same time, the main thread wanted to
read from it.
This is normally fine, except that if there are any `NotificationCenter`
observations on the `UserDefaults` used, the background thread would
then post that notification and wait for the main thread, leading to a
deadlock. What #2078 did was to use a different method to make sure that
the observation was received in the calling thread.
Additionally, #2079 avoided that observation whenever we used a
different `UserDefaults`.

### Problem

When the SDK changed to a custom `UserDefaults` (#2046), we made the
choice to only do this if there was no prior data.
However, if an app has prior RevenueCat data saved in
`UserDefaults.standard`, that instance is used.

This potential deadlock is possible for any app with prior data, that
also contains observations to `UserDefaults`, either manually through
`NotificationCenter`, or using something like `@AppStorage` with
`SwiftUI`.

### Solution

`DeviceCache.cachedAppUserID` is a very commonly used method. This PR
changes it to an in-memory cache, initialized to the `UserDefaults`
value.
By doing that, most code paths that rely on this value won't risk
deadlocking with `UserDefaults` writes that jump threads.

### Long term

I've filed SDK-3034 to fully migrate data from `UserDefaults.standard`
to avoid this problem altogether in the future.


[SDKONCALL-214]:
https://revenuecats.atlassian.net/browse/SDKONCALL-214?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[SDKONCALL-238]:
https://revenuecats.atlassian.net/browse/SDKONCALL-238?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[SDKONCALL-241]:
https://revenuecats.atlassian.net/browse/SDKONCALL-241?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2023-03-24 16:15:26 -07:00
NachoSoto 4341745d10 IdentityManager: clear ETagManager and DeviceCache if verification is enabled but cached CustomerInfo is not (#2330)
Android counterpart:
https://github.com/RevenueCat/purchases-android/pull/844
2023-03-13 11:57:18 -07:00
NachoSoto 9c21d532fa CustomerInfo: use same grace period logic for active subscriptions (#2327)
Follow up to #2288.
2023-03-07 10:08:06 -08:00
NachoSoto e68a31b206 CustomerInfo: removed entitlementVerification (#2320)
See conversation in
https://linear.app/revenuecat/issue/SDK-2842/[android]-create-trusted-property-in-entitlementinfo
This was redundant, it makes more sense to access this from
`EntitlementInfos`.
2023-03-03 09:58:33 -08:00
NachoSoto 32095dcd40 Renamed VerificationResult.notVerified to .notRequested (#2321)
See conversation in
https://linear.app/revenuecat/issue/SDK-2896/invalidate-devicecache-cache-if-entitlement-verification-is-missing
This is more clear, as "not verified" could be confused as "failed to
verify".

This hasn't shipped yet so it's safe to rename.
2023-03-03 09:48:38 -08:00
NachoSoto da9ba397a3 Update CustomerInfo.requestDate from 304 responses (#2310)
### Purpose:

This is a prerequisite for #2288. The new grace period means that if we
didn't update the date from the cached responses, entitlements would
become stale and expired after 3 days.

### Changes:
- Added `HTTPResponseBody.copy(with newRequestDate:)` to be able to
modify responses from the header request date
- `CustomerInfo` implements this method to modify its request date
- Added `HTTPResponse.requestDate` to be able to keep track of the
request date from the server
- Added `VerificationResult.from(cache:response:)` to determine the most
restrictive verification result based on what's cached or checked from a
response
- Added `HTTPResponse.copy(with:)` to modify the verification result of
a response using the previous method
- `HTTPClient` now uses the most restrictive verification result
- `HTTPClient` updates request date from server responses or cached
responses (unless verification failed). This was the missing piece for
#2288.

### Tests:
- Verify that `ETagManager` does not use an ETag if verification was
previously not enabled
- Verify that `ETagManager` returns the request date from the server
when returning a cached response
- Verify that `HTTPClient` updates the request date from the server or
from a cached response
- Verify that `HTTPClient` does not update request date if verification
failed
- Tests for `HTTPResponse` request date parsing
- Test for `CustomerInfoResponseHandler` updating request date
- Tests for `CustomerInfo.copy(with newRequestDate:)`


### Other smaller changes:
- Moved `CustomerInfo.asData()` into `Encodable.asJSONEncodedData()`
- Renamed `requestTime` to `requestDate` everywhere for consistency
2023-03-01 09:24:32 -08:00
Toni Rico fdd79cdcb0 Fix issue with missing subscriber attributes if set after login but before login callback (#2313)
### Description
Deals with
[SDK-2817](https://linear.app/revenuecat/issue/SDK-2817/fix-issue-with-missing-subscriber-attributes-on-purchases-[ios])
This is the iOS counterpart to
https://github.com/RevenueCat/purchases-android/pull/809

There was an issue where subscriber attributes could be delayed to be
synced to the backend. This happened when setting a subscriber attribute
AFTER the `logIn` call but BEFORE the `logIn` operation was completed.

This issue happens because we start syncing subscriber attributes
immediately when calling `logIn`, but if we set any after that but
before the login is complete, they will be assigned to the old user id.
We would sync those the next time the app is backgrounded/foregrounded
but might be missed if there is a purchase before that happens. When
there is a purchase, we only sync the current user's unsynced
attributes, but not attributes of other users.

This PR copies all unsynced attributes from the old user id to the new
one upon a successful login. This way, the unsynced attributes will be
sent if the user tries to purchase.

Note that we only copy these attributes when the old user id was
anonymous. This is because there is a possible edge case of copying
attributes from a user to a different user if the sync request fails and
remains unsynced when changing between user ids.
2023-02-23 15:52:58 +01:00
NachoSoto 84eed758c1 Introduced Configuration.EntitlementVerificationMode and VerificationResult (#2277)
Fixes [CSDK-629].
Depends on #2274 and #2267.

Entitlement verification is accomplished by preventing MiTM attacks
between the SDK and the RevenueCat server.
With verification enabled, the SDK ensures that the response created by
the server was not modified by a third-party, and the entitlements
received are exactly what was sent.

Entitlements are only verified if enabled using
`Configuration.Builder.with(entitlementVerificationMode:)`, which is
disabled by default.

### Changes:
- Added new `VerificationResult`, which replaces
`HTTPResponseVerificationResult`
- Renamed `Configuration.EntitlementVerificationLevel` to
`Configuration.EntitlementVerificationMode`
- `Configuration.with(entitlementVerificationMode:)` is now `public`
- Added `CustomerInfo.entitlementVerification`
- Added `EntitlementInfos.verification` and
`EntitlementInfo.verification`
- Added tests to cover serialization of these new values
- `CustomerInfoResponseHandler` copies the `ValidationResult` to
`CustomerInfo` (all operations that fetch `CustomerInfo` use this)
- Renamed "validation" to "verification" everywhere for consistency
- Finalized documentation

### TODO:
- [x] Figure out whether to use a separate type from
`HTTPResponseValidationResult` or rename that one
- [x] Figure out final API type and property names
- [x] Add tests for `CustomerInfo.copy(with:)`
- [x] Ensure validation result is encoded and decoded (for caching)
- [x] Add missing snapshots

[CSDK-629]:
https://revenuecats.atlassian.net/browse/CSDK-629?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2023-02-17 16:49:21 -08:00
NachoSoto c765b18a9d CustomerInfoManager: improved thread-safety (#2224) 2023-01-26 09:23:24 -08:00
NachoSoto 01bfb5bbda CustomerInfo: support parsing schema version 2 to restore SDK v3.x compatibility (#2213) 2023-01-17 07:13:51 -08:00
NachoSoto 2cd313f355 Configuration.with(appUserID:): allow passing nil and added new tests (#2110)
The docs had this:
> Pass `nil` or an empty string if you want ``Purchases`` to generate
this for you.

But `nil` wasn't a valid input.
I noticed [this reported
issue](https://github.com/RevenueCat/purchases-ios/issues/2107#issuecomment-1332007011)
where the user was doing this:
```swift
.with(appUserID: userUID ?? "")
```

Which technically would have the same semantics, since we allow empty
and convert it to anonymous, but now this is more clear and it matches
the docstring.
2022-12-05 10:16:06 -08:00
NachoSoto b93b082690 Fixed Xcode 13.2.x / Swift 5.5 compatibility (#1994)
Fixes [SDKONCALL-156].

This will be covered by #1990.

[SDKONCALL-156]:
https://revenuecats.atlassian.net/browse/SDKONCALL-156?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-10-24 14:36:05 -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 0bff3f65fe Sendable support (#1795)
Fixes [CSDK-379] and https://github.com/RevenueCat/purchases-ios/issues/1041#issuecomment-1206445939

[CSDK-379]: https://revenuecats.atlassian.net/browse/CSDK-379?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

### Main changes:
- `Sendable` conformances to all types that are thread-safe
- `@unchecked Sendable` for types that the compiler can't enforce, but with documentation as to why
- Made classes that aren't mocked `final` (note that this doesn't change the API, because none of these were `open` to begin with)
- Made some classes thread-safe that weren't (like `DeviceCache`)

For the non-`final` `class`es, I've actually managed to compile the SDK with all of them as `final` and `Sendable`, to make sure that making them `@unchecked Sendable` didn't hide any other issues.

### Future improvements:
- https://twitter.com/nachosoto/status/1557139992056500224?s=21&t=arWdvEzTIFANBvwqQ0vPiA
- https://twitter.com/nachosoto/status/1557141944777592832?s=21&t=arWdvEzTIFANBvwqQ0vPiA
- https://twitter.com/nachosoto/status/1557143374922059776?s=21&t=arWdvEzTIFANBvwqQ0vPiA

### Depends on:
- #1794
- #1804
- #1806
- #1807
- #1808
- #1813
- #1822
- #1823
- #1824
- #1825
- #1826
2022-08-31 13:30:38 -07:00