0

I'm trying to dismiss a .sheet in SwiftUI, after calling an async process to confirm the user's MFA code. (I'm using the AWS Amplify Framework).

I have a binding variable set on the main view, and reference it in the view the sheet presents with @Binding var displayMFAView: Bool. I have an authentication helper that tracks the user state: @EnvironmentObject var userAuthHelper: UserAuthHelper.

The following code dismisses the sheet as expected:

func confirmMFACode(verificationCode: String) {
    // Code to confifm MFA...
    print("User confirmed MFA")
    self.userAuthHelper.isSignedIn = true
    self.displayMFAView = false
}

However, if I call the auth process via Amplify's confirmSignIn method,

func confirmVerificationMFA(verificationCode: String) {
    AWSMobileClient.default().confirmSignIn(challengeResponse: verificationCode) { (signInResult, error) in
        if let error = error as? AWSMobileClientError {
            // ... error handling ...
        } else if let signInResult = signInResult {
            switch (signInResult.signInState) {
                case .signedIn:
                    print("User confirmed MFA")
                    self.userAuthHelper.isSignedIn = true
                    self.displayMFAView = false
                default:
                    print("\(signInResult.signInState.rawValue)")
                }
        }
    }
}

the sheet does not get dismissed. I have tried wrapping the variable assignment in DispatchQueue.main.async {..., but that hasn't solved the issue either.

...
DispatchQueue.main.async {
    self.userAuthHelper.isSignedIn = true
    self.displayMFAView = false
}
...

In fact, this throws the following into my logs:

Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.

Wrapping the switch (... in a DispatchQueue per https://stackoverflow.com/a/58288437/217101 gave me the same warning in my log.

Admittedly I don't have a firm grasp on SwiftUI or AWS Amplify. What am I not understanding?

jbnunn
  • 6,161
  • 4
  • 40
  • 65
  • I don't see all your code, but you might find helpful my answer for [How can I use Navigation in alert using SwiftUI](https://stackoverflow.com/questions/59276161/how-can-i-use-navigation-in-alert-using-swiftui/59281485#59281485)... seems there was something similar there. – Asperi Dec 14 '19 at 06:22

1 Answers1

0

From what I can tell the async call does something unexpected with the state variables, but not with an EnvironmentObject. So, nstead of @Binding var displayMFAView: Bool, I stored displayMFAView in an EnvironmentObject,

@EnvironmentObject var settings: UserSettings
@State var mfaCode: String = ""

and then can show or hide the .sheet(... by updating a boolean in that object:

Button(action: {
    self.signIn() // Async call happens here
    self.settings.displayMFAView.toggle()
}) {
    Text("Sign In")
}.sheet(isPresented: self.$settings.displayMFAView) {
    // Example code to capture text 
    TextField("Enter your MFA code", text: self.$mfaCode)
}

Button(action: {
    self.verifyMFACode(verificationCode: self.mfaCode) // async call
}) {
    Text("Confirm")
}

In func verifyMFACode(), I can make an async call to validate my user, then toggle the sheet to disappear on success:

func verifyMFACode(verificationCode: String) {
    AWSMobileClient.default().confirmSignIn(challengeResponse: verificationCode) {
    ...
    case .signedIn:
        self.settings.displayMFAView.toggle()
    ...
jbnunn
  • 6,161
  • 4
  • 40
  • 65