ASWebAuthenticationSessionError.canceledLogin does not fire when swiping down to dismiss

1.2k Views Asked by At

I am creating an ASWebAuthenticationSession session and in the completion handler I have cleanup tasks for when the user cancel's the login:

let session = ASWebAuthenticationSession(url: url, callbackURLScheme: redirectURI) { (callbackURL: URL?, error: Error?) in
    if case .ASWebAuthenticationSessionError.canceledLogin? = error {
        // clean up tasks
    }

    // proceed...
}

In iOS 13+, the user is able to swipe down to dismiss, but the entire completion handler is not fired at all in this scenario. I do not want to disable this gesture by enabling isModalInPresentation.

Is there a way to make the ASWebAuthenticationSessionError.canceledLogin fire for this case, or how can I detect the user swiping down to cancel the ASWebAuthenticationSession session?

3

There are 3 best solutions below

1
On

Frustrating how difficult this is, but I was able to detect the dismissal by overriding the window's willRemoveSubview().

class MyWindow: UIWindow {
    var willRemoveSubviewCallback: (() -> ())?

    override func willRemoveSubview(_ subview: UIView) {
        super.willRemoveSubview(subview)
        willRemoveSubviewCallback?()
    }
}

func signIn() {
    let url = ...
    let scheme = ...
    let window = UIApplication.shared.delegate!.window as! MyWindow
    
    var hasCalledHandler = false
    let handler: ASWebAuthenticationSession.CompletionHandler = { [weak window] (callbackUrl, error) in
        guard !hasCalledHandler else {
            return
        }
        hasCalledHandler = true
        window?.willRemoveSubviewCallback = nil
        
        // Handle callbackurl / error...
    }
    
    let session = ASWebAuthenticationSession(url: url, callbackURLScheme: scheme, completionHandler: handler)
    session.presentationContextProvider = window
    let success = session.start()
    if success {
        window.willRemoveSubviewCallback = {
            handler(nil, NSError(domain: ASWebAuthenticationSessionError.errorDomain, code: ASWebAuthenticationSessionError.canceledLogin.rawValue, userInfo: nil))
        }
    }
}

extension UIWindow: ASWebAuthenticationPresentationContextProviding {
    public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        return self
    }
}
0
On

For anyone else who may stumble across this question I ended up being able to capture the error by doing this in the completion handler...

    if let error, !isCancelledLoginError(error) {
        /// Capture a normal error in here
    }

And outside of the completion handler the function that checks if the user clicked Cancel...

    private func isCancelledLoginError(_ error: Error) -> Bool {
        (error as NSError).code == ASWebAuthenticationSessionError.canceledLogin.rawValue
    }
0
On

I had the same problem. I found out, that the ViewController from which the ASWebAuthenticationSession is presented from, gets a call of its dismiss() function if the user swiped down the ASWebAuthenticationSession screen. So if you want to react on this user interaction, this is the only place where this can be done as far as I know. Find out, from which ViewController the ASWebAuthenticationSession is presented from and the override its dismiss() function.