-
Notifications
You must be signed in to change notification settings - Fork 316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduced type-safe PurchasesError
and fixed some incorrect returned error types
#1879
Conversation
self.identityManager.logIn(appUserID: appUserID) { result in | ||
self.operationDispatcher.dispatchOnMainThread { | ||
completion(result.value?.info, result.value?.created ?? false, result.error) | ||
completion(result.value?.info, result.value?.created ?? false, result.error?.asPurchasesError) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BackendError
s! Caught by the compiler now.
abc278f
to
b75891e
Compare
Yess I got this all working with type-safe public errors 🤪 |
dda0acc
to
f0a66f8
Compare
@@ -391,7 +391,7 @@ final class PurchasesOrchestrator { | |||
)) | |||
|
|||
DispatchQueue.main.async { | |||
completion(nil, nil, error, false) | |||
completion(nil, nil, ErrorUtils.purchasesError(withUntypedError: error).asPublicError, false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is needed because of untyped throws
in Swift, which doesn't allow us to guarantee type safety here.
However this is pretty well covered in tests.
PurchasesError
errors and fixed some incorrect returned error types
fb6d963
to
557bc8a
Compare
9573724
to
e94f7ee
Compare
PurchasesError
errors and fixed some incorrect returned error typesPurchasesError
and fixed some incorrect returned error types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks good, like the extra security this provides us 😄 . Just a couple of questions.
import Foundation | ||
|
||
/// An error returned by a `RevenueCat` public API. | ||
public typealias PublicError = NSError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I wonder if we should rename this... PublicError
from the point of view of SDK users doesn't make that much sense... Maybe RCError
? PurchasesError
is already used... Can't think of other names at the moment 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I didn't love it either. It's just a transparent alias but I agree it could be better. Maybe ExposedError
, ReturnedError
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I guess I don't love any of the current options but I have a slight preference for ExposedError
. Also, not sure if we should namespace the errors to RC? As in RCExposedError
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to namespace in Swift (since it's already namespaces by the module name RevenueCat
. And this typealias
is transparent in Objective-C, meaning APIs see NSError
instead.
_ = Task<Void, Never> { | ||
do { | ||
let products = try await self.sk2StoreProducts(withIdentifiers: identifiers) | ||
Logger.debug(Strings.storeKit.store_product_request_finished) | ||
completion(.success(Set(products))) | ||
} catch { | ||
Logger.debug(Strings.storeKit.store_products_request_failed(error: error)) | ||
completion(.failure(error)) | ||
completion(.failure(ErrorUtils.storeProblemError(error: error))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ErrorCode
.
import Foundation | ||
|
||
/// An error returned by a `RevenueCat` public API. | ||
public typealias PublicError = NSError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I didn't love it either. It's just a transparent alias but I agree it could be better. Maybe ExposedError
, ReturnedError
?
… returned error types 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. - 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. - 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 2 bugs where we were returning private errors instead of `ErrorCode`s. Thanks to #1871 we know that the returned errors are still convertible to `ErrorCode` so users can `switch` over them.
e94f7ee
to
abc5cd9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is the question of that rename but the PR looks great!
**This is an automatic release.** ### Bugfixes * `watchOS`: fixed crash when ran on single-target apps with Xcode 14 and before `watchOS 9.0` (#1895) via NachoSoto (@NachoSoto) * `CustomerInfoManager`/`OfferingsManager`: improved display of underlying errors (#1888) via NachoSoto (@NachoSoto) * `Offering`: improved confusing log for `PackageType.custom` (#1884) via NachoSoto (@NachoSoto) * `PurchasesOrchestrator`: don't log warning if `allowSharingAppStoreAccount` setting was never explicitly set (#1885) via NachoSoto (@NachoSoto) * Introduced type-safe `PurchasesError` and fixed some incorrect returned error types (#1879) via NachoSoto (@NachoSoto) * `CustomerInfoManager`: fixed thread-unsafe implementation (#1878) via NachoSoto (@NachoSoto) ### New Features * Disable SK1's `StoreKitWrapper` if SK2 is enabled and available (#1882) via NachoSoto (@NachoSoto) * `Sendable` support (#1795) via NachoSoto (@NachoSoto) ### Other Changes * Renamed `StoreKitWrapper` to `StoreKit1Wrapper` (#1886) via NachoSoto (@NachoSoto) * Enabled `DEAD_CODE_STRIPPING` (#1887) via NachoSoto (@NachoSoto) * `HTTPClient`: added `X-Client-Bundle-ID` and logged on SDK initialization (#1883) via NachoSoto (@NachoSoto) * add link to SDK reference (#1872) via Andy Boedo (@aboedo) * Added `StoreKit2Setting.shouldOnlyUseStoreKit2` (#1881) via NachoSoto (@NachoSoto) * Introduced `TestLogHandler` to simplify how we test logged messages (#1858) via NachoSoto (@NachoSoto) * `Integration Tests`: added test for purchasing `StoreProduct` instead of `Package` (#1875) via NachoSoto (@NachoSoto) * `ErrorUtils`: added test to verify that returned errors can be converted to `ErrorCode` (#1871) via NachoSoto (@NachoSoto)
This was missed in #1879. Without this, we might have been forwarding some private errors instead of ensuring that we only sent `CodeError`s
This was missed in #1879. Without this, we might have been forwarding some private errors instead of ensuring that we only sent `CodeError`s
…'d as `ErrorCode` This is a key part of how we document error handling in the SDK, so it's important that it's covered in a test. The second test also verifies that this same error contains all the metadata. This is a follow up to #1879.
This wasn't required, likely since the introduction of #1879, which added `matchError` as a much better way to check errors. I noticed this when adding a new `case` with an associated value that wasn't `Equatable`. Tiny change but should make compilation faster, and reduce the binary size.
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 aroundError
s everywhere, which didn't provide any compile-time guarantees.Goals:
ErrorCode
s wrapped with the additionaluserInfo
, and not accidentally return other types likeSKError
,BackendError
,ErrorCode
directly, etc.Changes:
PurchasesError
for wrapping anErrorCode
with the additionaluserInfo
.Error
toNSError
. This is a change in the API, but the new type is a more specificError
so it shouldn't lead to any changes in client apps.PurchasesError
isn't implicitly convertible toNSError
, this ensures that we don't return those values directly, and instead usePurchasesError.asPublicError
.ErrorCodeConvertible
to a now more precisePurchasesErrorConvertible
.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 canswitch
over them.