即使关闭应用程序,如何保持用户登录?我正在使用 swift 3 和 Firebase

Posted

技术标签:

【中文标题】即使关闭应用程序,如何保持用户登录?我正在使用 swift 3 和 Firebase【英文标题】:how to keep a user login even if they close the app? I'm using swift 3, and Firebase 【发布时间】:2017-05-11 17:50:16 【问题描述】:

我是编码和堆栈溢出的新手,我试图让用户即使在关闭应用程序后也保持登录状态。我也不希望他们总是看到登录屏幕。即使用户关闭应用程序并重新打开应用程序,我该如何保持用户登录。我正在使用 Swift 3.0、Xcode 8 和 Firebase。

import UIKit
import Firebase
import SwiftKeychainWrapper


class LoginViewController: UIViewController 

    @IBOutlet weak var emailField: UITextField!
    @IBOutlet weak var pwField: UITextField!


    override func viewDidLoad() 
        super.viewDidLoad()
        let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(LoginViewController.dismissKeyboard))
        //Uncomment the line below if you want the tap not not interfere and cancel other interactions.
        //tap.cancelsTouchesInView = false

        view.addGestureRecognizer(tap)

        // Do any additional setup after loading the view.
    


    func dismissKeyboard() 
        //Causes the view (or one of its embedded text fields) to resign the first responder status.
        view.endEditing(true)
    

    @IBAction func loginPressed(_ sender: Any) 
        guard emailField.text != "", pwField.text != "" else return

        FIRAuth.auth()?.signIn(withEmail: emailField.text!, password: pwField.text!, completion:  (user, error) in
            if let error = error 
                print(error.localizedDescription)
            

            if user != nil 
                let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "TabBarViewController")

                self.present(vc, animated: true, completion: nil)
            
        )
    

下面是我的 UsersViewController 代码,它有注销按钮

import UIKit

导入 Firebase

类用户视图控制器:UIViewController、UITableViewDelegate、UITableViewDataSource

@IBOutlet weak var tableview: UITableView!

var user = [User]()



override func viewDidLoad() 
    super.viewDidLoad()


    retrieveUsers()



func retrieveUsers() 
    let ref = FIRDatabase.database().reference()

    ref.child("users").queryOrderedByKey().observeSingleEvent(of: .value, with:  snapshot in

        let users = snapshot.value as! [String : AnyObject]
        self.user.removeAll()
        for (_, value) in users 
            if let uid = value["uid"] as? String 
                if uid != FIRAuth.auth()!.currentUser!.uid 
                    let userToShow = User()
                    if let fullName = value["full name"] as? String, let imagePath = value["urlToImage"] as? String 
                        userToShow.fullName = fullName
                        userToShow.imagePath = imagePath
                        userToShow.userID = uid
                        self.user.append(userToShow)
                    
                
            
        
        self.tableview.reloadData()
    )
    ref.removeAllObservers()



func numberOfSections(in tableView: UITableView) -> Int 
    return 1


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = tableview.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as! UserCell

    cell.nameLabel.text = self.user[indexPath.row].fullName
    cell.userID = self.user[indexPath.row].userID
    cell.userImage.downloadImage(from: self.user[indexPath.row].imagePath!)
    checkFollowing(indexPath: indexPath)

    return cell


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    return user.count 



func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 

    let uid = FIRAuth.auth()!.currentUser!.uid
    let ref = FIRDatabase.database().reference()
    let key = ref.child("users").childByAutoId().key

    var isFollower = false

    ref.child("users").child(uid).child("following").queryOrderedByKey().observeSingleEvent(of: .value, with:  snapshot in

        if let following = snapshot.value as? [String : AnyObject] 
            for (ke, value) in following 
                if value as! String == self.user[indexPath.row].userID 
                    isFollower = true

                    ref.child("users").child(uid).child("following/\(ke)").removeValue()
                    ref.child("users").child(self.user[indexPath.row].userID).child("followers/\(ke)").removeValue()

                    self.tableview.cellForRow(at: indexPath)?.accessoryType = .none
                
            
        
        if !isFollower 
            let following = ["following/\(key)" : self.user[indexPath.row].userID]
            let followers = ["followers/\(key)" : uid]

            ref.child("users").child(uid).updateChildValues(following as Any as! [AnyHashable : Any])
            ref.child("users").child(self.user[indexPath.row].userID).updateChildValues(followers)

            self.tableview.cellForRow(at: indexPath)?.accessoryType = .checkmark
        
    )
    ref.removeAllObservers()




func checkFollowing(indexPath: IndexPath) 
    let uid = FIRAuth.auth()!.currentUser!.uid
    let ref = FIRDatabase.database().reference()

    ref.child("users").child(uid).child("following").queryOrderedByKey().observeSingleEvent(of: .value, with:  snapshot in

        if let following = snapshot.value as? [String : AnyObject] 
            for (_, value) in following 
                if value as! String == self.user[indexPath.row].userID 
                    self.tableview.cellForRow(at: indexPath)?.accessoryType = .checkmark
                
            
        
    )
    ref.removeAllObservers()








