                 self.loginType = .implicit
             }
 
-
             self.login(requestedScopes: authenticationProvider.scopes, presentingViewController: presentingViewController, completion: self.postCompletionHandler)
         }
     }
 
-    private func executeNativeAuthenticators(authenticators: [UberAuthenticating], completion: ((_ fallback: Bool) -> Void)) {
-        var stop: Bool = false
+    private func executeNativeAuthenticators(authenticators: [UberAuthenticating], completion: @escaping ((_ fallback: Bool) -> Void)) {
         var fallback: Bool = false
-        
-        for authenticator in authenticators where stop == false {
-            self.authenticator = authenticator
-            fallback = false
-
-            DeeplinkManager.shared.open(authenticator.authorizationURL, completion: { error in
-                if error == nil {
-                    return stop = true
-                }
-
-                // If the user rejected the attempt to call the Uber app, don't use fallback.
-                if self.loginType == .native, error?.code == DeeplinkErrorType.deeplinkNotFollowed.rawValue {
-                        self.loginCompletion(accessToken: nil, error: UberAuthenticationErrorFactory.errorForType(ridesAuthenticationErrorType: .unableToPresentLogin))
-                        return stop = true
-                } else {
-                    fallback = true
-                }
-            })
-        }
-
-        completion(fallback)
-    }
-    
+        AsyncDispatcher.exec(for: authenticators.map({ return $0.authorizationURL }),
+                             with: { (url: URL) in
+                                self.authenticator = authenticators.first(where: { $0.authorizationURL == url })
+                             },
+                             asyncMethod: DeeplinkManager.shared.open(_:completion:),
+                             continue: { (error: NSError?) -> Bool in
+                                fallback = false
+
+                                if error == nil {
+                                    // don't try next native authenticator
+                                    return false
+                                }
+
+                                // If the user rejected the attempt to call the Uber app, don't use fallback.
+                                if self.loginType == .native, error?.code == DeeplinkErrorType.deeplinkNotFollowed.rawValue {
+                                    self.loginCompletion(accessToken: nil, error: UberAuthenticationErrorFactory.errorForType(ridesAuthenticationErrorType: .unableToPresentLogin))
+
+                                    // don't try next native authenticator
+                                    return false
+                                } else {
+                                    fallback = true
+                                }
+
+                                // try next native authenticator
+                                return true
+                             },
+                             finally: {
+                                completion(fallback)
+                             })
+    }
+
     func handleLoginCanceled() {
         loggingIn = false
         willEnterForegroundCalled = false

    /// Login using native deeplink
    private func executeDeeplinkLogin(presentingViewController: UIViewController?, authenticationProvider: AuthenticationProvider) {

        let authenticators = authenticationProvider.authenticators(for: loginType)
        executeNativeAuthenticators(authenticators: authenticators) { (fallback: Bool) in
            if (!fallback) {
                return

        postCompletionHandler?(accessToken, error)
    }
}

/// A helper class to execute dependent asynchronous methods on an array of elements serially
private class AsyncDispatcher<T, U> {

    typealias AsyncCompletion = ((T) -> Void)?
    typealias AsyncContinue = (T) -> Bool

    /// Executes the given asyncMethod for each element in the list serially. Allows for aborting the list early if required.
    ///
    /// - Parameters:
    ///   - elements: The list with which to execute asyncMethod.
    ///   - with: block called just prior to executing asyncMethod with the given element.
    ///   - asyncMethod: the async method to execute.
    ///   - continue: block called with the results of asyncMethod; return true to move onto the next element, or false to abort the loop.
    ///   - finally: block called after the asyncMethod loop has completed, either due to reaching the end of the list or aborting early.
    static func exec(for elements: [U],
                     with: @escaping (U) -> (),
                     asyncMethod: @escaping (U, AsyncCompletion) -> (),
                     continue: @escaping AsyncContinue,
                     finally: @escaping () -> ()) {
        if elements.first != nil {
            execHelper(for: elements, currentIndex: 0, with: with, asyncMethod: asyncMethod, continue: `continue`, finally: finally)
        }
    }

    private static func execHelper(for elements: [U],
                                   currentIndex: Int,
                                   with: @escaping (U) -> (),
                                   asyncMethod: @escaping (U, AsyncCompletion) -> (),
                                   continue: @escaping AsyncContinue,
                                   finally: @escaping () -> ()) {
        let element = elements[currentIndex]
        with(element)
        asyncMethod(element) { (result: T) in
            let next = `continue`(result)
            if next && currentIndex < elements.count - 1 {
                execHelper(for: elements, currentIndex: currentIndex + 1, with: with, asyncMethod: asyncMethod, continue: `continue`, finally: finally)
            } else {
                finally()
            }
        }
    }
}
