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

Add BTVenmoError.canceled case #1087

Merged
merged 11 commits into from
Aug 10, 2023
Merged

Add BTVenmoError.canceled case #1087

merged 11 commits into from
Aug 10, 2023

Conversation

scannillo
Copy link
Contributor

@scannillo scannillo commented Aug 1, 2023

Summary of changes

Fixes #1085. Previously, we returned a completion of (nil, nil) when the Venmo flow is canceled by the user. This results in a no-op for merchants using our async-await function wrappers.

This PR aligns with how the SEPA & PayPal modules handle cancelation cases, by returning an error to the merchant (either via the completion, or throwing if using async-await).

  • Add a .canceled error case to BTVenmoError
  • Clarify docstring
  • Update SDK_ERROR_CODES.md

Checklist

  • Added a changelog entry

Authors

@scannillo

@scannillo scannillo requested a review from a team as a code owner August 1, 2023 20:58
@scannillo
Copy link
Contributor Author

CI is having a field day

@@ -80,7 +80,11 @@ class BraintreeDemoVenmoViewController: BraintreeDemoPaymentButtonBaseViewContro
progressBlock("Got a nonce 💎!")
completionBlock(venmoAccount)
} catch {
progressBlock(error.localizedDescription)
if (error as NSError).code == 10 {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I know we can reference the error codes markdown, but this code would read better if there was a static constant used in place of 10.

Copy link
Contributor Author

@scannillo scannillo Aug 2, 2023

Choose a reason for hiding this comment

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

That's a good call. And on top of that, I think it should be easier for a merchant to check against our error codes (a merchant asked about it in #1080) versus needing them to check our SDK_ERROR_CODES.md file list.

Something like : if error.code == BTVenmoError.cancel.code { // do things } would be nice to expose for merchants.

@jaxdesmarais What do you think? Should we ticket this work?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I think I would still lean towards exposing the errors as a whole publicly for merchants using Swift. Since many of our errors use associated types, we can't expose those to Obj-C publicly as only Int type enums can be exposed.

If we wanted to expose them publicly for Obj-C merchants we would need to move the error codes into their own enum without associated types and construct the error messages at the callsite or in a method where we can include the details we are currently including with the associated type when constructing the localizedDescription.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeh - there are ways to do it without having to move the error creation to the callsite, though it makes our internal <Feature>Error.swift files a little uglier. Something like this:

enum BTAmericanExpressError: Int, Error, CustomNSError, LocalizedError {

    case unknown

    case noRewardsData

    case deallocated

    static var errorDomain: String {
        "com.braintreepayments.BTAmericanExpressErrorDomain"
    }
    
    var errorCode: Int {
        switch self {
        case .unknown:
            return BTAmericanExpressErrorCode.unknown.rawValue
        case .noRewardsData:
            return BTAmericanExpressErrorCode.noRewardsData.rawValue
        case .deallocated:
            return BTAmericanExpressErrorCode.deallocated.rawValue
        }
    }
    
    var errorDescription: String? {
        switch self {
        case .unknown:
            return "An unknown error occurred. Please contact support."

        case .noRewardsData:
            return "No American Express Rewards data was returned. Please contact support."

        case .deallocated:
            return "BTAmericanExpressClient has been deallocated."
        }
    }
}


/// Public for merchants to reference (accessible in both Swift & ObjC)
@objc public enum BTAmericanExpressErrorCode: Int {
    
    case unknown = 0
    case noRewardsData = 1
    case deallocated = 2
}

There might even be a slicker way to do that switch statement, but something like this would solve the issue

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, yeah I hadn't thought about just extracting the code portion.

Yeah, I guess the question in that case would be the merchant experience. With the above the callsite would look something like:

if (error as? NSError)?.code == BTAmericanExpressErrorCode.noRewardsData.rawValue {
    // do something for this specific error
}

vs making the enum public the callsite could look something like:

switch error as? BTAmericanExpressError {
case . noRewardsData:
    // do something for this specific error
default:
    // do something else for other errors
}

I think you could technically use a switch for the first but it'd be something like switch ((error as? NSError)?.code as? BTJSONErrorCode). It really comes down to how we want to support Obj-C vs Swift first in the future, which is up to us! We can certainly play around with some other options and see what feels best or engage the merchant that opened the issue to see what their ideal world would be.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thats a great point. I added this convo to our "iOS Minor Version Improvements" doc!

@@ -57,6 +57,9 @@ enum BTVenmoError: Error, CustomNSError, LocalizedError {

/// 9. Enriched Customer Data is disabled
case enrichedCustomerDataDisabled

/// 10. The Venmo flow was canceled by the user
case canceled
Copy link
Contributor

Choose a reason for hiding this comment

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

q: is this a breaking change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope, since we are only adding an error code & not changing existing ones we didn't think this was breaking

CHANGELOG.md Show resolved Hide resolved
@@ -54,7 +54,8 @@ import BraintreeCore
/// - Parameters:
/// - request: A Venmo request.
/// - completion: This completion will be invoked when app switch is complete or an error occurs. On success, you will receive
/// an instance of `BTVenmoAccountNonce`; on failure, an error; on user cancellation, you will receive `nil` for both parameters.
/// an instance of `BTVenmoAccountNonce`; on failure or user cancelation you will receive an error.
/// If the user cancels out of the flow, the error code will be `.canceled`.
@objc(tokenizeWithVenmoRequest:completion:)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The docs for the async method also needs this update!

@jaxdesmarais
Copy link
Contributor

CI is having a field day

Should be fixed now! See PR: #1089

Base automatically changed from venmo-demo-swift to main August 8, 2023 16:39
@scannillo scannillo merged commit 5d442a6 into main Aug 10, 2023
6 checks passed
@scannillo scannillo deleted the venmo-cancel-error branch August 10, 2023 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants