2021-07-22 11:52:43 +02: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
|
|
|
|
|
//
|
|
|
|
|
// RCPurchasesErrorUtils.swift
|
|
|
|
|
//
|
|
|
|
|
// Created by Cesar de la Vega on 7/21/21.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
|
import StoreKit
|
|
|
|
|
|
2022-08-02 11:19:32 -07:00
|
|
|
// swiftlint:disable file_length multiline_parameters type_body_length
|
2021-12-15 15:33:44 -08:00
|
|
|
|
|
|
|
|
enum ErrorUtils {
|
2021-07-22 11:52:43 +02:00
|
|
|
|
|
|
|
|
/**
|
2021-08-30 13:38:21 -07:00
|
|
|
* Constructs an NSError with the ``ErrorCode/networkError`` code and a populated `NSUnderlyingErrorKey` in
|
|
|
|
|
* the `NSError.userInfo` dictionary.
|
2021-07-22 11:52:43 +02:00
|
|
|
*
|
2021-08-30 13:38:21 -07:00
|
|
|
* - Parameter underlyingError: The value of the `NSUnderlyingErrorKey` key.
|
2021-07-22 11:52:43 +02:00
|
|
|
*
|
2021-08-25 16:08:49 -07:00
|
|
|
* - Note: This error is used when there is an error performing network request returns an error or when there
|
2021-08-30 13:38:21 -07:00
|
|
|
* is an `NSJSONSerialization` error.
|
2021-07-22 11:52:43 +02:00
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func networkError(
|
2022-04-12 15:11:22 -07:00
|
|
|
message: String? = nil,
|
|
|
|
|
withUnderlyingError underlyingError: Error? = nil,
|
2022-11-11 08:02:28 -08:00
|
|
|
extraUserInfo: [NSError.UserInfoKey: Any] = [:],
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2022-01-12 14:44:43 -08:00
|
|
|
|
|
|
|
|
let errorCode: ErrorCode
|
2022-04-12 15:11:22 -07:00
|
|
|
if case NetworkError.dnsError(_, _, _)? = underlyingError {
|
2022-01-12 14:44:43 -08:00
|
|
|
errorCode = .apiEndpointBlockedError
|
|
|
|
|
} else {
|
|
|
|
|
errorCode = .networkError
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return error(with: errorCode,
|
2023-02-08 10:38:52 -08:00
|
|
|
message: message ?? underlyingError?.localizedDescription,
|
2022-01-12 14:44:43 -08:00
|
|
|
underlyingError: underlyingError,
|
2022-04-12 15:11:22 -07:00
|
|
|
extraUserInfo: extraUserInfo,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
|
|
|
|
|
2022-04-15 15:54:18 +02:00
|
|
|
/**
|
|
|
|
|
* Constructs an NSError with the ``ErrorCode/offlineConnection`` code.
|
|
|
|
|
*/
|
|
|
|
|
static func offlineConnectionError(
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2022-04-15 15:54:18 +02:00
|
|
|
return error(with: .offlineConnectionError,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-16 03:06:59 -08:00
|
|
|
/**
|
|
|
|
|
* Constructs an NSError with the ``ErrorCode/signatureVerificationFailed`` code.
|
|
|
|
|
*/
|
|
|
|
|
static func signatureVerificationFailedError(
|
2023-08-10 15:23:13 -07:00
|
|
|
path: String,
|
2023-06-16 07:55:21 -07:00
|
|
|
code: HTTPStatusCode,
|
2023-02-16 03:06:59 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
|
|
|
|
) -> PurchasesError {
|
|
|
|
|
return error(
|
|
|
|
|
with: .signatureVerificationFailed,
|
2023-08-10 15:23:13 -07:00
|
|
|
message: "Request to '\(path)' failed verification",
|
2023-02-16 03:06:59 -08:00
|
|
|
extraUserInfo: [
|
2023-08-10 15:23:13 -07:00
|
|
|
"request_path": path,
|
2023-06-16 07:55:21 -07:00
|
|
|
"status_code": code.rawValue
|
2023-02-16 03:06:59 -08:00
|
|
|
],
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-22 11:52:43 +02:00
|
|
|
/**
|
2021-11-22 12:37:33 -03:00
|
|
|
* Maps a ``BackendErrorCode`` code to a ``ErrorCode``. code. Constructs an Error with the mapped code and adds a
|
2021-08-30 13:38:21 -07:00
|
|
|
* `NSUnderlyingErrorKey` in the `NSError.userInfo` dictionary. The backend error code will be mapped using
|
|
|
|
|
* ``BackendErrorCode/toPurchasesErrorCode()``.
|
2021-07-22 11:52:43 +02:00
|
|
|
*
|
2022-11-11 08:02:28 -08:00
|
|
|
* - Parameter backendCode: The code of the error.
|
|
|
|
|
* - Parameter originalBackendErrorCode: the original numerical value of this error.
|
2021-08-30 13:38:21 -07:00
|
|
|
* - Parameter backendMessage: The message of the errror contained under the `NSUnderlyingErrorKey` key.
|
2021-07-22 11:52:43 +02:00
|
|
|
*
|
2021-08-25 16:08:49 -07:00
|
|
|
* - Note: This error is used when an network request returns an error. The backend error returned is wrapped in
|
2021-07-22 11:52:43 +02:00
|
|
|
* this internal error code.
|
|
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func backendError(
|
2022-11-11 08:02:28 -08:00
|
|
|
withBackendCode backendCode: BackendErrorCode,
|
|
|
|
|
originalBackendErrorCode: Int,
|
|
|
|
|
backendMessage: String?,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2022-11-11 08:02:28 -08:00
|
|
|
return backendError(withBackendCode: backendCode,
|
|
|
|
|
originalBackendErrorCode: originalBackendErrorCode,
|
|
|
|
|
backendMessage: backendMessage,
|
|
|
|
|
extraUserInfo: [:],
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-30 13:38:21 -07:00
|
|
|
* Constructs an Error with the ``ErrorCode/unexpectedBackendResponseError`` code.
|
2021-07-22 11:52:43 +02:00
|
|
|
*
|
2021-10-27 08:37:17 -07:00
|
|
|
* - Note: This error is used when a network request returns an unexpected response.
|
2021-07-22 11:52:43 +02:00
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func unexpectedBackendResponseError(
|
2022-11-11 08:02:28 -08:00
|
|
|
extraUserInfo: [NSError.UserInfoKey: Any] = [:],
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-12-15 15:33:44 -08:00
|
|
|
return error(with: ErrorCode.unexpectedBackendResponseError, extraUserInfo: extraUserInfo,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-27 08:37:17 -07:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/unexpectedBackendResponseError`` code which contains an underlying
|
|
|
|
|
* ``UnexpectedBackendResponseSubErrorCode``
|
|
|
|
|
*
|
|
|
|
|
* - Note: This error is used when a network request returns an unexpected response and we can determine some
|
|
|
|
|
* of what went wrong with the response.
|
|
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func unexpectedBackendResponse(
|
2022-01-31 10:48:05 -03:00
|
|
|
withSubError subError: Error?,
|
|
|
|
|
extraContext: String? = nil,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2022-01-31 10:48:05 -03:00
|
|
|
return backendResponseError(withSubError: subError,
|
|
|
|
|
extraContext: extraContext,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-10-27 08:37:17 -07:00
|
|
|
}
|
|
|
|
|
|
2021-07-22 11:52:43 +02:00
|
|
|
/**
|
2021-08-30 13:38:21 -07:00
|
|
|
* Constructs an Error with the ``ErrorCode/missingReceiptFileError`` code.
|
2021-07-22 11:52:43 +02:00
|
|
|
*
|
2021-08-25 16:08:49 -07:00
|
|
|
* - Note: This error is used when the receipt is missing in the device. This can happen if the user is in
|
|
|
|
|
* sandbox or if there are no previous purchases.
|
2021-07-22 11:52:43 +02:00
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func missingReceiptFileError(
|
2023-06-14 12:36:31 -07:00
|
|
|
_ receiptURL: URL?,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2023-06-14 12:36:31 -07:00
|
|
|
let fileExists: Bool = {
|
|
|
|
|
if let receiptURL = receiptURL {
|
|
|
|
|
return FileManager.default.fileExists(atPath: receiptURL.path)
|
|
|
|
|
} else {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
2021-12-15 15:33:44 -08:00
|
|
|
return error(with: ErrorCode.missingReceiptFileError,
|
2023-06-14 12:36:31 -07:00
|
|
|
extraUserInfo: [
|
|
|
|
|
"rc_receipt_url": receiptURL?.absoluteString ?? "<null>",
|
|
|
|
|
"rc_receipt_file_exists": fileExists
|
|
|
|
|
],
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-05 13:46:07 -07:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/emptySubscriberAttributes`` code.
|
|
|
|
|
*/
|
|
|
|
|
static func emptySubscriberAttributesError(
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
|
|
|
|
) -> PurchasesError {
|
|
|
|
|
return error(with: .emptySubscriberAttributes,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-22 11:52:43 +02:00
|
|
|
/**
|
2021-08-30 13:38:21 -07:00
|
|
|
* Constructs an Error with the ``ErrorCode/invalidAppUserIdError`` code.
|
2021-07-22 11:52:43 +02:00
|
|
|
*
|
2021-08-25 16:08:49 -07:00
|
|
|
* - Note: This error is used when the appUserID can't be found in user defaults. This can happen if user defaults
|
2021-07-22 11:52:43 +02:00
|
|
|
* are removed manually or if the OS deletes entries when running out of space.
|
|
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func missingAppUserIDError(
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-12-15 15:33:44 -08:00
|
|
|
return error(with: ErrorCode.invalidAppUserIdError,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-08-17 20:24:10 -03:00
|
|
|
}
|
2021-07-22 11:52:43 +02:00
|
|
|
|
2021-08-17 20:24:10 -03:00
|
|
|
/**
|
2021-08-30 13:38:21 -07:00
|
|
|
* Constructs an Error with the ``ErrorCode/productDiscountMissingIdentifierError`` code.
|
2021-08-17 20:24:10 -03:00
|
|
|
*
|
2021-08-25 16:08:49 -07:00
|
|
|
* - Note: This error code is used when attemping to post data about product discounts but the discount is
|
2021-08-17 20:24:10 -03:00
|
|
|
* missing an indentifier.
|
|
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func productDiscountMissingIdentifierError(
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-12-15 15:33:44 -08:00
|
|
|
return error(with: ErrorCode.productDiscountMissingIdentifierError,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-18 14:27:28 -07:00
|
|
|
/**
|
2021-08-30 13:38:21 -07:00
|
|
|
* Constructs an Error with the ``ErrorCode/productDiscountMissingSubscriptionGroupIdentifierError`` code.
|
2021-08-18 14:27:28 -07:00
|
|
|
*
|
2021-08-25 16:08:49 -07:00
|
|
|
* - Note: This error code is used when attemping to post data about product discounts but the discount is
|
2021-08-18 14:27:28 -07:00
|
|
|
* missing a subscriptionGroupIndentifier.
|
|
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func productDiscountMissingSubscriptionGroupIdentifierError(
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-12-15 15:33:44 -08:00
|
|
|
return error(with: ErrorCode.productDiscountMissingSubscriptionGroupIdentifierError,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-08-18 14:27:28 -07:00
|
|
|
}
|
|
|
|
|
|
2021-07-22 11:52:43 +02:00
|
|
|
/**
|
2021-08-30 13:38:21 -07:00
|
|
|
* Constructs an Error with the ``ErrorCode/logOutAnonymousUserError`` code.
|
2021-07-22 11:52:43 +02:00
|
|
|
*
|
2021-08-25 16:08:49 -07:00
|
|
|
* - Note: This error is used when logOut is called but the current user is anonymous,
|
2021-08-30 13:38:21 -07:00
|
|
|
* as noted by ``Purchases/isAnonymous`` property.
|
2021-07-22 11:52:43 +02:00
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func logOutAnonymousUserError(
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-12-15 15:33:44 -08:00
|
|
|
return error(with: ErrorCode.logOutAnonymousUserError,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-30 13:38:21 -07:00
|
|
|
* Constructs an Error with the ``ErrorCode/paymentPendingError`` code.
|
2021-07-22 11:52:43 +02:00
|
|
|
*
|
2021-08-25 16:08:49 -07:00
|
|
|
* - Note: This error is used during an “ask to buy” flow for a payment. The completion block of the purchasing
|
|
|
|
|
* function will get this error to indicate the guardian has to complete the purchase.
|
2021-07-22 11:52:43 +02:00
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func paymentDeferredError(
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-12-15 15:33:44 -08:00
|
|
|
return error(with: ErrorCode.paymentPendingError, message: "The payment is deferred.",
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-08-17 20:24:10 -03:00
|
|
|
}
|
2021-07-22 11:52:43 +02:00
|
|
|
|
2021-08-17 20:24:10 -03:00
|
|
|
/**
|
2021-08-30 13:38:21 -07:00
|
|
|
* Constructs an Error with the ``ErrorCode/unknownError`` code and optional message.
|
2021-08-17 20:24:10 -03:00
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func unknownError(
|
2021-12-15 16:05:18 -08:00
|
|
|
message: String? = nil, error: Error? = nil,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-12-15 16:05:18 -08:00
|
|
|
return ErrorUtils.error(with: ErrorCode.unknownError, message: message, underlyingError: error,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-08-17 20:24:10 -03:00
|
|
|
}
|
2021-07-22 11:52:43 +02:00
|
|
|
|
2021-08-17 20:24:10 -03:00
|
|
|
/**
|
2021-08-30 13:38:21 -07:00
|
|
|
* Constructs an Error with the ``ErrorCode/operationAlreadyInProgressForProductError`` code.
|
2021-08-17 20:24:10 -03:00
|
|
|
*
|
2021-08-25 16:08:49 -07:00
|
|
|
* - Note: This error is used when a purchase is initiated for a product, but there's already a purchase for the
|
|
|
|
|
* same product in progress.
|
2021-08-17 20:24:10 -03:00
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func operationAlreadyInProgressError(
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-12-15 15:33:44 -08:00
|
|
|
return error(with: ErrorCode.operationAlreadyInProgressForProductError,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-29 18:27:38 -03:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/configurationError`` code.
|
|
|
|
|
*
|
|
|
|
|
* - Note: This error is used when the configuration in App Store Connect doesn't match the configuration
|
|
|
|
|
* in the RevenueCat dashboard.
|
|
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func configurationError(
|
|
|
|
|
message: String? = nil,
|
2022-08-02 11:19:32 -07:00
|
|
|
underlyingError: Error? = nil,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
|
|
|
|
return self.error(with: ErrorCode.configurationError,
|
|
|
|
|
message: message, underlyingError: underlyingError,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-09-29 18:27:38 -03:00
|
|
|
}
|
|
|
|
|
|
2021-07-22 11:52:43 +02:00
|
|
|
/**
|
2022-04-05 11:54:01 -07:00
|
|
|
* Maps an `SKError` to a Error with an ``ErrorCode``. Adds a underlying error in the `NSError.userInfo` dictionary.
|
2021-07-22 11:52:43 +02:00
|
|
|
*
|
2021-08-30 13:38:21 -07:00
|
|
|
* - Parameter skError: The originating `SKError`.
|
2021-07-22 11:52:43 +02:00
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func purchasesError(
|
2022-04-05 11:54:01 -07:00
|
|
|
withSKError error: Error,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2022-04-05 11:54:01 -07:00
|
|
|
switch error {
|
|
|
|
|
case let skError as SKError:
|
2022-04-12 15:13:28 -07:00
|
|
|
return skError.asPurchasesError
|
2023-06-22 06:54:02 -07:00
|
|
|
case let purchasesError as PurchasesError:
|
|
|
|
|
return purchasesError
|
2023-10-25 20:25:09 -07:00
|
|
|
case is URLError:
|
|
|
|
|
// Some StoreKit APIs can return `URLError`s.
|
|
|
|
|
// See https://github.com/RevenueCat/purchases-ios/issues/3343
|
|
|
|
|
return NetworkError.networkError(
|
|
|
|
|
error as NSError,
|
|
|
|
|
.init(file: fileName,
|
|
|
|
|
function: functionName,
|
|
|
|
|
line: line)
|
|
|
|
|
).asPurchasesError
|
2022-04-05 11:54:01 -07:00
|
|
|
default:
|
|
|
|
|
return ErrorUtils.unknownError(
|
|
|
|
|
error: error,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line
|
|
|
|
|
)
|
|
|
|
|
}
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
|
|
|
|
|
2021-12-15 16:05:18 -08:00
|
|
|
/**
|
2022-03-10 11:57:23 -08:00
|
|
|
* Maps a `StoreKitError` or `Product.PurchaseError` to an `Error` with an ``ErrorCode``.
|
2021-12-15 16:05:18 -08:00
|
|
|
* Adds a underlying error in the `NSError.userInfo` dictionary.
|
|
|
|
|
*
|
2022-03-10 11:57:23 -08:00
|
|
|
* - Parameter storeKitError: The originating `StoreKitError` or `Product.PurchaseError`.
|
2021-12-15 16:05:18 -08:00
|
|
|
*/
|
|
|
|
|
@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
|
2022-04-05 11:54:01 -07:00
|
|
|
static func purchasesError(
|
|
|
|
|
withStoreKitError error: Error,
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2022-03-10 11:57:23 -08:00
|
|
|
switch error {
|
|
|
|
|
case let storeKitError as StoreKitError:
|
2022-04-12 15:13:28 -07:00
|
|
|
return storeKitError.asPurchasesError
|
2023-06-22 06:54:02 -07:00
|
|
|
case let purchaseError as Product.PurchaseError:
|
|
|
|
|
return purchaseError.asPurchasesError
|
|
|
|
|
case let purchasesError as PurchasesError:
|
|
|
|
|
return purchasesError
|
2022-03-10 11:57:23 -08:00
|
|
|
default:
|
2022-04-05 11:54:01 -07:00
|
|
|
return ErrorUtils.unknownError(
|
|
|
|
|
error: error,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line
|
|
|
|
|
)
|
2022-03-10 11:57:23 -08:00
|
|
|
}
|
2021-12-15 16:05:18 -08:00
|
|
|
}
|
|
|
|
|
|
2022-09-05 13:46:07 -07:00
|
|
|
/**
|
|
|
|
|
* Maps an untyped `Error` into a `PurchasesError`.
|
|
|
|
|
* If the error is already a `PurchasesError`, this simply returns the same value,
|
|
|
|
|
* otherwise it wraps it into a ``ErrorCode/unknownError``.
|
|
|
|
|
*/
|
|
|
|
|
static func purchasesError(
|
|
|
|
|
withUntypedError error: Error,
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
|
|
|
|
) -> PurchasesError {
|
2022-12-21 08:47:47 -08:00
|
|
|
func handlePublicError(_ error: PublicError) -> PurchasesError {
|
|
|
|
|
if let errorCode = ErrorCode(rawValue: error.code) {
|
|
|
|
|
return .init(error: errorCode, userInfo: error.userInfo)
|
|
|
|
|
} else {
|
|
|
|
|
return createUnknownError()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func createUnknownError() -> PurchasesError {
|
|
|
|
|
ErrorUtils.unknownError(
|
|
|
|
|
error: error,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-01 12:37:21 -07:00
|
|
|
switch error {
|
|
|
|
|
case let purchasesError as PurchasesError:
|
|
|
|
|
return purchasesError
|
|
|
|
|
case let convertible as PurchasesErrorConvertible:
|
|
|
|
|
return convertible.asPurchasesError
|
|
|
|
|
case let error as PublicError where error.domain == ErrorCode.errorDomain:
|
|
|
|
|
return handlePublicError(error)
|
|
|
|
|
default:
|
|
|
|
|
return createUnknownError()
|
2022-09-05 13:46:07 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-22 12:37:33 -03:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/purchaseCancelledError`` code.
|
|
|
|
|
*
|
|
|
|
|
* - Note: This error is used when a purchase is cancelled by the user.
|
|
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func purchaseCancelledError(
|
2022-04-05 11:54:01 -07:00
|
|
|
error: Error? = nil,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-11-22 12:37:33 -03:00
|
|
|
let errorCode = ErrorCode.purchaseCancelledError
|
|
|
|
|
return ErrorUtils.error(with: errorCode,
|
|
|
|
|
message: errorCode.description,
|
2022-04-05 11:54:01 -07:00
|
|
|
underlyingError: error,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-11-22 12:37:33 -03:00
|
|
|
}
|
2021-07-22 11:52:43 +02:00
|
|
|
|
2021-12-15 16:05:18 -08:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/productNotAvailableForPurchaseError`` code.
|
|
|
|
|
*
|
2022-02-03 10:41:31 -03:00
|
|
|
* #### Related Articles
|
|
|
|
|
* - [`StoreKitError.notAvailableInStorefront`](https://rev.cat/storekit-error-not-available-in-storefront)
|
2021-12-15 16:05:18 -08:00
|
|
|
*/
|
|
|
|
|
static func productNotAvailableForPurchaseError(
|
|
|
|
|
error: Error? = nil,
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-12-15 16:05:18 -08:00
|
|
|
return ErrorUtils.error(with: .productNotAvailableForPurchaseError,
|
|
|
|
|
underlyingError: error)
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-05 11:54:01 -07:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/productAlreadyPurchasedError`` code.
|
|
|
|
|
*/
|
|
|
|
|
static func productAlreadyPurchasedError(
|
|
|
|
|
error: Error? = nil,
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2022-04-05 11:54:01 -07:00
|
|
|
return ErrorUtils.error(with: .productAlreadyPurchasedError,
|
|
|
|
|
underlyingError: error)
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-10 11:57:23 -08:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/purchaseNotAllowedError`` code.
|
|
|
|
|
*/
|
|
|
|
|
static func purchaseNotAllowedError(
|
|
|
|
|
error: Error? = nil,
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2022-03-10 11:57:23 -08:00
|
|
|
return ErrorUtils.error(with: .purchaseNotAllowedError,
|
|
|
|
|
underlyingError: error)
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-05 11:54:01 -07:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/purchaseInvalidError`` code.
|
|
|
|
|
*/
|
|
|
|
|
static func purchaseInvalidError(
|
|
|
|
|
error: Error? = nil,
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2022-04-05 11:54:01 -07:00
|
|
|
return ErrorUtils.error(with: .purchaseInvalidError,
|
|
|
|
|
underlyingError: error)
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-10 11:57:23 -08:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/ineligibleError`` code.
|
|
|
|
|
*/
|
|
|
|
|
static func ineligibleError(
|
|
|
|
|
error: Error? = nil,
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2022-03-10 11:57:23 -08:00
|
|
|
return ErrorUtils.error(with: .ineligibleError,
|
|
|
|
|
underlyingError: error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/ineligibleError`` code.
|
|
|
|
|
*/
|
|
|
|
|
static func invalidPromotionalOfferError(
|
|
|
|
|
error: Error? = nil,
|
2022-12-09 15:04:29 -08:00
|
|
|
message: String? = nil,
|
2022-03-10 11:57:23 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2022-03-10 11:57:23 -08:00
|
|
|
return ErrorUtils.error(with: .invalidPromotionalOfferError,
|
2022-12-09 15:04:29 -08:00
|
|
|
message: message,
|
2022-03-10 11:57:23 -08:00
|
|
|
underlyingError: error)
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-22 12:37:33 -03:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/storeProblemError`` code.
|
|
|
|
|
*
|
|
|
|
|
* - Note: This error is used when there is a problem with the App Store.
|
|
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func storeProblemError(
|
2021-12-15 16:05:18 -08:00
|
|
|
withMessage message: String? = nil, error: Error? = nil,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-11-22 12:37:33 -03:00
|
|
|
let errorCode = ErrorCode.storeProblemError
|
|
|
|
|
return ErrorUtils.error(with: errorCode,
|
|
|
|
|
message: message,
|
2021-12-15 15:33:44 -08:00
|
|
|
underlyingError: error,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-11-22 12:37:33 -03:00
|
|
|
}
|
2021-07-22 11:52:43 +02:00
|
|
|
|
2021-11-22 12:37:33 -03:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/customerInfoError`` code.
|
|
|
|
|
*
|
|
|
|
|
* - Note: This error is used when there is a problem related to the customer info.
|
|
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func customerInfoError(
|
2022-05-24 14:22:11 -07:00
|
|
|
withMessage message: String? = nil, error: Error? = nil,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-11-22 12:37:33 -03:00
|
|
|
let errorCode = ErrorCode.customerInfoError
|
|
|
|
|
return ErrorUtils.error(with: errorCode,
|
|
|
|
|
message: message,
|
2021-12-15 15:33:44 -08:00
|
|
|
underlyingError: error,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-11-22 12:37:33 -03:00
|
|
|
}
|
2021-11-18 08:23:33 -08:00
|
|
|
|
2021-11-22 12:37:33 -03:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/systemInfoError`` code.
|
|
|
|
|
*
|
|
|
|
|
* - Note: This error is used when there is a problem related to the system info.
|
|
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func systemInfoError(
|
|
|
|
|
withMessage message: String, error: Error? = nil,
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-11-22 12:37:33 -03:00
|
|
|
let errorCode = ErrorCode.systemInfoError
|
|
|
|
|
return ErrorUtils.error(with: errorCode,
|
|
|
|
|
message: message,
|
2021-12-15 15:33:44 -08:00
|
|
|
underlyingError: error,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-11-22 12:37:33 -03:00
|
|
|
}
|
2021-11-18 08:23:33 -08:00
|
|
|
|
2021-11-22 12:37:33 -03:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/beginRefundRequestError`` code.
|
|
|
|
|
*
|
|
|
|
|
* - Note: This error is used when there is a problem beginning a refund request.
|
|
|
|
|
*/
|
2021-12-15 15:33:44 -08:00
|
|
|
static func beginRefundRequestError(
|
|
|
|
|
withMessage message: String, error: Error? = nil,
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-11-22 12:37:33 -03:00
|
|
|
let errorCode = ErrorCode.beginRefundRequestError
|
|
|
|
|
return ErrorUtils.error(with: errorCode,
|
|
|
|
|
message: message,
|
2021-12-15 15:33:44 -08:00
|
|
|
underlyingError: error,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
|
|
|
|
|
2021-12-15 15:42:53 -08:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/productRequestTimedOut`` code.
|
|
|
|
|
*
|
|
|
|
|
* - Note: This error is used when fetching products times out.
|
|
|
|
|
*/
|
|
|
|
|
static func productRequestTimedOutError(
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-12-15 15:42:53 -08:00
|
|
|
return ErrorUtils.error(with: .productRequestTimedOut,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-25 17:21:46 -03:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/featureNotAvailableInCustomEntitlementsComputationMode`` code.
|
|
|
|
|
*
|
|
|
|
|
* - Note: This error is used when trying to use a feature that isn't supported
|
|
|
|
|
* in customEntitlementsComputation mode
|
|
|
|
|
* and the ``DangerousSettings/customEntitlementsComputation`` flag is set to true.
|
|
|
|
|
*/
|
|
|
|
|
static func featureNotAvailableInCustomEntitlementsComputationModeError(
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
|
|
|
|
) -> PurchasesError {
|
|
|
|
|
return ErrorUtils.error(with: .featureNotAvailableInCustomEntitlementsComputationMode,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-06 15:46:04 -06:00
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/featureNotSupportedWithStoreKit1`` code.
|
|
|
|
|
*
|
|
|
|
|
* - Note: This error is used when trying to use a feature that isn't supported
|
|
|
|
|
* by StoreKit 1 when the SDK is running in StoreKit 1 mode.
|
|
|
|
|
*/
|
|
|
|
|
static func featureNotSupportedWithStoreKit1Error(
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
|
|
|
|
) -> PurchasesError {
|
|
|
|
|
return ErrorUtils.error(with: .featureNotSupportedWithStoreKit1,
|
|
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
|
|
|
|
}
|
2025-02-20 18:27:16 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructs an Error with the ``ErrorCode/unsupportedError`` code.
|
|
|
|
|
*
|
|
|
|
|
* - Note: This error is used when trying to use a feature that isn't supported
|
|
|
|
|
* by StoreKit 1 when the SDK is running in StoreKit 1 mode.
|
|
|
|
|
*/
|
|
|
|
|
static func unsupportedInUIPreviewModeError(
|
|
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
|
|
|
|
) -> PurchasesError {
|
|
|
|
|
return ErrorUtils.error(with: .unsupportedError,
|
|
|
|
|
message: "Operation not supported in UI preview mode",
|
|
|
|
|
fileName: fileName,
|
|
|
|
|
functionName: functionName,
|
|
|
|
|
line: line)
|
|
|
|
|
}
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-24 12:56:37 -07:00
|
|
|
extension ErrorUtils {
|
2021-07-22 11:52:43 +02:00
|
|
|
|
2021-10-25 10:37:08 -07:00
|
|
|
static func backendError(withBackendCode backendCode: BackendErrorCode,
|
2022-11-11 08:02:28 -08:00
|
|
|
originalBackendErrorCode: Int,
|
2022-07-29 10:04:26 -07:00
|
|
|
message: String? = nil,
|
|
|
|
|
backendMessage: String? = nil,
|
2022-11-11 08:02:28 -08:00
|
|
|
extraUserInfo: [NSError.UserInfoKey: Any] = [:],
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-10-25 10:37:08 -07:00
|
|
|
let errorCode = backendCode.toPurchasesErrorCode()
|
2022-11-11 08:02:28 -08:00
|
|
|
let underlyingError = self.backendUnderlyingError(backendCode: backendCode,
|
|
|
|
|
originalBackendErrorCode: originalBackendErrorCode,
|
|
|
|
|
backendMessage: backendMessage)
|
2021-07-22 11:52:43 +02:00
|
|
|
|
|
|
|
|
return error(with: errorCode,
|
2022-10-11 12:56:15 -07:00
|
|
|
message: message ?? backendMessage,
|
2021-07-22 11:52:43 +02:00
|
|
|
underlyingError: underlyingError,
|
2022-11-11 08:02:28 -08:00
|
|
|
extraUserInfo: extraUserInfo + [
|
|
|
|
|
.backendErrorCode: originalBackendErrorCode
|
|
|
|
|
],
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: fileName, functionName: functionName, line: line)
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private extension ErrorUtils {
|
|
|
|
|
|
|
|
|
|
static func error(with code: ErrorCode,
|
|
|
|
|
message: String? = nil,
|
|
|
|
|
underlyingError: Error? = nil,
|
2022-11-11 08:02:28 -08:00
|
|
|
extraUserInfo: [NSError.UserInfoKey: Any] = [:],
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID,
|
|
|
|
|
functionName: String = #function,
|
2022-09-05 13:46:07 -07:00
|
|
|
line: UInt = #line) -> PurchasesError {
|
2022-10-11 12:56:15 -07:00
|
|
|
let localizedDescription: String
|
|
|
|
|
|
|
|
|
|
if let message = message, message != code.description {
|
|
|
|
|
// Print both ErrorCode and message only if they're different
|
|
|
|
|
localizedDescription = "\(code.description) \(message)"
|
|
|
|
|
} else {
|
|
|
|
|
localizedDescription = code.description
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-11 08:02:28 -08:00
|
|
|
var userInfo = extraUserInfo
|
2022-10-11 12:56:15 -07:00
|
|
|
userInfo[NSLocalizedDescriptionKey as NSError.UserInfoKey] = localizedDescription
|
2022-01-31 10:48:05 -03:00
|
|
|
if let underlyingError = underlyingError {
|
|
|
|
|
userInfo[NSUnderlyingErrorKey as NSError.UserInfoKey] = underlyingError
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
2022-04-12 15:14:15 -07:00
|
|
|
userInfo[.readableErrorCode] = code.codeName
|
|
|
|
|
userInfo[.file] = "\(fileName):\(line)"
|
|
|
|
|
userInfo[.function] = functionName
|
2021-07-22 11:52:43 +02:00
|
|
|
|
2022-04-15 15:54:18 +02:00
|
|
|
Self.logErrorIfNeeded(
|
|
|
|
|
code,
|
2022-10-11 12:56:15 -07:00
|
|
|
localizedDescription: localizedDescription,
|
2022-04-15 15:54:18 +02:00
|
|
|
fileName: fileName, functionName: functionName, line: line
|
|
|
|
|
)
|
2021-11-18 09:58:17 -08:00
|
|
|
|
2022-09-05 13:46:07 -07:00
|
|
|
return .init(error: code, userInfo: userInfo)
|
2021-10-27 08:37:17 -07:00
|
|
|
}
|
|
|
|
|
|
2021-12-15 15:33:44 -08:00
|
|
|
static func backendResponseError(
|
2022-01-31 10:48:05 -03:00
|
|
|
withSubError subError: Error?,
|
|
|
|
|
extraContext: String?,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID, functionName: String = #function, line: UInt = #line
|
2022-09-05 13:46:07 -07:00
|
|
|
) -> PurchasesError {
|
2021-10-27 08:37:17 -07:00
|
|
|
var userInfo: [NSError.UserInfoKey: Any] = [:]
|
2022-01-31 10:48:05 -03:00
|
|
|
let describableSubError = subError as? DescribableError
|
2021-11-02 09:13:16 -07:00
|
|
|
let errorDescription = describableSubError?.description ?? ErrorCode.unexpectedBackendResponseError.description
|
|
|
|
|
userInfo[NSLocalizedDescriptionKey as NSError.UserInfoKey] = errorDescription
|
2022-01-31 10:48:05 -03:00
|
|
|
userInfo[NSUnderlyingErrorKey as NSError.UserInfoKey] = subError
|
2022-04-12 15:14:15 -07:00
|
|
|
userInfo[.readableErrorCode] = ErrorCode.unexpectedBackendResponseError.codeName
|
|
|
|
|
userInfo[.extraContext] = extraContext
|
|
|
|
|
userInfo[.file] = "\(fileName):\(line)"
|
|
|
|
|
userInfo[.function] = functionName
|
2021-10-27 08:37:17 -07:00
|
|
|
|
2022-09-05 13:46:07 -07:00
|
|
|
return .init(error: .unexpectedBackendResponseError, userInfo: userInfo)
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
|
|
|
|
|
2022-11-11 08:02:28 -08:00
|
|
|
static func backendUnderlyingError(backendCode: BackendErrorCode,
|
|
|
|
|
originalBackendErrorCode: Int,
|
|
|
|
|
backendMessage: String?) -> NSError {
|
2022-09-05 13:46:07 -07:00
|
|
|
return backendCode.addingUserInfo([
|
2022-11-11 08:02:28 -08:00
|
|
|
NSLocalizedDescriptionKey as NSError.UserInfoKey: backendMessage ?? "",
|
|
|
|
|
.backendErrorCode: originalBackendErrorCode
|
2022-09-05 13:46:07 -07:00
|
|
|
])
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
2021-08-17 20:24:10 -03:00
|
|
|
|
2022-04-15 15:54:18 +02:00
|
|
|
// swiftlint:disable:next function_body_length
|
2021-12-15 15:33:44 -08:00
|
|
|
private static func logErrorIfNeeded(_ code: ErrorCode,
|
2022-10-11 12:56:15 -07:00
|
|
|
localizedDescription: String,
|
2021-12-15 15:33:44 -08:00
|
|
|
fileName: String = #fileID,
|
|
|
|
|
functionName: String = #function,
|
|
|
|
|
line: UInt = #line) {
|
2021-11-18 09:58:17 -08:00
|
|
|
switch code {
|
|
|
|
|
case .networkError,
|
|
|
|
|
.unknownError,
|
|
|
|
|
.receiptAlreadyInUseError,
|
|
|
|
|
.unexpectedBackendResponseError,
|
|
|
|
|
.invalidReceiptError,
|
|
|
|
|
.invalidAppUserIdError,
|
2022-08-17 11:26:10 -07:00
|
|
|
.invalidCredentialsError,
|
2021-11-18 09:58:17 -08:00
|
|
|
.operationAlreadyInProgressForProductError,
|
|
|
|
|
.unknownBackendError,
|
|
|
|
|
.invalidSubscriberAttributesError,
|
|
|
|
|
.logOutAnonymousUserError,
|
|
|
|
|
.receiptInUseByOtherSubscriberError,
|
|
|
|
|
.configurationError,
|
|
|
|
|
.unsupportedError,
|
|
|
|
|
.emptySubscriberAttributes,
|
|
|
|
|
.productDiscountMissingIdentifierError,
|
2021-11-22 12:37:33 -03:00
|
|
|
.productDiscountMissingSubscriptionGroupIdentifierError,
|
|
|
|
|
.customerInfoError,
|
|
|
|
|
.systemInfoError,
|
2022-01-12 14:44:43 -08:00
|
|
|
.beginRefundRequestError,
|
2022-03-10 11:57:23 -08:00
|
|
|
.apiEndpointBlockedError,
|
2022-04-15 15:54:18 +02:00
|
|
|
.invalidPromotionalOfferError,
|
2023-02-16 03:06:59 -08:00
|
|
|
.offlineConnectionError,
|
2023-04-25 17:21:46 -03:00
|
|
|
.featureNotAvailableInCustomEntitlementsComputationMode,
|
2024-11-06 15:46:04 -06:00
|
|
|
.signatureVerificationFailed,
|
2024-11-25 16:14:32 +01:00
|
|
|
.featureNotSupportedWithStoreKit1,
|
|
|
|
|
.invalidWebPurchaseToken,
|
2024-12-05 17:44:08 +01:00
|
|
|
.purchaseBelongsToOtherUser,
|
2024-11-25 16:14:32 +01:00
|
|
|
.expiredWebPurchaseToken:
|
2022-04-15 15:54:18 +02:00
|
|
|
Logger.error(
|
2022-10-11 12:56:15 -07:00
|
|
|
localizedDescription,
|
2022-04-15 15:54:18 +02:00
|
|
|
fileName: fileName,
|
|
|
|
|
functionName: functionName,
|
|
|
|
|
line: line
|
|
|
|
|
)
|
2021-11-18 09:58:17 -08:00
|
|
|
|
|
|
|
|
case .purchaseCancelledError,
|
|
|
|
|
.storeProblemError,
|
|
|
|
|
.purchaseNotAllowedError,
|
|
|
|
|
.purchaseInvalidError,
|
|
|
|
|
.productNotAvailableForPurchaseError,
|
|
|
|
|
.productAlreadyPurchasedError,
|
|
|
|
|
.missingReceiptFileError,
|
|
|
|
|
.invalidAppleSubscriptionKeyError,
|
|
|
|
|
.ineligibleError,
|
|
|
|
|
.insufficientPermissionsError,
|
2021-12-08 16:18:58 -08:00
|
|
|
.paymentPendingError,
|
|
|
|
|
.productRequestTimedOut:
|
2022-04-15 15:54:18 +02:00
|
|
|
Logger.appleError(
|
2022-10-11 12:56:15 -07:00
|
|
|
localizedDescription,
|
2022-04-15 15:54:18 +02:00
|
|
|
fileName: fileName,
|
|
|
|
|
functionName: functionName,
|
|
|
|
|
line: line
|
|
|
|
|
)
|
2021-11-18 09:58:17 -08:00
|
|
|
|
|
|
|
|
@unknown default:
|
2022-04-15 15:54:18 +02:00
|
|
|
Logger.error(
|
2022-10-11 12:56:15 -07:00
|
|
|
localizedDescription,
|
2022-04-15 15:54:18 +02:00
|
|
|
fileName: fileName,
|
|
|
|
|
functionName: functionName,
|
|
|
|
|
line: line
|
|
|
|
|
)
|
2021-11-18 09:58:17 -08:00
|
|
|
}
|
|
|
|
|
}
|
2021-07-22 11:52:43 +02:00
|
|
|
}
|
2022-04-12 15:11:22 -07:00
|
|
|
|
|
|
|
|
extension Error {
|
|
|
|
|
|
2022-09-05 13:46:07 -07:00
|
|
|
@_disfavoredOverload
|
|
|
|
|
func addingUserInfo<Result: NSError>(_ userInfo: [NSError.UserInfoKey: Any]) -> Result {
|
|
|
|
|
return self.addingUserInfo(userInfo as [String: Any])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func addingUserInfo<Result: NSError>(_ userInfo: [String: Any]) -> Result {
|
2022-04-12 15:11:22 -07:00
|
|
|
let nsError = self as NSError
|
2023-11-16 00:20:00 +01:00
|
|
|
return .init(domain: nsError.domain,
|
|
|
|
|
code: nsError.code,
|
|
|
|
|
userInfo: nsError.userInfo + userInfo)
|
2022-04-12 15:11:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Represents where an `Error` was created
|
|
|
|
|
struct ErrorSource {
|
|
|
|
|
|
|
|
|
|
let file: String
|
|
|
|
|
let function: String
|
|
|
|
|
let line: UInt
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// `Equatable` conformance allows `Error` types that contain source information
|
|
|
|
|
/// to easily conform to `Equatable`.
|
|
|
|
|
extension ErrorSource: Equatable {
|
|
|
|
|
|
|
|
|
|
/// However, for ease of testing, we don't actually care if the source of the errors matches
|
|
|
|
|
/// since expectations will be created in the test and therefore will never match.
|
|
|
|
|
static func == (lhs: ErrorSource, rhs: ErrorSource) -> Bool {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|