每当用户登录/登录应用时,一些 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/