Firebase 无密码电子邮件身份验证无法在 iOS 上打开应用



我正在尝试实现很酷的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文档,因此非常感谢任何想法。

我终于想通了。 代码很好。 这是一个与动态链接有关的问题。 我在Firebase中设置了几个链接,因为我必须一度创建一个新的捆绑ID。 当我删除Firebase中的旧链接时,电子邮件链接开始工作。

它像这样出现在我的应用程序关联站点中,奇怪的是,即使我删除了旧链接,它仍然如此,但至少它现在可以工作了!

{"applinks":{"apps":[],"details":[{"appID":"TEAMID.com.OLDBUNDLEIDENTIFIER.APPNAME","paths":["NOT//*","/*"]},{"appID":"TEAMID.com.NEWBUNDLEIDENTIFIER.APPNAME","paths":["NOT//","/">

]}]}}更新:我实现无密码电子邮件登录的完整代码如下。 使用文档拼凑起来对我来说很痛苦,所以希望这可以为您省去麻烦。

假设您了解 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 && 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) 应用程序委托中的代码

// 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)
}
}

对于将 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. 在 SceneDelegate 文件中,import FirebaseDynamicLinks并添加以下代码
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
}

}
}

最新更新