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

3DS adds a large amount of whitespace to every ScrollView with SwiftUI and UIViewControllerRepresentable #1761

Closed
AlexanderTheLit opened this issue Feb 7, 2021 · 9 comments
Assignees
Labels
triaged Issue has been reviewed by Stripe and is being tracked internally

Comments

@AlexanderTheLit
Copy link

Summary

Setup Intent in a ViewController called by a UIViewControllerRepresentable in SwiftUI works great and integrates well. The only issue is that when I use 3DS the WebView causes this weird issue where large amounts of white space are added to every ScrollView in my app where buttons can't be tapped and other strangeness occurs like lists being clipped into a small rectangle.

I have included the SwiftUI portion of the code below to reproduce, as for the Setup Intent ViewController I use this code exactly https://github.com/stripe-samples/mobile-saving-card-without-payment/blob/master/client/ios/App%20(Swift)/CheckoutViewController.swift

*What's weird is that it only occurs on device. I've tried with iPhone 12, XR, and 8 plus

Code to reproduce

Use the card number 4000002500003155. Once the 3DS presents, tap fail or complete authentication. Then close the setup intent sheet and scroll down to see that the view has a large amount of whitespace below the blue Rectangle.

import SwiftUI

struct SetupIntent: UIViewControllerRepresentable {
    
    func makeUIViewController(context: Context) -> UIViewController {
        let input = SetupIntentViewController()
        return input
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        
    }
    
}

struct ContentView: View {
    
    @State var isPresentingSetupIntent = true
    
    var body: some View {
        ScrollView{
            VStack{
                
                Button("Setup Intent"){
                    isPresentingSetupIntent = true
                }
                
                Rectangle()
                    .fill(Color.blue)
                    .frame(width: 400, height: 1000)
        
            }
            
        }.sheet(isPresented: $isPresentingSetupIntent, content: {
            SetupIntent()  
        })
    }
}

iOS version

IOS 14.4

Installation method

Pod

SDK version

  • Stripe (21.2.0):
    • Stripe/Stripe3DS2 (= 21.2.0)
  • Stripe/Stripe3DS2 (21.2.0)
@csabol-stripe csabol-stripe self-assigned this Feb 9, 2021
@csabol-stripe csabol-stripe added the triaged Issue has been reviewed by Stripe and is being tracked internally label Feb 9, 2021
@csabol-stripe
Copy link
Contributor

Hi @AlexanderTheLit, I tried to reproduce this issue on an iPhone X running both 14.2 and 14.4 but have not been able to. Do you have additional screenshots, videos, and/or example projects that you could share?

@AlexanderTheLit
Copy link
Author

Thanks for the quick reply @csabol-stripe. Huh strange, I've included a video highlighting the issue on a iPhone XR 14.4. It only happens with the 3DS WebView, for a non 3DS SetupIntent it works perfectly. I have also included the SetupIntentViewController swift file I'm using.

StripeViewController.swift.zip

3DSIssue.mp4

@csabol-stripe
Copy link
Contributor

ah I am able to reproduce with your code. This also reproduces if you change the pay button to just present an SFSafariViewController instance (bypassing the Stripe SDK)
@objc func pay() { let vc = SFSafariViewController(url: URL(string: "https://www.stripe.com")!) self.present(vc, animated: true, completion: nil) }

It looks like this is probably an issue with SwiftUI + view controller presentation though I haven't been able to track down any similar bugs. I will leave this issue open for now because @davidme-stripe is working on some SwiftUI support and sample code now that may be able to help with this

@davidme-stripe
Copy link
Contributor

davidme-stripe commented Feb 18, 2021

@AlexanderTheLit, can you try the new SwiftUI bindings in 21.3.0? We now have an STPPaymentCardTextField.Representable SwiftUI View and a .setupIntentConfirmationSheet() ViewModifier, which should handle this use case. You can see a usage example in the IntegrationTester app.

@AlexanderTheLit
Copy link
Author

AlexanderTheLit commented Feb 19, 2021

Thanks for the reply @davidme-stripe. I integrated your code, but sadly the issue is still persisting. I think @csabol-stripe is correct, the issue lies with SwiftUI and SFSafariViewController presentation.

Also not sure if I'm integrating it right, but when calling CustomCardSetupIntent() in a .sheet .setupIntentConfirmationSheet automatically closes the .sheet when onCompletion is called even on error.

*Video below

import SwiftUI

struct ContentView: View {
    
    @State var isPresentingSetupIntent = true
    
    var body: some View {
        ScrollView{
            VStack{
                
                Button("Setup Intent"){
                    isPresentingSetupIntent = true
                }
                
                Rectangle()
                    .fill(Color.blue)
                    .frame(width: 400, height: 1100)
        
            }
            
        }.sheet(isPresented: $isPresentingSetupIntent, content: {
            CustomCardSetupIntent()
        })
    }
}

struct CustomCardSetupIntent: View {
  @StateObject var model = MySIBackendModel()
  @State var isConfirmingSetupIntent = false
  @State var paymentMethodParams: STPPaymentMethodParams?

  var body: some View {
      VStack {
        STPPaymentCardTextField.Representable(paymentMethodParams: $paymentMethodParams)
            .padding()
        if let setupIntent = model.intentParams {
          Button("Setup") {
            setupIntent.paymentMethodParams = paymentMethodParams
            isConfirmingSetupIntent = true
          }
          .setupIntentConfirmationSheet(isConfirmingSetupIntent: $isConfirmingSetupIntent,
                                     setupIntentParams: setupIntent,
                                     onCompletion: model.onCompletion)
          .disabled(isConfirmingSetupIntent || paymentMethodParams == nil)
        } else {
          ProgressView()
        }
        if let paymentStatus = model.paymentStatus {
            PaymentHandlerStatusView(actionStatus: paymentStatus, lastPaymentError: model.lastPaymentError)
        }
        Spacer()
      }.onAppear { model.preparePaymentIntent() }
    }
}
SetupIntentIssue.mp4

@davidme-stripe
Copy link
Contributor

davidme-stripe commented Feb 23, 2021

That does look like a SwiftUI bug, unfortunately. :( Please file it via Feedback Assistant, we'll file our own report as well.

As for the sheet-in-a-sheet dismissing issue, that's a bug on our end. I'll investigate a fix.

@mvarie
Copy link

mvarie commented Mar 16, 2021

@AlexanderTheLit
When you change the modalPresentationStyle of SFSafariViewController to .overCurrentContext or .overFullScreen, the problem disappears:

@objc func pay() {
  let vc = SFSafariViewController(url: URL(string: "https://www.stripe.com")!)
  vc.modalPresentationStyle = .overCurrentContext
  self.present(vc, animated: true, completion: nil)
}

However, when using Stripe, you're not handling SFSafariViewController directly.
Not that I recommend it, but to explore the issue further, the following extension seems to work when using Stripe (that is, it seems to make your problem disappear).

extension SFSafariViewController{
    
    open override var modalPresentationStyle: UIModalPresentationStyle{
        get{
            return .overCurrentContext
        }
        set {}
    }    
}

Of course, it's hacky at best.

@AlexanderTheLit
Copy link
Author

Very interesting @mvarie, I just tried it and it does indeed fix the issue. To be honest I kind of like the way .overCurrentContext looks as-well. I'm going to mess around with it more tonight, but I'm probably going to push an update out with this because I've already had a few customers mention this issue.

@davidme-stripe
Copy link
Contributor

This should be fixed in 21.4.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triaged Issue has been reviewed by Stripe and is being tracked internally
Projects
None yet
Development

No branches or pull requests

4 participants