@IBAction func logOutPressed(_ sender: Any) 


    do 

        try FIRAuth.auth()?.signOut()

        if FIRAuth.auth()?.currentUser == nil 

            // Remove User Session from device
            UserDefaults.standard.removeObject(forKey: "uid")
            UserDefaults.standard.synchronize()

            let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LoginVC") as! LoginViewController

        

     catch let signOutError as NSError 

        // handle logout error

    

扩展 UIImageView

func downloadImage(from imgURL: String!) 
    let url = URLRequest(url: URL(string: imgURL)!)

    let task = URLSession.shared.dataTask(with: url) 
        (data, response, error) in

        if error != nil 
            print(error!)
            return
        

        DispatchQueue.main.async 
            self.image = UIImage(data: data!)
        

    

    task.resume()

【问题讨论】:

【参考方案1】:

Firebase 身份验证可以为您处理此问题。与 Firebase 数据库一样,Auth 通过设置侦听器来工作。您可以像这样在 App Delegate 中监听现有用户:

final class AppDelegate: UIResponder, UIApplicationDelegate 
    private let auth = FIRAuth.auth()

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool 
        auth?.addStateDidChangeListener  [weak self] (_, user) in
            if let user = user 
                // user is already logged in
             else 
                // user is not logged in
            
        
    

【讨论】:

我会在哪里添加这个? 我会像我一样将它放在 App Delegate 中,这样您就可以在状态发生变化时更改窗口的根视图控制器。 在您关闭应用程序后仍然无法让他们保持登录状态。 确实如此。如果登录成功,Firebase 身份验证就会保留用户。 我需要在评论部分放一些东西才能让它工作吗?【参考方案2】:

当用户成功登录后,将他们的 UID 放入 UserDefaults 以存储会话,如下所示:

UserDefaults.standard.set(FIRAuth.auth()!.currentUser!.uid, forKey: "user_uid_key")
UserDefaults.standard.synchronize()

然后,无论您的应用加载的第一个 View Controller 是什么,检查 viewDidAppear() 中的那个键,如下所示:

override func viewDidAppear(_ animated: Bool) 

    super.viewDidAppear(animated)

    // Check if the user is logged in
    if UserDefaults.standard.object(forKey: "user_uid_key") != nil 
        // send them to a new view controller or do whatever you want
    

将 UserDefaults 放在您注册/登录用户的 IBAction/Function 的成功块中,如下所示:

FIRAuth.auth()?.signIn(withEmail: email, password: password, completion:  (user, error) in

            if user != nil 
                // Login success
                // Saves User UID to UserDefaults  
                UserDefaults.standard.set(FIRAuth.auth()!.currentUser!.uid, forKey: "USER_KEY_UID")
                UserDefaults.standard.synchronize()


            
            else   
                // login error

  )

用户注销时删除 UserDefault:

@IBAction func logoutButtonPressed(sender: UIButton!) 

    do 

        try FIRAuth.auth()?.signOut()

        if FIRAuth.auth()?.currentUser == nil 

            // Remove User Session from device
            UserDefaults.standard.removeObject(forKey: "USER_KEY_UID")
            UserDefaults.standard.synchronize()

            let loginVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LoginVC") as! LoginVC
            present("TheVCYouWantToSendTheUserTo", animated: true, completion: nil)
        

     catch let signOutError as NSError 

        // handle logout error
    

更新:

您忘记在登录函数中包含 UserDefaults。这就是为什么它会给你一个错误。将此添加到您的登录 IBAction。

@IBAction func loginPressed(_ sender: Any) 
    guard emailField.text != "", pwField.text != "" else return

    FIRAuth.auth()?.signIn(withEmail: emailField.text!, password: pwField.text!, completion:  (user, error) in
        if let error = error 
            print(error.localizedDescription)

// You forgot to save User UID to UserDefaults here... 
                UserDefaults.standard.set(FIRAuth.auth()!.currentUser!.uid, forKey: "uid")
                UserDefaults.standard.synchronize()

        

        if user != nil 
            let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "TabBarViewController")

            self.present(vc, animated: true, completion: nil)
        
    )

【讨论】:

我在哪里放置用户默认代码?还是在同一节下 我更新了上面的代码,使其更具可读性。看看这是否适合你 Firebase 身份验证已经在没有 UserDefaults 的应用启动时持续存在。 它的工作原理就像它会自动转到视图控制器但是如果我按下注销这将带我回到登录页面它会自动再次登录我该如何解决这个问题以便如果我注销它不会自动登录 对于删除用户默认值的代码,我是将其放在 loginviewcontroller 代码中还是注销按钮所在的位置?

以上是关于即使关闭应用程序,如何保持用户登录?我正在使用 swift 3 和 Firebase的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 aws amplify 保持用户登录 react-native

使用 swift 和 parse 保持当前用户登录

我正在尝试使用 Framework-7 Vuejs 创建一个应用程序。即使在使用 Firebase 退出应用程序后,我也想让我的用户保持登录状态

保存 Kivy 应用程序的登录屏幕用户名和密码

Firebase:如何让 Android 用户保持登录状态?

React - 即使刷新了如何保持在同一页面上?