Fixes [CSDK-16].
See
https://docs.google.com/document/d/1DDyed98RFpIvUwUPJcetc7X4fBhDzf-r1trC4g3zf4c/edit#
for reasoning and documentation.
### Motivation
The RevenueCat SDK has always used `UserDefaults.standard` as the
default value. The advantage of this was simplicity, however, this
brings a few challenges.
### Issues
- While `UserDefaults` is itself thread-safe, our users might be
observing `UserDefaults.defaults` changes via `KVO`,
`NotificationCenter`, or `@AppStorage` (see
https://github.com/RevenueCat/purchases-ios/issues/1527). We update
`UserDefaults` from background threads, and users might be getting
updates on those threads without handling thread-safety correctly.
- Users can accidentally delete data from the shared `UserDefaults` (see
https://rev.cat/userdefaults-crash).
Using a custom `UserDefaults` solves all these issues.
### Change
The new `UserDefaults.revenueCatSuite` is used when:
- Only if users aren’t overriding the `UserDefaults` parameter
- Only if `UserDefaults.standard` has no value for
`com.revenuecat.userdefaults.appUserID.new` (the user hasn't used the
SDK in that app before).
#### Logged during app start:
> PurchaseTester[35110:6323140] DEBUG: ℹ️ Configuring SDK using
RevenueCat's UserDefaults suite.
### Alternatives Considered
See
https://docs.google.com/document/d/1c2B4MNAFKs_DcFJ3OE_KnmryHCArBkjlZbAD9_kSXu0/edit#
We considered also migrating data for everyone into the new
`UserDefaults`.
This new approach is much simpler, less error prone, easier to
implement, and maintain.
The only downside is that existing users won’t benefit from this change,
but @aboedo correctly pointed out: if the app is working for them, no
reason to change anything.
So in this way, this becomes a purely forward-looking improvement.
[CSDK-16]:
https://revenuecats.atlassian.net/browse/CSDK-16?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.
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.
* [CF-106] Fetch AdServices Token (#1519)
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
Co-authored-by: Josh Holtz <me@joshholtz.com>
* [CF-553] Post AdServices token (#1534)
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
Co-authored-by: Josh Holtz <me@joshholtz.com>
* Update ios 14 snapshots tets
* Reworked adservices to use new Purchases.shared.attribution API
* Updated with some comments from PR
* Add iAd code back (#1739)
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
* use new mapKeys dictionary extension for device cache
* update for some pr comments
* remove purchasestests
* generate missing snapshots
* fix deprecation messages to clarify method now called on shared instance
* Fix for inserting nonstring into userdefaults
* Add tests for bug
* fix lint
* Added availablility checks for tests and removed unused varialbes
* Whoops... wrong class
Co-authored-by: beylmk <maddie@revenuecat.com>
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>