ios - 用户登录/注册 iOS 后,Firebase 出现观察问题

标签 ios swift firebase firebase-realtime-database

每当用户登录/登录应用时,一些 Firebase 观察结果,例如 fetchSavedActionsForTracking() 和 fetchActionsForDowns()(请参阅下面的完整函数),在单击按钮期间不会观察到,并且应用会表现得很奇怪,就像与节点混合一样在 Firebase 中。当我再次编译代码时,代码完美运行,一切恢复正常。我不确定 Xcode 是否会导致此问题,或者我在 View Controller 中有很多观察结果。如果有人遇到同样的问题或有想法,请告诉我。我在其他 VC 中也有一些观察,但它们都很好

这是我作为 View Controller 的观察结果:

func fetchProperties() {

    // Added the first property
    Database.database().reference().child("Properties").observe(.childAdded, with: { (snapshot) in

        if let dictinaryProperty = snapshot.value as? [String: AnyObject]{

            // Assign property to Property()
            self.property = Property()

            // set the variables in dictionaryProperty's value to property's value (Be carefull the names have to match)
            self.property?.setValuesForKeys(dictinaryProperty)

            // User ID in Firebase
            self.property?.keyID = snapshot.key

            // Then add it to arrayPropertys
            self.arrayPropertys.append(self.property!)

            // This will crash because of background thread, so lets use DispatchQueue.main.async
            DispatchQueue.main.async(execute: {
                self.tableView.reloadData()
            })
        }

    }, withCancel: nil)


    // Remove
    Database.database().reference().child("Properties").observe(.childRemoved, with: { (snapshot) in

        if let dictinaryProperty = snapshot.value as? [String: AnyObject]{

            // Assign property to Property()
            self.property = Property()

            // set the variables in dictionaryProperty's value to property's value (Be carefull the names have to match)
            self.property?.setValuesForKeys(dictinaryProperty)

            // User ID in Firebase
            self.property?.keyID = snapshot.key

            // Check on the loaded tracking
            if self.arrayPropertysTracking.isEmpty != true {

                // Then delete from Firebase
                self.checkingAndDeletingTracking(remove: self.property!)
            }

            // Then add it to arrayPropertys
            self.arrayPropertysRemoved.append(self.property!)

            self.removedProperties()

            // This will crash because of background thread, so lets use DispatchQueue.main.async
            DispatchQueue.main.async(execute: {
                self.tableView.reloadData()
            })
        }

    }, withCancel: nil)

    // Child is edited
    Database.database().reference().child("Properties").observe(.childChanged, with: { (snapshot) in

        if let dictinaryProperty = snapshot.value as? [String: AnyObject]{

            // Assign property to Property()
            self.property = Property()

            // set the variables in dictionaryProperty's value to property's value (Be carefull the names have to match)
            self.property?.setValuesForKeys(dictinaryProperty)

            // User ID in Firebase
            self.property?.keyID = snapshot.key

            // Check if the updated property exists in Tracking window and if it does update it too
            if self.arrayPropertysTracking.isEmpty != true {

                self.checkingAndUpdateTracking(update: self.property!)
            }

            // Then add it to arrayPropertys
            self.arrayPropertiesEdited.append(self.property!)

            self.editedProperties()

            // This will crash because of background thread, so lets use DispatchQueue.main.async
            DispatchQueue.main.async(execute: {
                self.tableView.reloadData()
            })
        }

    }, withCancel: nil)


}

第二;

// Load saved actions of the current user
func fetchSavedActionsForTracking() {

    let userID = Auth.auth().currentUser?.uid

    if let actualUserID = userID {

        // Add the first property
        Database.database().reference().child("Actions For Tracking").child(actualUserID).observe(.childAdded, with: { (snapshot) in

            // The key is the property owner's ID
            let keySnapshot = snapshot.key as? String

            // Tags of saved buttons
            let valueSnapshot = snapshot.value as? String

            if let actualKey = keySnapshot {

                 self.arraySavedKeyIDs.append(actualKey)

            }

        //This will crash because of background thread, so lets use DispatchQueue.main.async
        DispatchQueue.main.async(execute: {
            self.tableView.reloadData()
        })


        }, withCancel: nil)

        // Removed the saved actions in Firebase when a property delected in Tracking window
        Database.database().reference().child("Actions For Tracking").child(actualUserID).observe(.childRemoved, with: { (snapshot) in

            // The key is the property owner's ID
            let keySnapshot = snapshot.key as? String

            // Tags of saved buttons
            let valueSnapshot = snapshot.value as? String

            if let actualKey = keySnapshot {

                self.arrayRemovedTags.append(actualKey)

            }

            // Remove the tag from the array
            self.removedArrayTags()

            // When saved is clicked twice clear the arrayRemovedTags
            self.arrayRemovedTags.removeAll()

            //This will crash because of background thread, so lets use DispatchQueue.main.async
            DispatchQueue.main.async(execute: {
                self.tableView.reloadData()
            })

        }, withCancel: nil)

    }

}

