Аутентификация по электронной почте без пароля Firebase не открывает приложение на iOS

#ios #swift #firebase #firebase-authentication #password-less

# #iOS #swift #firebase #firebase-аутентификация #без пароля

Вопрос:

Я пытаюсь и терплю неудачу в реализации классной функции входа по электронной почте Firebase. Я успешно настроил отправку ссылки по электронной почте. Однако я не могу получить ссылку по электронной почте, чтобы открыть приложение. Он просто открывает страницу предварительного просмотра, как будто не может открыть приложение.

Я протестировал настроенную мной динамическую ссылку и могу заставить ее открывать приложение на устройстве. Я просто не могу получить ссылку по электронной почте, чтобы сделать то же самое.

Код в моем приложении:

 func sendFirebaseEmailLink() {

    let actionCodeSettings = ActionCodeSettings.init()

    // userEmail comes from a textField
    let email = userEmail

    actionCodeSettings.url = URL.init(string: String(format: "https://<myappname>.firebaseapp.com/?email=%@", email))
    // The sign-in operation has to always be completed in the app.
    actionCodeSettings.handleCodeInApp = true
    actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)

    Auth.auth().sendSignInLink(toEmail: email,
        actionCodeSettings: actionCodeSettings) { error in

        if let error = error {
            print(error.localizedDescription)
            return
        }
        else {
            UserDefaults.standard.set(email, forKey: "Email")
            print("email sent to user")
        }
    }
}
 

Когда я говорю, что успешно получил динамическую ссылку для открытия приложения, я имею в виду, что когда я перехожу по созданной мной ссылке (mylinkname.page.link/emaillogin) на устройстве, на котором установлено приложение, оно открывает приложение. Из-за этого и [этого полезного видео Firebase] [1] по настройке динамической ссылки кажется, что я правильно описал эти детали, и проблема связана с кодом, но я новичок в этом, поэтому я не уверен.

Я потратил несколько дней, ходя по кругу, чтобы разобраться в этом и пытаясь проанализировать плотную документацию Firebase, поэтому любые идеи приветствуются.

Ответ №1:

Я наконец понял это. Код был в порядке. Это была проблема, связанная с динамической ссылкой. У меня была настроена пара ссылок в Firebase, потому что в какой-то момент мне пришлось создать новый идентификатор пакета. Когда я удалил старую в Firebase, ссылка на электронную почту начала работать.

Оно отображается на моем сайте ассоциации приложений следующим образом и, как ни странно, все еще работает, хотя я удалил старую ссылку, но, по крайней мере, оно работает сейчас!

