braintree / braintree_ios

Braintree SDK for iOS
https://developer.paypal.com/braintree/docs/start/hello-client/ios/v5
MIT License
560 stars 294 forks source link

PayPal - Attempt to present UIAlertController on SFAuthenticationViewController #401

Closed aodhol closed 6 years ago

aodhol commented 6 years ago

General information

Issue description

It seems the SFAuthenticationViewController is still in the hierarchy after the delegate method:

BTAppSwitchDelegate.appContextDidReturn is called.

I'm not seeing the UIAlertView I expect to see because:

Warning: Attempt to present <UIAlertController: 0x7b640010f900> on <SFAuthenticationViewController: 0x7b6400104f00> whose view is not in the window hierarchy

Are you sure the BTAppSwitchDelegate delegate method above is being called at the appropriate time in the SFSafariViewController Completion Handler or wherever?

demerino commented 6 years ago

@aodhol Thanks for the feedback. The new BTAppSwitchDelegate.appContextDidReturn callback is unrelated to the dismissal of possibly presented SFSafariViewController or SFAuthenticationViewController. It just implies that the user has returned to your application - this would be useful for updating existing UI or other aspects of your application.

Hierarchy changes should wait until you receive the callback from the driver.

Just curious, what is your intent with displaying an alert before receiving the BTPaypalDriver callback?

aodhol commented 6 years ago

@demerino to clarify: I am getting the BTPaypalDriver callback, the problem is that SFAuthenticationViewController has apparently not dismissed by that time if the error is to be believed. I'm trying to show an error message when the shipping or billing addresses are missing....

demerino commented 6 years ago

I'm not sure I understand the issue. In my test on iOS 11 - showing an alert after getting the callback isn't showing that error (in the simulator). Can you help me reproduce the issue?

[driver requestBillingAgreement:checkout completion:^(BTPayPalAccountNonce * _Nullable tokenizedPayPalCheckout, NSError * _Nullable error) {

        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"ALERT", nil) message:nil preferredStyle:UIAlertControllerStyleAlert];
        [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Done", nil) style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction * _Nonnull action) {
            NSLog(@"Alert Closed");
        }]];

        [self presentViewController:alertController animated:YES completion:nil];
}];
aodhol commented 6 years ago

@demerino For me, the problem occurs when I do the following (though inconsistently i.e. there's a possible race condition):

let payPalDriver = BTPayPalDriver(apiClient: braintreeClient)
payPalDriver.viewControllerPresentingDelegate = self
payPalDriver.appSwitchDelegate = self

let payPalRequest = BTPayPalRequest(amount: "1.99")
payPalRequest.currencyCode = "GBP"
payPalRequest.isShippingAddressRequired = true

payPalDriver.requestOneTimePayment(payPalRequest) { (tokenizedPayPalAccount, error) -> Void in

    // Do stuff.....

    // FIXME: the asyncAfter is a workaround for this issue: https://github.com/braintree/braintree_ios/issues/401
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        self.showAlert(title: "Error", message: "Shipping or billing address not specified")
    }       
    return   
}

I'd rather not have to dispatchAfter one second as it seems arbitrary and brittle and from a UX point of view, introducing further delay compounds the poor experience of opening of a Web browser etc.

Could you try to do it several times and keep an eye on the console?

demerino commented 6 years ago

Are you still having issues with this?

aodhol commented 6 years ago

@demerino Just tested on 4.16.0 and the problem does occur:

Attempt to present <UIAlertController: 0x116737600> on <SFAuthenticationViewController: 0x1166da600> whose view is not in the window hierarchy!
bensonbob commented 6 years ago

i have similar issue Warning: Attempt to present <SFAuthenticationViewController: 0x7fcfb15c3400> on <BTDropInController: 0x7fcfb9323780> whose view is not in the window hierarchy!

Any suggestions

demerino commented 6 years ago

@aodhol @bensonbob Got it. Yes the method is called at the correct time but it does not mean that the SFAuthenticationSession view controller has completed the dismissal animation which is probably why you're seeing the error: https://github.com/braintree/braintree_ios/blob/master/BraintreePayPal/BTPayPalDriver.m#L412

The method is called when context has returned to the merchant app to continue processing the PayPal flow.

I haven't tried it myself, but maybe listening on the parent view controller's didAppear or similar will work?

crookedneighbor commented 6 years ago

Have y'all tried @demerino's suggestion?

I haven't tried it myself, but maybe listening on the parent view controller's didAppear or similar will work?

robertofrontado commented 6 years ago

This is probably the same issue we are facing on FB SDK, I opened a ticket for them where I also put a workaround

crookedneighbor commented 6 years ago

Closing this now as @demerino has given a good suggestion.

If y'all are still having issues, feel free to comment here.