Skip to content
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

Make SDK Errors Public #1159

Merged
merged 7 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Braintree iOS SDK Release Notes

## unreleased
* Update all SDK errors to be public and Equatable (fixes #1152 and #1080)
jaxdesmarais marked this conversation as resolved.
Show resolved Hide resolved

## 6.10.0 (2023-11-17)
* BraintreePayPalNativeCheckout
* Update PayPalCheckout from 1.1.0 to 1.2.0.
Expand Down
2 changes: 1 addition & 1 deletion Demo/Application/Features/IdealViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class IdealViewController: PaymentButtonBaseViewController {

localPaymentClient.startPaymentFlow(request) { result, error in
guard let result else {
if (error as? NSError)?.code == 5 {
if error as? BTLocalPaymentError == .canceled("") {
self.progressBlock("Canceled 🎲")
} else {
self.progressBlock("Error: \(error?.localizedDescription ?? "")")
Expand Down
12 changes: 7 additions & 5 deletions Demo/Application/Features/SEPADirectDebitViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ class SEPADirectDebitViewController: PaymentButtonBaseViewController {
sepaDirectDebitRequest.merchantAccountID = "EUR-sepa-direct-debit"

sepaDirectDebitClient.tokenize(sepaDirectDebitRequest) { sepaDirectDebitNonce, error in
if let sepaDirectDebitNonce = sepaDirectDebitNonce {
if let sepaDirectDebitNonce {
self.completionBlock(sepaDirectDebitNonce)
} else if let error = error {
self.progressBlock(error.localizedDescription)
} else {
self.progressBlock("Canceled")
} else if let error {
if error as? BTSEPADirectDebitError == .webFlowCanceled {
self.progressBlock("Canceled")
} else {
self.progressBlock(error.localizedDescription)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class ThreeDSecureViewController: PaymentButtonBaseViewController {
self.updateCallbackCount()

guard let threeDSecureResult else {
if (error as? NSError)?.code == 5 {
if error as? BTThreeDSecureError == .canceled {
self.progressBlock("Canceled 🎲")
} else {
self.progressBlock(error?.localizedDescription)
Expand Down
2 changes: 1 addition & 1 deletion Demo/Application/Features/VenmoViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class VenmoViewController: PaymentButtonBaseViewController {
progressBlock("Got a nonce 💎!")
completionBlock(venmoAccount)
} catch {
if (error as NSError).code == 10 {
if error as? BTVenmoError == .canceled {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🌟

Copy link
Contributor

@scannillo scannillo Dec 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is slick! Much improved over forcing merchants to look at our markdown list of codes and hard code the int values

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏈 Punt, but curious your thoughts.

For V7, we could get to something like this if error == .canceled { ... } if the error type we return is BT SDK specific. Is that what you're thinking as the V7 goal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - I think that would be dope if we return strongly typed errors. The only case it could be a little weird is we would need to wrap any errors from core. I think in most cases we do that anyways but I know there are a few instances where we return a core error directly. Though I don't think it should be a blocker for strongly typed errors.

progressBlock("Canceled 🔰")
} else {
progressBlock(error.localizedDescription)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// Error details associated with American Express.
enum BTAmericanExpressError: Int, Error, CustomNSError, LocalizedError {
public enum BTAmericanExpressError: Int, Error, CustomNSError, LocalizedError, Equatable {

/// 0. Unknown error
case unknown
Expand All @@ -12,15 +12,15 @@ enum BTAmericanExpressError: Int, Error, CustomNSError, LocalizedError {
/// 2. Deallocated BTAmericanExpressClient
case deallocated

static var errorDomain: String {
public static var errorDomain: String {
"com.braintreepayments.BTAmericanExpressErrorDomain"
}

var errorCode: Int {
public var errorCode: Int {
rawValue
}

var errorDescription: String? {
public var errorDescription: String? {
switch self {
case .unknown:
return "An unknown error occurred. Please contact support."
Expand Down
8 changes: 4 additions & 4 deletions Sources/BraintreeApplePay/BTApplePayError.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// Error codes associated with Apple Pay.
enum BTApplePayError: Int, Error, CustomNSError, LocalizedError {
public enum BTApplePayError: Int, Error, CustomNSError, LocalizedError, Equatable {

/// 0. Unknown error
case unknown
Expand All @@ -15,15 +15,15 @@ enum BTApplePayError: Int, Error, CustomNSError, LocalizedError {
/// 3. Unable to create BTApplePayCardNonce
case failedToCreateNonce

static var errorDomain: String {
public static var errorDomain: String {
"com.braintreepayments.BTApplePayErrorDomain"
}

var errorCode: Int {
public var errorCode: Int {
rawValue
}

var errorDescription: String? {
public var errorDescription: String? {
switch self {
case .unknown:
return ""
Expand Down
14 changes: 10 additions & 4 deletions Sources/BraintreeCard/BTCardError.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

// Error codes associated with cards
enum BTCardError: Error, CustomNSError, LocalizedError {
public enum BTCardError: Error, CustomNSError, LocalizedError, Equatable {

/// 0. Unknown error
case unknown
Expand All @@ -18,11 +18,11 @@ enum BTCardError: Error, CustomNSError, LocalizedError {
/// 4. Failed to fetch Braintree configuration
case fetchConfigurationFailed

static var errorDomain: String {
public static var errorDomain: String {
"com.braintreepayments.BTCardClientErrorDomain"
}

var errorCode: Int {
public var errorCode: Int {
switch self {
case .unknown:
return 0
Expand All @@ -37,7 +37,7 @@ enum BTCardError: Error, CustomNSError, LocalizedError {
}
}

var errorUserInfo: [String: Any] {
public var errorUserInfo: [String: Any] {
switch self {
case .unknown:
return [NSLocalizedDescriptionKey: "An unknown error occurred. Please contact support."]
Expand All @@ -51,4 +51,10 @@ enum BTCardError: Error, CustomNSError, LocalizedError {
return [NSLocalizedDescriptionKey: "Failed to fetch Braintree configuration."]
}
}

// MARK: - Equatable Conformance

public static func == (lhs: BTCardError, rhs: BTCardError) -> Bool {
lhs.errorCode == rhs.errorCode
}
Comment on lines +55 to +59
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required for any of the enums with associated types. It it's able to resolve the equality of the error codes automatically.

}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import Foundation

/// Error codes associated with a API Client.
enum BTAnalyticsServiceError: Int, Error, CustomNSError, LocalizedError {
public enum BTAnalyticsServiceError: Int, Error, CustomNSError, LocalizedError, Equatable {

/// 0. Invalid API client
case invalidAPIClient

static var errorDomain: String {
public static var errorDomain: String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do all the enum properties need to be public to resolve the merchant casting issue? errorDomain feels internal (but maybe it's a swifty thing).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - it needs to be public to satisfy protocol requirements since we are making the enum as a whole public.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need a taylor swift emoji for "swifty" things 😄

"com.braintreepayments.BTAnalyticsServiceErrorDomain"
}

var errorCode: Int {
public var errorCode: Int {
rawValue
}

var errorDescription: String? {
public var errorDescription: String? {
switch self {
case .invalidAPIClient:
return "API client must have client token or tokenization key"
Expand Down
8 changes: 4 additions & 4 deletions Sources/BraintreeCore/BTAPIClientError.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// Error codes associated with a API Client.
enum BTAPIClientError: Int, Error, CustomNSError, LocalizedError {
public enum BTAPIClientError: Int, Error, CustomNSError, LocalizedError, Equatable {

/// 0. Configuration fetch failed
case configurationUnavailable
Expand All @@ -12,15 +12,15 @@ enum BTAPIClientError: Int, Error, CustomNSError, LocalizedError {
/// 2. Deallocated BTAPIClient
case deallocated

static var errorDomain: String {
public static var errorDomain: String {
"com.braintreepayments.BTAPIClientErrorDomain"
}

var errorCode: Int {
public var errorCode: Int {
rawValue
}

var errorDescription: String? {
public var errorDescription: String? {
switch self {
case .configurationUnavailable:
return "The operation couldn’t be completed. Unable to fetch remote configuration from Braintree API at this time."
Expand Down
8 changes: 4 additions & 4 deletions Sources/BraintreeCore/BTClientTokenError.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// Error codes associated with a client token.
enum BTClientTokenError: Error, CustomNSError, LocalizedError {
public enum BTClientTokenError: Error, CustomNSError, LocalizedError, Equatable {

/// 0. Authorization fingerprint was not present or invalid
case invalidAuthorizationFingerprint
Expand All @@ -18,11 +18,11 @@ enum BTClientTokenError: Error, CustomNSError, LocalizedError {
/// 4. Failed decoding from Base64 or UTF8
case failedDecoding(String)

static var errorDomain: String {
public static var errorDomain: String {
"com.braintreepayments.BTClientTokenErrorDomain"
}

var errorCode: Int {
public var errorCode: Int {
switch self {
case .invalidAuthorizationFingerprint:
return 0
Expand All @@ -37,7 +37,7 @@ enum BTClientTokenError: Error, CustomNSError, LocalizedError {
}
}

var errorDescription: String? {
public var errorDescription: String? {
switch self {
case .invalidAuthorizationFingerprint:
return "Invalid client token. Please ensure your server is generating a valid Braintree ClientToken. Authorization fingerprint was not present or invalid."
Expand Down
14 changes: 10 additions & 4 deletions Sources/BraintreeCore/BTHTTPError.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// Error codes associated with BTHTTP
enum BTHTTPError: Error, CustomNSError, LocalizedError {
public enum BTHTTPError: Error, CustomNSError, LocalizedError, Equatable {

/// 0. Unknown error (reserved)
case unknown
Expand Down Expand Up @@ -42,11 +42,11 @@ enum BTHTTPError: Error, CustomNSError, LocalizedError {
/// 12. Deallocated HTTPClient
case deallocated(String)

static var errorDomain: String {
public static var errorDomain: String {
BTCoreConstants.httpErrorDomain
}

var errorCode: Int {
public var errorCode: Int {
switch self {
case .unknown:
return 0
Expand Down Expand Up @@ -77,7 +77,7 @@ enum BTHTTPError: Error, CustomNSError, LocalizedError {
}
}

var errorUserInfo: [String : Any] {
public var errorUserInfo: [String: Any] {
switch self {
case .unknown:
return [NSLocalizedDescriptionKey: "An unexpected error occurred with the HTTP request."]
Expand Down Expand Up @@ -107,4 +107,10 @@ enum BTHTTPError: Error, CustomNSError, LocalizedError {
return [NSLocalizedDescriptionKey: "\(httpType) has been deallocated."]
}
}

// MARK: - Equatable Conformance

public static func == (lhs: BTHTTPError, rhs: BTHTTPError) -> Bool {
lhs.errorCode == rhs.errorCode
}
}
8 changes: 4 additions & 4 deletions Sources/BraintreeCore/BTJSONError.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

enum BTJSONError: Error, CustomNSError, LocalizedError {
public enum BTJSONError: Error, CustomNSError, LocalizedError, Equatable {

/// 0. JSONSerialization failure
case jsonSerializationFailure
Expand All @@ -11,11 +11,11 @@ enum BTJSONError: Error, CustomNSError, LocalizedError {
/// 2. Invalid key
case keyInvalid(String)

static var errorDomain: String {
public static var errorDomain: String {
"com.braintreepayments.BTJSONErrorDomain"
}

var errorCode: Int {
public var errorCode: Int {
switch self {
case .jsonSerializationFailure:
return 0
Expand All @@ -26,7 +26,7 @@ enum BTJSONError: Error, CustomNSError, LocalizedError {
}
}

var errorDescription: String? {
public var errorDescription: String? {
switch self {
case .jsonSerializationFailure:
return "Failed to serialize JSON data in initilizer"
Expand Down
8 changes: 4 additions & 4 deletions Sources/BraintreeDataCollector/BTDataCollectorError.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// Error details associated with Braintree Data Collector.
enum BTDataCollectorError: Int, Error, CustomNSError, LocalizedError {
public enum BTDataCollectorError: Int, Error, CustomNSError, LocalizedError, Equatable {

/// 0. Unknown error
case unknown
Expand All @@ -12,15 +12,15 @@ enum BTDataCollectorError: Int, Error, CustomNSError, LocalizedError {
/// 2. The device data could not be encoded.
case encodingFailure

static var errorDomain: String {
public static var errorDomain: String {
"com.braintreepayments.BTDataCollectorErrorDomain"
}

var errorCode: Int {
public var errorCode: Int {
rawValue
}

var errorDescription: String? {
public var errorDescription: String? {
switch self {
case .unknown:
return "An unknown error occurred. Please contact support."
Expand Down
16 changes: 11 additions & 5 deletions Sources/BraintreeLocalPayment/BTLocalPaymentError.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Foundation

/// Error codes associated with Payment Flow
enum BTLocalPaymentError: Error, CustomNSError, LocalizedError {
public enum BTLocalPaymentError: Error, CustomNSError, LocalizedError, Equatable {

/// 0. Unknown error
case unknown

Expand Down Expand Up @@ -36,9 +36,9 @@ enum BTLocalPaymentError: Error, CustomNSError, LocalizedError {
/// 10. ASWebAuthentication error
case webSessionError(Error)

static var errorDomain = "com.braintreepayments.BTLocalPaymentErrorDomain"
public static var errorDomain = "com.braintreepayments.BTLocalPaymentErrorDomain"

var errorCode: Int {
public var errorCode: Int {
switch self {
case .unknown:
return 0
Expand All @@ -65,7 +65,7 @@ enum BTLocalPaymentError: Error, CustomNSError, LocalizedError {
}
}

var errorDescription: String {
public var errorDescription: String {
switch self {
case .unknown:
return ""
Expand All @@ -91,4 +91,10 @@ enum BTLocalPaymentError: Error, CustomNSError, LocalizedError {
return "ASWebAuthenticationSession failed with \(error.localizedDescription)"
}
}

// MARK: - Equatable Conformance

public static func == (lhs: BTLocalPaymentError, rhs: BTLocalPaymentError) -> Bool {
lhs.errorCode == rhs.errorCode
}
}
Loading