{«ссылки на приложения»: {«приложения»: [], «подробности»: [{«AppID»: «TEAMID.com .OLDBUNDLEIDENTIFIER.APPNAME»,»пути»: [«НЕТ //*»,»/*»]},{» AppID»:»TEAMID.com .NEWBUNDLEIDENTIFIER.APPNAME»,»пути»: [«НЕТ //«,»/«]}]}}

ОБНОВЛЕНИЕ: ниже приведен мой полный код для реализации входа по электронной почте без пароля. Мне было больно собирать воедино документацию, поэтому, надеюсь, это избавит вас от проблем.

Основные шаги при условии, что вы понимаете основы настройки Firebase.

1) Настройте динамическую ссылку с помощью видеоурока Firebase.

2) Код в контроллере представления:

 var userEmail: String?
var link: String?

func sendFirebaseEmailLink() {

    let actionCodeSettings = ActionCodeSettings.init()
    let email = userEmail
    actionCodeSettings.url = URL.init(string: String(format: "https://<myappname>.page.link/emaillogin/?email=%@", email!))
    // The sign-in operation has to always be completed in the app.
    actionCodeSettings.handleCodeInApp = true
    actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)

    Auth.auth().sendSignInLink(toEmail: email!,
        actionCodeSettings: actionCodeSettings) { error in

        if let error = error {
            print(error.localizedDescription)
            return
        }
        else {
            UserDefaults.standard.set(email, forKey: "Email")
            print("email sent to user")
        }

        // TODO: Notify user to check email and click the link.
    }
}

// Sign in user after they clicked email link called from AppDelegate
@objc func signInUserAfterEmailLinkClick() {

    // Get link url string from the dynamic link captured in AppDelegate.
    if let link = UserDefaults.standard.value(forKey: "Link") as? String {
        self.link = link
    }

    // Sign user in with the link and email.
    Auth.auth().signIn(withEmail: userEmail!, link: link!) { (result, error) in

        if error == nil amp;amp; result != nil {

            if (Auth.auth().currentUser?.isEmailVerified)! {
                print("User verified with passwordless email")

                // TODO: Do something after user verified like present a new View Controller

            }
            else {
                print("User NOT verified by passwordless email")

            }
        }
        else {
            print("Error with passwordless email verfification: (error?.localizedDescription ?? "Strangely, no error avaialble.")")
        }   
    }
}
 

3) Код в AppDelegate

 // For Passwordless Email Login to Handle Dynamic Link after User Clicks Email Link
func application(_ application: UIApplication, continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {

    if let incomingURL = userActivity.webpageURL {
        print("Incoming URL is (incomingURL)")

        // Parse incoming
        let linkHandled = DynamicLinks.dynamicLinks().handleUniversalLink(incomingURL) { (dynamicLink, error) in

            guard error == nil else {
                print("Found an error: (error!.localizedDescription)")
                return
            }
            if let dynamicLink = dynamicLink {
                self.handleIncomingDynamicLink(dynamicLink)
            }
        }
        if linkHandled {
            return true
        }
        else {
            // Maybe do other things with dynamic links in future?
            return false
        }
    }
    return false
}

// Handles the link and saves it to userDefaults to assist with login.
func handleIncomingDynamicLink(_ dynamicLink: DynamicLink) {
    guard let url = dynamicLink.url else {
        print("My dynamic link object has no url")
        return
    }
    print("Incoming link parameter is (url.absoluteString)")

    let link = url.absoluteString
    if Auth.auth().isSignIn(withEmailLink: link) {

        // Save link to userDefaults to help finalize login.
        UserDefaults.standard.set(link, forKey: "Link")

        // Send notification to ViewController to push the First Time Login VC
        NotificationCenter.default.post(
            name: Notification.Name("SuccessfulPasswordlessEmailNotification"), object: nil, userInfo: nil)
    }
}
 

Комментарии:

1. У меня похожая проблема. Большинство людей могут аутентифицироваться по ссылке, но некоторые не могут. Он открывает App Store, показывает приложение как установленное, пользователь открывает приложение, но затем процесс входа в систему прерывается. Есть идеи о том, в чем может быть проблема?

2. @Dpedriha Я предлагаю опубликовать отдельный вопрос об этом с вашим кодом.

3. код работает для 95% пользователей, поэтому проблема не в коде.

4. У меня была аналогичная проблема. Несовершенным решением было отменить приоритетность подхода к входу по ссылке и предложить также вариант входа с паролем.

Ответ №2:

Для тех, кто использует SwiftUI с файлами AppDelegate и SceneDelegate вместо UIKit, вот что я сделал:

  1. Создайте функцию для отправки ссылки на электронную почту пользователя
 func sendSignLink(email: String) async throws {
    
do {
        let actionCodeSettings = ActionCodeSettings()
        actionCodeSettings.url = URL(string: "*enter your Firebase Dynamic link here*")
        actionCodeSettings.handleCodeInApp = true
        actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
        
        try await Auth.auth().sendSignInLink(toEmail: email, actionCodeSettings: actionCodeSettings)
        UserDefaults.standard.set(email, forKey: "email")
    }
    catch {
        throw error
    }
    
}
 
  1. В файле import FirebaseDynamicLinks SceneDelegate и добавьте приведенный ниже код
     func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
                
        if let incomingURL = userActivity.webpageURL {
            
            print("n nIncoming URL is (incomingURL)")
            
            _ = DynamicLinks.dynamicLinks().handleUniversalLink(incomingURL) { (dynamicLink, error) in
                
                guard error == nil else {
                    print("n nError with handling incoming URL: (error!.localizedDescription)")
                    return
                }
                
                if let dynamicLink = dynamicLink {
                    
                    guard let url = dynamicLink.url else {
                        print("n nDynamic link object has no url")
                        return
                    }
                    
                    print("n nIncoming link parameter is (url.absoluteString)")
                    
                    let link = url.absoluteString
                    
                    if Auth.auth().isSignIn(withEmailLink: link) {
                        
                        // Send notification to trigger the rest of the sign in sequence
                        NotificationCenter.default.post(name: Notification.Name("Success"), object: nil, userInfo: ["link": link])
                        
                    } else {
                        
                        // Send error notification
                        NotificationCenter.default.post(name: Notification.Name("Error"), object: nil, userInfo: nil)
                        
                    }
                    
                }
                
            }
        }
    }
 
  1. Создайте функцию для обработки входа после того, как пользователь нажал на ссылку в своем электронном письме
 func signInWithEmail(link: String) async throws {
    
    do {
        let email = UserDefaults.standard.value(forKey: "email")
        try await Auth.auth().signIn(withEmail: email, link: link)
    }
    catch {
        throw error
    }
    
}
 
  1. В соответствующем представлении обрабатывайте отправляемые уведомления
 struct MyView: View {
    
    var body: some View {
        
        VStack {
            Text("View")
        }
        
        .onReceive(NotificationCenter.default.publisher(for: Notification.Name("Success"))) { notificationInfo in
            
            if let userInfo = notificationInfo.userInfo {
                if let link = userInfo["link"] as? String {
                    Task.init {
                        do {
                            try await signInWithEmail(link: link)
                        } catch {
                            print(error)
                            
                        }
                    }
                }
            }
        }
        
        
        .onReceive(NotificationCenter.default.publisher(for: Notification.Name("Error"))) { _ in
            //do something with error
        }
        
    }
}