第三;

 // Load properties from tracking window
func fetchTracking() {

    let userID = Auth.auth().currentUser?.uid

    if let actualUserID = userID {

        Database.database().reference().child("Tracking").child(actualUserID).observe(.childAdded, with: { (snapshot) in

            if let dictinaryProperty = snapshot.value as? [String: AnyObject]{

                // Assign property to Property()
                self.property = Property()

                // set the variables in dictionaryProperty's value to property's value (Be carefull the names have to match)
                self.property?.setValuesForKeys(dictinaryProperty)

                // Then add it to arrayPropertys
                self.arrayPropertysTracking.append(self.property!)

                // This will crash because of background thread, so lets use DispatchQueue.main.async
                DispatchQueue.main.async(execute: {
                    self.tableView.reloadData()
                })

            }

        }, withCancel: nil)

        // Remove
        Database.database().reference().child("Tracking").child(actualUserID).observe(.childRemoved, with: { (snapshot) in

            if let dictinaryProperty = snapshot.value as? [String: AnyObject]{

                // Assign property to Property()
                self.property = Property()

                // set the variables in dictionaryProperty's value to property's value (Be carefull the names have to match)
                self.property?.setValuesForKeys(dictinaryProperty)


                // Then add it to arrayPropertys
                //self.arrayRemovedTracking.append(self.property!)

                // Remove the property from arrayPropertysTracking
                for remove in 0...self.arrayPropertysTracking.count-1 {

                    if self.arrayPropertysTracking[remove].keyID == self.property?.keyID {

                        self.arrayPropertysTracking.remove(at: remove)

                        return

                    }
                }

                // This will crash because of background thread, so lets use DispatchQueue.main.async
                DispatchQueue.main.async(execute: {
                    self.tableView.reloadData()
                })
            }

        }, withCancel: nil)
    }
}

第四:

 // Fetch the down button actions
func fetchActionsForDowns() {

    let userID = Auth.auth().currentUser?.uid

    if let actualUserID = userID {

        // Add the first property
        Database.database().reference().child("Actions For Downs").child(actualUserID).observe(.childAdded, with: { (snapshot) in

            // The key is the property owner's ID
            let keySnapshot = snapshot.key as? String

            // Tags of saved buttons
            let valueSnapshot = snapshot.value as? String

            if let actualKey = keySnapshot {

                self.arrayDownsKeyIDs.append(actualKey)

            }

            //This will crash because of background thread, so lets use DispatchQueue.main.async
            DispatchQueue.main.async(execute: {
                self.tableView.reloadData()
            })


        }, withCancel: nil)

        // Removed the saved actions in Firebase when a property delected in Tracking window
        Database.database().reference().child("Actions For Downs").child(actualUserID).observe(.childRemoved, with: { (snapshot) in

            // The key is the property owner's ID
            let keySnapshot = snapshot.key as? String

            // Tags of saved buttons
            // let valueSnapshot = snapshot.value as? String

            if let actualKey = keySnapshot {

                self.arrayDownsRemoved.append(actualKey)

            }

            // Remove the action from the array
            self.removeActionsForDownsFunctions()

            // When saved is clicked twice clear the arrayRemovedTags
            self.arrayDownsRemoved.removeAll()

            //This will crash because of background thread, so lets use DispatchQueue.main.async
            DispatchQueue.main.async(execute: {
                self.tableView.reloadData()
            })

        }, withCancel: nil)

    }

}

第五;

func fetchAnsweresOnDowns() {

    let userID = Auth.auth().currentUser?.uid
    if let actualUserID = userID {

        Database.database().reference().child("Answers for Downs").child(actualUserID).observe(.childAdded, with: { (snapshot) in

            if let dictinaryProperty = snapshot.value as? [String: AnyObject]{

                // this will empty the variables in downedProperty
                self.answerDowns = Property()

                // set the variables in dictionaryProperty's value to property's value (Be carefull the names have to match)
                self.answerDowns?.setValuesForKeys(dictinaryProperty)

                // Type
                self.answerDowns?.downID = snapshot.key

                // Then add it to arrayPropertys
                self.arrayAnswerDowns.append(self.answerDowns!)

                // Do not upload unless cell has been clicked
                if self.isExpanded == true {

                    // Upload into received downs
                    self.uploadeReceivedDowns(receive:self.arrayPropertys[(self.selectedIndex?.row)!])
                }


                // This will crash because of background thread, so lets use DispatchQueue.main.async
                DispatchQueue.main.async(execute: {
                    self.tableView.reloadData()
                })

            }

        }, withCancel: nil)

        // Removed property from answers on downs
        Database.database().reference().child("Answers for Downs").child(actualUserID).observe(.childRemoved, with: { (snapshot) in

            if let dictinaryProperty = snapshot.value as? [String: AnyObject]{

                // this will empty the variables in downedProperty
                self.answerDowns = Property()

                // set the variables in dictionaryProperty's value to property's value (Be carefull the names have to match)
                self.answerDowns?.setValuesForKeys(dictinaryProperty)

                // Type
                self.answerDowns?.downID = snapshot.key

                // Then add it to arrayPropertys
                self.arrayRemovedAnswerDowns.append(self.answerDowns!)

                self.removeAnswerDownsFunction()

                // This will crash because of background thread, so lets use DispatchQueue.main.async
                DispatchQueue.main.async(execute: {
                    self.tableView.reloadData()
                })

            }

        }, withCancel: nil)

    }


}

