Files
purchases-ios/Sources/Error Handling/ErrorCode.swift
T

298 lines
13 KiB
Swift
Raw Normal View History

2021-07-14 12:57:51 -07:00
//
2021-07-19 14:50:28 -07:00
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// ErrorCode.swift
2021-07-14 12:57:51 -07:00
//
// Created by Joshua Liebowitz on 7/8/21.
//
import Foundation
/**
Error codes used by the Purchases SDK
*/
2021-07-19 14:50:28 -07:00
@objc(RCPurchasesErrorCode) public enum ErrorCode: Int, Error {
2021-07-14 12:57:51 -07:00
// swiftlint:disable missing_docs
2021-07-14 12:57:51 -07:00
@objc(RCUnknownError) case unknownError = 0
@objc(RCPurchaseCancelledError) case purchaseCancelledError = 1
@objc(RCStoreProblemError) case storeProblemError = 2
@objc(RCPurchaseNotAllowedError) case purchaseNotAllowedError = 3
@objc(RCPurchaseInvalidError) case purchaseInvalidError = 4
@objc(RCProductNotAvailableForPurchaseError) case productNotAvailableForPurchaseError = 5
@objc(RCProductAlreadyPurchasedError) case productAlreadyPurchasedError = 6
@objc(RCReceiptAlreadyInUseError) case receiptAlreadyInUseError = 7
@objc(RCInvalidReceiptError) case invalidReceiptError = 8
@objc(RCMissingReceiptFileError) case missingReceiptFileError = 9
@objc(RCNetworkError) case networkError = 10
@objc(RCInvalidCredentialsError) case invalidCredentialsError = 11
@objc(RCUnexpectedBackendResponseError) case unexpectedBackendResponseError = 12
@objc(RCReceiptInUseByOtherSubscriberError) case receiptInUseByOtherSubscriberError = 13
@objc(RCInvalidAppUserIdError) case invalidAppUserIdError = 14
2021-08-18 14:27:28 -07:00
@objc(RCOperationAlreadyInProgressForProductError) case operationAlreadyInProgressForProductError = 15
2021-07-14 12:57:51 -07:00
@objc(RCUnknownBackendError) case unknownBackendError = 16
@objc(RCInvalidAppleSubscriptionKeyError) case invalidAppleSubscriptionKeyError = 17
@objc(RCIneligibleError) case ineligibleError = 18
@objc(RCInsufficientPermissionsError) case insufficientPermissionsError = 19
@objc(RCPaymentPendingError) case paymentPendingError = 20
@objc(RCInvalidSubscriberAttributesError) case invalidSubscriberAttributesError = 21
@objc(RCLogOutAnonymousUserError) case logOutAnonymousUserError = 22
@objc(RCConfigurationError) case configurationError = 23
@objc(RCUnsupportedError) case unsupportedError = 24
@objc(RCEmptySubscriberAttributesError) case emptySubscriberAttributes = 25
@objc(RCProductDiscountMissingIdentifierError) case productDiscountMissingIdentifierError = 26
2021-08-18 14:27:28 -07:00
@objc(RCProductDiscountMissingSubscriptionGroupIdentifierError)
case productDiscountMissingSubscriptionGroupIdentifierError = 28
@objc(RCCustomerInfoError) case customerInfoError = 29
@objc(RCSystemInfoError) case systemInfoError = 30
@objc(RCBeginRefundRequestError)case beginRefundRequestError = 31
@objc(RCProductRequestTimedOut) case productRequestTimedOut = 32
@objc(RCAPIEndpointBlocked) case apiEndpointBlockedError = 33
@objc(RCInvalidPromotionalOfferError) case invalidPromotionalOfferError = 34
@objc(RCOfflineConnectionError) case offlineConnectionError = 35
2021-07-14 12:57:51 -07:00
// swiftlint:enable missing_docs
2022-06-14 12:39:41 -07:00
}
extension ErrorCode {
/**
* When an ErrorCode has been deprecated and then removed, add it to the reserved list so that we do not
* accidentally reuse it. For example:
* `@objc(RCMissingAppUserIDForAliasCreationError) case missingAppUserIDForAliasCreationError = 27` was removed,
* so we add its rawValue of `27` to the `reservedRawValues` array. That way our unit tests will catch if we
* accidentally add `27` back into the enumeration.
*/
static var reservedRawValues: Set<RawValue> {
return [27]
}
2021-07-14 12:57:51 -07:00
}
2021-07-22 11:52:43 +02:00
2021-09-17 14:06:52 -06:00
extension ErrorCode: CaseIterable { }
extension ErrorCode: DescribableError {
2021-07-22 11:52:43 +02:00
// swiftlint:disable:next missing_docs
public var description: String {
2021-07-22 11:52:43 +02:00
switch self {
case .networkError:
return "Error performing request."
case .unknownError:
return "Unknown error."
case .purchaseCancelledError:
return "Purchase was cancelled."
case .storeProblemError:
#if os(macOS) || targetEnvironment(macCatalyst)
// See https://github.com/RevenueCat/purchases-ios/issues/370
return "There was a problem with the App Store. This could also indicate the purchase dialog was cancelled."
#else
2021-07-22 11:52:43 +02:00
return "There was a problem with the App Store."
#endif
2021-07-22 11:52:43 +02:00
case .purchaseNotAllowedError:
return "The device or user is not allowed to make the purchase."
case .purchaseInvalidError:
return "One or more of the arguments provided are invalid."
case .productNotAvailableForPurchaseError:
return "The product is not available for purchase."
case .productAlreadyPurchasedError:
return "This product is already active for the user."
case .receiptAlreadyInUseError:
return "There is already another active subscriber using the same receipt."
case .missingReceiptFileError:
return "The receipt is missing."
case .invalidCredentialsError:
return "There was a credentials issue. Check the underlying error for more details."
case .unexpectedBackendResponseError:
return "Received malformed response from the backend."
case .invalidReceiptError:
return "The receipt is not valid."
case .invalidAppUserIdError:
return "The app user id is not valid."
2021-08-18 14:27:28 -07:00
case .operationAlreadyInProgressForProductError:
return "The operation is already in progress for this product."
2021-07-22 11:52:43 +02:00
case .unknownBackendError:
return "There was an unknown backend error."
case .receiptInUseByOtherSubscriberError:
return "The receipt is in use by other subscriber."
case .invalidAppleSubscriptionKeyError:
return """
Apple Subscription Key is invalid or not present. In order to provide subscription offers, you must
first generate a subscription key.
Please see https://docs.revenuecat.com/docs/ios-subscription-offers for more info.
"""
case .ineligibleError:
return "The User is ineligible for that action."
case .insufficientPermissionsError:
return "App does not have sufficient permissions to make purchases"
case .paymentPendingError:
return "The payment is pending."
case .invalidSubscriberAttributesError:
return "One or more of the attributes sent could not be saved."
case .logOutAnonymousUserError:
return "LogOut was called but the current user is anonymous."
case .configurationError:
return "There is an issue with your configuration. Check the underlying error for more details."
case .unsupportedError:
return """
There was a problem with the operation. Looks like we doesn't support that yet.
Check the underlying error for more details.
"""
2021-08-06 14:28:11 -07:00
case .emptySubscriberAttributes:
return "A request for subscriber attributes returned none."
2021-08-17 20:24:10 -03:00
case .productDiscountMissingIdentifierError:
return """
The SKProductDiscount or Product.SubscriptionOffer wrapped
by StoreProductDiscount is missing an identifier.
This is a required property and likely an AppStore quirk that it is missing.
"""
2021-08-18 14:27:28 -07:00
case .productDiscountMissingSubscriptionGroupIdentifierError:
return "Unable to create a discount offer, the product is missing a subscriptionGroupIdentifier."
2021-11-22 12:37:33 -03:00
case .customerInfoError:
return "There was a problem related to the customer info."
case .systemInfoError:
return "There was a problem related to the system info."
case .beginRefundRequestError:
return "Error when trying to begin refund request."
case .productRequestTimedOut:
return "SKProductsRequest took too long to complete."
case .apiEndpointBlockedError:
return "Requests to RevenueCat are being blocked. See: https://rev.cat/dnsBlocking for more info."
case .invalidPromotionalOfferError:
return """
The information associated with this PromotionalOffer is not valid.
See https://rev.cat/ios-subscription-offers for more info.
"""
case .offlineConnectionError:
return "Error performing request because the internet connection appears to be offline."
2021-07-22 11:52:43 +02:00
@unknown default:
return "Something went wrong."
}
}
}
extension ErrorCode: CustomNSError {
// swiftlint:disable missing_docs
public var errorUserInfo: [String: Any] {
return [
NSDebugDescriptionErrorKey: self.description,
"rc_code_name": self.codeName
]
}
2021-12-02 11:38:14 -08:00
}
extension ErrorCode {
2021-07-22 11:52:43 +02:00
/**
* The error short string, based on the error code.
*/
var codeName: String {
switch self {
case .networkError:
return "NETWORK_ERROR"
case .unknownError:
return "UNKNOWN"
case .purchaseCancelledError:
return "PURCHASE_CANCELLED"
case .storeProblemError:
return "STORE_PROBLEM"
case .purchaseNotAllowedError:
return "PURCHASE_NOT_ALLOWED"
case .purchaseInvalidError:
return "PURCHASE_INVALID"
case .productNotAvailableForPurchaseError:
return "PRODUCT_NOT_AVAILABLE_FOR_PURCHASE"
case .productAlreadyPurchasedError:
return "PRODUCT_ALREADY_PURCHASED"
case .receiptAlreadyInUseError:
return "RECEIPT_ALREADY_IN_USE"
case .missingReceiptFileError:
return "MISSING_RECEIPT_FILE"
case .invalidCredentialsError:
return "INVALID_CREDENTIALS"
case .unexpectedBackendResponseError:
return "UNEXPECTED_BACKEND_RESPONSE_ERROR"
case .invalidReceiptError:
return "INVALID_RECEIPT"
case .invalidAppUserIdError:
return "INVALID_APP_USER_ID"
2021-08-18 14:27:28 -07:00
case .operationAlreadyInProgressForProductError:
return "OPERATION_ALREADY_IN_PROGRESS_FOR_PRODUCT_ERROR"
2021-07-22 11:52:43 +02:00
case .unknownBackendError:
return "UNKNOWN_BACKEND_ERROR"
case .receiptInUseByOtherSubscriberError:
return "RECEIPT_IN_USE_BY_OTHER_SUBSCRIBER"
case .invalidAppleSubscriptionKeyError:
return "INVALID_APPLE_SUBSCRIPTION_KEY"
case .ineligibleError:
return "INELIGIBLE_ERROR"
case .insufficientPermissionsError:
return "INSUFFICIENT_PERMISSIONS_ERROR"
case .paymentPendingError:
return "PAYMENT_PENDING_ERROR"
case .invalidSubscriberAttributesError:
return "INVALID_SUBSCRIBER_ATTRIBUTES"
case .logOutAnonymousUserError:
return "LOGOUT_CALLED_WITH_ANONYMOUS_USER"
case .configurationError:
return "CONFIGURATION_ERROR"
case .unsupportedError:
return "UNSUPPORTED_ERROR"
2021-08-06 14:28:11 -07:00
case .emptySubscriberAttributes:
return "EMPTY_SUBSCRIBER_ATTRIBUTES"
2021-08-17 20:24:10 -03:00
case .productDiscountMissingIdentifierError:
return "PRODUCT_DISCOUNT_MISSING_IDENTIFIER_ERROR"
2021-08-18 14:27:28 -07:00
case .productDiscountMissingSubscriptionGroupIdentifierError:
return "PRODUCT_DISCOUNT_MISSING_SUBSCRIPTION_GROUP_IDENTIFIER_ERROR"
2021-11-22 12:37:33 -03:00
case .customerInfoError:
return "CUSTOMER_INFO_ERROR"
case .systemInfoError:
return "SYSTEM_INFO_ERROR"
case .beginRefundRequestError:
return "BEGIN_REFUND_REQUEST_ERROR"
case .productRequestTimedOut:
return "PRODUCT_REQUEST_TIMED_OUT_ERROR"
case .apiEndpointBlockedError:
return "API_ENDPOINT_BLOCKED_ERROR"
case .invalidPromotionalOfferError:
return "INVALID_PROMOTIONAL_OFFER_ERROR"
case .offlineConnectionError:
return "OFFLINE_CONNECTION_ERROR"
2021-07-22 11:52:43 +02:00
@unknown default:
return "UNRECOGNIZED_ERROR"
}
}
}
/// An `Error` that can be converted into an ``ErrorCode``
/// - Note: the return value is ``Error`` since that allows errors
/// to be encoded into `NSError` to include additional context.
protocol ErrorCodeConvertible: Swift.Error {
/// Convert the receiver into an `ErrorCode` with all the necessary context
/// ### Related symbols:
/// - ``ErrorUtils``
var asPurchasesError: Error { get }
}
extension ErrorCodeConvertible {
var description: String {
return self.asPurchasesError.localizedDescription
}
}