这里是主视图 Controller 的 viewDidLoad() 函数:

verride func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.


    // User is not logged in 
    checkIfUserIsLogedin()

    // The delegate for receiving an action/data and notifying the viewcontroller
    // The datasource is to feed an object such as a tableview and piker from the viewcontroller
    tableView.delegate = self      // Notify the ViewController whenever the user tabs on rows or scroll
    tableView.dataSource = self    // The dataSource will ask the ViewController for table cells

    // Initialize ref
    ref = Database.database().reference()


    // Creates UINib documents(i.e it create the the files created by main.storyboard or simailiar and truning them to a file)
    let nib = UINib.init(nibName: "CustomTableViewCell", bundle: nil)
    tableView.register(nib, forCellReuseIdentifier: "cell")


    // Set up the search bar 
    searchBarSetup()

    // This function has to be here to reload table view with added property (i.e refresh table)

    fetchProperties()

    fetchSavedActionsForTracking()

    fetchTracking()

    fetchActionsForDowns()

    fetchAnsweresOnDowns()

}

这是我检查用户是否登录的地方(也存在于主视图 Controller 中):

func checkIfUserIsLogedin() {
    if Auth.auth().currentUser?.uid == nil {
        perform(#selector(handleLogout), with: nil, afterDelay: 0)
        //handleLogout()
    }
}

func handleLogout() {

    // When User click logout
    do {

       try Auth.auth().signOut()

    } catch let logoutError {
        print(logoutError)
    }

    // Object of loginViewController

    loginVC = storyboard?.instantiateViewController(withIdentifier: "LoginVC")

    // Transition to loginViewController()
    present(loginVC!, animated: true, completion: nil)

 }

这是身份验证(我将其放在单独的 View Controller 中)

登录:

// Sign in into Firebase
func handleLogin() {

    // Unwrap the text fields using guard which will simply do if-statament
    guard let email = emailTextField.text, let password = passwordTextField.text else {
        print("is nil")
        return
    }

    Auth.auth().signIn(withEmail: email, password: password) { (user, err) in

        if err != nil {
            // There is an error
            if email == "" || password == "" {

                print("Empty")
                // Create an alert message
                let alertMessage = UIAlertController(title: "Empty Fields", message: "Please Fill The Fields", preferredStyle: .alert)

                // Attach an action on alert message
                alertMessage.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                    alertMessage.dismiss(animated: true, completion: nil)
                }))
                // Display the alert message
                self.present(alertMessage, animated: true, completion: nil)

                return
            }

            print("Wrong Email or Password")
            // Create an alert message
            let alertMessage = UIAlertController(title: "Wrong Email or Password", message: "Please try again", preferredStyle: .alert)

            // Attach an action on alert message
            alertMessage.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                alertMessage.dismiss(animated: true, completion: nil)
            }))
            // Display the alert message
            self.present(alertMessage, animated: true, completion: nil)

            return

        }
        // Sueccessful
        self.presentingViewController?.dismiss(animated: true, completion: nil)
        print("Login sueccessful into Firebase database")
    }

}

注册:

func handleRegister() {

    // Unwrap the text fields using guard which will simply do if-statament
    guard let email = emailTextField.text, let password = passwordTextField.text, let name = nameTextField.text else {
        print("is nil")
        return
    }

    // Upload the user's name and email into authentication
    Auth.auth().createUser(withEmail: email, password: password) { (user: User?, error) in

        if error != nil {
            // Handle the error (i.e notify the user of the error)
            if email == "" || password == "" || name == "" {

                print("Empty")
                // Create an alert message
                let alertMessage = UIAlertController(title: "Empty Fields", message: "Please Fill The Fields", preferredStyle: .alert)

                // Attach an action on alert message
                alertMessage.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                    alertMessage.dismiss(animated: true, completion: nil)
                }))
                // Display the alert message
                self.present(alertMessage, animated: true, completion: nil)

                return
            }


            else if let errCode = AuthErrorCode(rawValue: error!._code) {

                switch errCode {
                case .invalidEmail:

                    print("invalid email")
                    // Create an alert message
                    let alertMessage = UIAlertController(title: "Invalid Email", message: "Please check the entered email address", preferredStyle: .alert)
                    // Attach an action on alert message
                    alertMessage.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                        alertMessage.dismiss(animated: true, completion: nil)
                    }))
                    // Display the alert message
                    self.present(alertMessage, animated: true, completion: nil)

                case .emailAlreadyInUse:

                    print("in use")
                    // Create an alert message
                    let alertMessage = UIAlertController(title: "Existed Email", message: "The email existed in our database, login instead of registering", preferredStyle: .alert)
                    // Attach an action on alert message
                    alertMessage.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                        alertMessage.dismiss(animated: true, completion: nil)
                    }))
                    // Display the alert message
                    self.present(alertMessage, animated: true, completion: nil)

                case .weakPassword:

                    print("password is weak")
                    // Create an alert message
                    let alertMessage = UIAlertController(title: "Password is weak", message: "Use upper and lower characters along with numbers", preferredStyle: .alert)
                    // Attach an action on alert message
                    alertMessage.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
                        alertMessage.dismiss(animated: true, completion: nil)
                    }))
                    // Display the alert message
                    self.present(alertMessage, animated: true, completion: nil)

                default:
                    print("Other error!")
                }

            }
        }

        // Get the user id from Firebase
        guard let uid = user?.uid else {
            return
        }


        // Successfully authenticated

        //Upload the name and user's ID into Database
        let usersReference = self.ref?.child("Users Info").child(uid)

        let values = ["name": name, "email": email]
        //self.ref?.child("User1").setValue(values)

        // Will add valuse without overwriting
        usersReference?.updateChildValues(values, withCompletionBlock: { (err, ref) in

            if err != nil {
                print(err)
                return
            }

            // Transition to home screen
            self.presentingViewController?.dismiss(animated: true, completion: nil)
            print("Saved user successfully into Firebase database")
        })
    }
}

这是日志记录 View Controller 中的 viewDidload():

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.

    // initialize the constraints of the white view and stack view
    containerView.translatesAutoresizingMaskIntoConstraints = false
    stackView.translatesAutoresizingMaskIntoConstraints = false
    nameTextField.translatesAutoresizingMaskIntoConstraints = false
    emailTextField.translatesAutoresizingMaskIntoConstraints = false
    passwordTextField.translatesAutoresizingMaskIntoConstraints = false

    // Create a varaible ref for Firebase
    ref = Database.database().reference()

    // You have to add the text field delegate if you want to add fuctions when user press/finish typing. For example when "return" key press, hide the keyboard
    self.nameTextField.delegate = self
    self.emailTextField.delegate = self
    self.passwordTextField.delegate = self

最佳答案

viewDidLoad()中,代码检查用户是否登录,然后调用

fetchProperties()

fetchSavedActionsForTracking()

fetchTracking()

fetchActionsForDowns()

fetchAnsweresOnDowns()

用户是否已登录。如果用户已注销,那么您可能需要按下某个调用handleLogin()的按钮。但这不会再次调用这五个函数,因此数据不会更新以反射(reflect)当前用户。本质上,您希望确保在用户登录后调用这些函数。您可以通过多种不同的方法来实现此目的,例如:

如果用户在加载应用程序时登录,则调用函数

func checkIfUserIsLogedin() {
    if Auth.auth().currentUser?.uid == nil {
        perform(#selector(handleLogout), with: nil, afterDelay: 0)
        //handleLogout()
    } else {
        fetchProperties()

        fetchSavedActionsForTracking()

        fetchTracking()

        fetchActionsForDowns()

        fetchAnsweresOnDowns()        
    }
}

如果用户最初没有登录,则在用户登录时调用函数

func handleLogin() {
   ...
   ...
   // Successful
   fetchProperties()

   fetchSavedActionsForTracking()

   fetchTracking()

   fetchActionsForDowns()

   fetchAnsweresOnDowns() 

}

关于ios - 用户登录/注册 iOS 后,Firebase 出现观察问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45762345/

相关文章:

ios - Swiftui 列出 Mac Catalyst 上不匹配的部分

ios - 在 Swift Bridging Header 中包含 Google Analytics

ios - twilio 视频 sdk 不工作

firebase - 云Firestore : Enforcing Unique User Names

get() 和 getAfter() 之间的 Firebase 安全规则区别

ios - 如何从 Webview 打开 Safari View Controller (swift)

ios - PayPal iOS SDK 的凭据

swift - 如何在 Swift 中对数组进行排序

ios - Material Vertical 步进形式可以在 Swift iOS Xcode 中实现

firebase - 我无法获取要使用 nativescript 上传的文件的大小