firebase & swift - 这个 class 不符合密钥的键值编码 -----
firebase & swift - this class is not key value coding-compliant for the key -----
我知道还有其他类似的问题,但我认为我的问题是我如何访问 firebase 而不是 outlet,因为我的错误出现在 @IBAction 函数中,该函数可以在错误发生之前被调用。
@IBAction func sign_in_out_tapped(sender : UIButton) {
if let op_user = user {
if (op_user.user_ref?.valueForKey("current_status"))! as! String == "OUT" {
let sign_in_time = NSDate()
op_user.user_ref?.childByAppendingPath("logins").updateChildValues([String(op_user.user_ref?.valueForKey("num_of_logins")! as! Int + 1): ["sign_in_time": sign_in_time]])
signinout = SignInOutModel(sign_in_time: sign_in_time)
op_user.user_ref?.updateChildValues(["current_status": "IN"])
} else {
signinout!.sign_out_time = NSDate()
op_user.user_ref?.childByAppendingPath("logins").childByAppendingPath(String(user?.user_ref?.valueForKey("num_of_logins"))).updateChildValues(["sign_out_time": signinout!.sign_out_time!])
signinout!.duration = (op_user.user_ref?.childByAppendingPath("logins").childByAppendingPath(String(user?.user_ref?.valueForKey("num_of_logins"))).valueForKey("sign_in_time")?.timeIntervalSinceNow)!
op_user.user_ref?.childByAppendingPath("logins").childByAppendingPath(String(user?.user_ref?.valueForKey("num_of_logins"))).updateChildValues(["duration": signinout!.duration])
op_user.user_ref?.updateChildValues(["total_hours": (Double((op_user.user_ref?.valueForKey("total_hours"))! as! NSNumber) + signinout!.duration)])
}
} else {
let sign_in_alert = UIAlertController(title: "Sign in.", message: "What is your first and last name?", preferredStyle: UIAlertControllerStyle.Alert)
sign_in_alert.addTextFieldWithConfigurationHandler { textField in
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MainViewController.handleTextFieldTextDidChangeNotification(_:)), name: UITextFieldTextDidChangeNotification, object: textField)
}
func removeTextFieldObserver() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UITextFieldTextDidChangeNotification, object: sign_in_alert.textFields![0])
}
let cancel_action = UIAlertAction(title: "Cancel", style: .Cancel) { action in
print("Sign In Cancel Button Pressed")
removeTextFieldObserver()
}
let save_action = UIAlertAction(title: "Save", style: .Default) { action in
print("Sign Out Save Button Pressed")
let textField = sign_in_alert.textFields![0] as UITextField
if let user_name = textField.text {
var not_found = false
self.students_ref.childByAppendingPath(user_name).observeEventType(.Value, withBlock: { snapshot in
if (snapshot.value is NSNull) {
not_found = true
} else {
self.user = User(user_name: user_name)
self.user?.user_ref = self.students_ref.childByAppendingPath(user_name)
self.refresh_user_name_label()
}
})
if !not_found {
self.mentors_ref.childByAppendingPath(user_name).observeEventType(.Value, withBlock: { snapshot in
if (snapshot.value is NSNull) {
not_found = true
} else {
self.user = User(user_name: user_name)
self.user?.user_ref = self.mentors_ref.childByAppendingPath(user_name)
self.refresh_user_name_label()
}
})
} else {
self.error_message("User not found. Please update Firebase.")
}
} else {
self.error_message("Could not sign in.")
}
removeTextFieldObserver()
}
save_action.enabled = false
AddAlertSaveAction = save_action
sign_in_alert.addAction(cancel_action)
sign_in_alert.addAction(save_action)
self.presentViewController(sign_in_alert, animated: true, completion: nil)
if let _ = user {
let sign_in_time = NSDate()
signinout = SignInOutModel(sign_in_time: sign_in_time)
}
}
refresh_sign_in_out_button()
}
我相信错误在顶部,它说 "op_user.user_ref?.valueForKey("current_status"))! as String == "OUT"" 因为错误不仅说,
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Firebase 0x7fa12e0b5530> valueForUndefinedKey:]: this class is not key value coding-compliant for the key current_status.'
但是在通过调试器时,程序直到 "valueForKey("current_status")" 才终止。
如有任何帮助,我们将不胜感激!谢谢!
编辑:我的 firebase:
{
"mentors" : {
"Ash Dreyer" : {
"current_status" : "IN",
"num_of_logins" : 0,
"total_hours" : 0
},
"Donald Pinckney" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Jasmine Zhou" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Michael Corsetto" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
}
},
"students" : {
"Bryton Moeller" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Kelly Ostrom" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Kyle Stachowicz" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Wesley Aptekar-Cassels" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
}
}
}
编辑:
我的项目的目标是创建一个标志 in/out 应用程序。我的导师希望其他人能够看到某人是否已登录,并希望跟踪某人总共登录了多长时间(比如他们是否已经在商店里工作了 100 个小时之类的。)
您的问题在于您尝试访问 Firebase 引用数据的方式。 valueForKey 不是正确的方法。您需要执行以下两项操作之一:
- 首先将你的 firebaseSnap.value 转换为字典,然后使用 dict[key] 语法。
- Snapshot.childSnapshotForPath("current_status").值
我推荐第 2 种方法,因为它是最简洁的方法。但是,如果您需要经常访问引用,您可能希望转换为字典,因为那样访问它看起来会更好。
注意:这两个都需要一个 firebase 事件侦听器来获取 firebase 快照。
如果您有任何问题,请告诉我,祝您好运!
根据您的后续评论,以下几点可能会有所帮助。请原谅冗长的回答,但只是想帮助减少和简化您正在编写的代码量。
一般来说,解除键与数据的关联是一个很好的模型。这样做的原因是:如果用户更改了他们的名字怎么办?他们结婚后使用了不同的姓氏,决定他们喜欢被称为 Jim 而不是 James 等。如果您使用他们的名字作为密钥,它是不可更改的,并且您引用它的任何其他地方也将被保留。如果将名称设为子节点,则可以随时更改,不会影响其他数据。
第一步是将结构改成这样
{
"mentors" : {
"uid_0" : {
"user_name": "Ash Dreyer",
"current_status" : "IN",
"num_of_logins" : 0,
"total_hours" : 0
},
"uid_1" : {
"user_name": "Donald Pinckney",
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
uid_0、uid_1 等是在创建用户时由 Firebase 创建的,可以从 authData.uid 中检索。对于您存储在 /mentors(或 /users)节点中的每个用户,这是一个很好的数据片段。
当用户登录时,您有几个选择:如果您不需要他们的数据,您可以只更新 current_status 节点。您知道特定的节点名称,因为它是它们的 auth.uid.
let ref = /mentors node
let thisUser = ref.childByAppendingPath(auth.uid)
let thisUsersStatusRef = thisUser.childByAppendingPath("current_status")
thisUsersStatusRef.setValue("In")
并对时间戳做同样的事情。
如果您需要在 UI 中显示他们的名字,只需通过 observeSingleEvent 读取他们的数据,并更新当前状态和时间戳。
let ref = /mentors node
let thisUser = ref.childByAppendingPath(auth.uid)
ref.observeSingleEventOfType(.Value, withBlock: { snapshot
//read the user data from the snapshot and do whatever with it
let name = snapshot.value["user_name"] as? String
print(name!)
//now update the status
thisUsersStatusRef.setValue("In")
//and the timestamp
}
当用户退出时,你只需要计算小时,设置current_status退出并写入时间戳
thisUsersStatusRef.setValue("Out")
thisUsersHoursRef.setValue(hours)
thisUsersTimeStamp.setValue(timestamp)
这里的大局是,如果您在用户登录时读入用户数据,您就会预先获得计算小时数等的所有信息,因为它可以存储在变量中,因此当他们注销时,执行计算并写入。所以它基本上只攻击 Firebaes 两次(一次是在他们登录时,一次是在他们注销时)。
最后一件事是每个用户都应该观察 /mentors 节点的 childAdded、childChanged 或 childRemoved 事件(不是值)。对于这些观察者,当添加、编辑或更改用户时,所有用户都会收到该单个数据节点的通知,并可以相应地更新他们的 UI。
我知道还有其他类似的问题,但我认为我的问题是我如何访问 firebase 而不是 outlet,因为我的错误出现在 @IBAction 函数中,该函数可以在错误发生之前被调用。
@IBAction func sign_in_out_tapped(sender : UIButton) {
if let op_user = user {
if (op_user.user_ref?.valueForKey("current_status"))! as! String == "OUT" {
let sign_in_time = NSDate()
op_user.user_ref?.childByAppendingPath("logins").updateChildValues([String(op_user.user_ref?.valueForKey("num_of_logins")! as! Int + 1): ["sign_in_time": sign_in_time]])
signinout = SignInOutModel(sign_in_time: sign_in_time)
op_user.user_ref?.updateChildValues(["current_status": "IN"])
} else {
signinout!.sign_out_time = NSDate()
op_user.user_ref?.childByAppendingPath("logins").childByAppendingPath(String(user?.user_ref?.valueForKey("num_of_logins"))).updateChildValues(["sign_out_time": signinout!.sign_out_time!])
signinout!.duration = (op_user.user_ref?.childByAppendingPath("logins").childByAppendingPath(String(user?.user_ref?.valueForKey("num_of_logins"))).valueForKey("sign_in_time")?.timeIntervalSinceNow)!
op_user.user_ref?.childByAppendingPath("logins").childByAppendingPath(String(user?.user_ref?.valueForKey("num_of_logins"))).updateChildValues(["duration": signinout!.duration])
op_user.user_ref?.updateChildValues(["total_hours": (Double((op_user.user_ref?.valueForKey("total_hours"))! as! NSNumber) + signinout!.duration)])
}
} else {
let sign_in_alert = UIAlertController(title: "Sign in.", message: "What is your first and last name?", preferredStyle: UIAlertControllerStyle.Alert)
sign_in_alert.addTextFieldWithConfigurationHandler { textField in
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MainViewController.handleTextFieldTextDidChangeNotification(_:)), name: UITextFieldTextDidChangeNotification, object: textField)
}
func removeTextFieldObserver() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UITextFieldTextDidChangeNotification, object: sign_in_alert.textFields![0])
}
let cancel_action = UIAlertAction(title: "Cancel", style: .Cancel) { action in
print("Sign In Cancel Button Pressed")
removeTextFieldObserver()
}
let save_action = UIAlertAction(title: "Save", style: .Default) { action in
print("Sign Out Save Button Pressed")
let textField = sign_in_alert.textFields![0] as UITextField
if let user_name = textField.text {
var not_found = false
self.students_ref.childByAppendingPath(user_name).observeEventType(.Value, withBlock: { snapshot in
if (snapshot.value is NSNull) {
not_found = true
} else {
self.user = User(user_name: user_name)
self.user?.user_ref = self.students_ref.childByAppendingPath(user_name)
self.refresh_user_name_label()
}
})
if !not_found {
self.mentors_ref.childByAppendingPath(user_name).observeEventType(.Value, withBlock: { snapshot in
if (snapshot.value is NSNull) {
not_found = true
} else {
self.user = User(user_name: user_name)
self.user?.user_ref = self.mentors_ref.childByAppendingPath(user_name)
self.refresh_user_name_label()
}
})
} else {
self.error_message("User not found. Please update Firebase.")
}
} else {
self.error_message("Could not sign in.")
}
removeTextFieldObserver()
}
save_action.enabled = false
AddAlertSaveAction = save_action
sign_in_alert.addAction(cancel_action)
sign_in_alert.addAction(save_action)
self.presentViewController(sign_in_alert, animated: true, completion: nil)
if let _ = user {
let sign_in_time = NSDate()
signinout = SignInOutModel(sign_in_time: sign_in_time)
}
}
refresh_sign_in_out_button()
}
我相信错误在顶部,它说 "op_user.user_ref?.valueForKey("current_status"))! as String == "OUT"" 因为错误不仅说,
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Firebase 0x7fa12e0b5530> valueForUndefinedKey:]: this class is not key value coding-compliant for the key current_status.'
但是在通过调试器时,程序直到 "valueForKey("current_status")" 才终止。
如有任何帮助,我们将不胜感激!谢谢!
编辑:我的 firebase:
{
"mentors" : {
"Ash Dreyer" : {
"current_status" : "IN",
"num_of_logins" : 0,
"total_hours" : 0
},
"Donald Pinckney" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Jasmine Zhou" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Michael Corsetto" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
}
},
"students" : {
"Bryton Moeller" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Kelly Ostrom" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Kyle Stachowicz" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Wesley Aptekar-Cassels" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
}
}
}
编辑:
我的项目的目标是创建一个标志 in/out 应用程序。我的导师希望其他人能够看到某人是否已登录,并希望跟踪某人总共登录了多长时间(比如他们是否已经在商店里工作了 100 个小时之类的。)
您的问题在于您尝试访问 Firebase 引用数据的方式。 valueForKey 不是正确的方法。您需要执行以下两项操作之一:
- 首先将你的 firebaseSnap.value 转换为字典,然后使用 dict[key] 语法。
- Snapshot.childSnapshotForPath("current_status").值
我推荐第 2 种方法,因为它是最简洁的方法。但是,如果您需要经常访问引用,您可能希望转换为字典,因为那样访问它看起来会更好。
注意:这两个都需要一个 firebase 事件侦听器来获取 firebase 快照。
如果您有任何问题,请告诉我,祝您好运!
根据您的后续评论,以下几点可能会有所帮助。请原谅冗长的回答,但只是想帮助减少和简化您正在编写的代码量。
一般来说,解除键与数据的关联是一个很好的模型。这样做的原因是:如果用户更改了他们的名字怎么办?他们结婚后使用了不同的姓氏,决定他们喜欢被称为 Jim 而不是 James 等。如果您使用他们的名字作为密钥,它是不可更改的,并且您引用它的任何其他地方也将被保留。如果将名称设为子节点,则可以随时更改,不会影响其他数据。
第一步是将结构改成这样
{
"mentors" : {
"uid_0" : {
"user_name": "Ash Dreyer",
"current_status" : "IN",
"num_of_logins" : 0,
"total_hours" : 0
},
"uid_1" : {
"user_name": "Donald Pinckney",
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
uid_0、uid_1 等是在创建用户时由 Firebase 创建的,可以从 authData.uid 中检索。对于您存储在 /mentors(或 /users)节点中的每个用户,这是一个很好的数据片段。
当用户登录时,您有几个选择:如果您不需要他们的数据,您可以只更新 current_status 节点。您知道特定的节点名称,因为它是它们的 auth.uid.
let ref = /mentors node
let thisUser = ref.childByAppendingPath(auth.uid)
let thisUsersStatusRef = thisUser.childByAppendingPath("current_status")
thisUsersStatusRef.setValue("In")
并对时间戳做同样的事情。
如果您需要在 UI 中显示他们的名字,只需通过 observeSingleEvent 读取他们的数据,并更新当前状态和时间戳。
let ref = /mentors node
let thisUser = ref.childByAppendingPath(auth.uid)
ref.observeSingleEventOfType(.Value, withBlock: { snapshot
//read the user data from the snapshot and do whatever with it
let name = snapshot.value["user_name"] as? String
print(name!)
//now update the status
thisUsersStatusRef.setValue("In")
//and the timestamp
}
当用户退出时,你只需要计算小时,设置current_status退出并写入时间戳
thisUsersStatusRef.setValue("Out")
thisUsersHoursRef.setValue(hours)
thisUsersTimeStamp.setValue(timestamp)
这里的大局是,如果您在用户登录时读入用户数据,您就会预先获得计算小时数等的所有信息,因为它可以存储在变量中,因此当他们注销时,执行计算并写入。所以它基本上只攻击 Firebaes 两次(一次是在他们登录时,一次是在他们注销时)。
最后一件事是每个用户都应该观察 /mentors 节点的 childAdded、childChanged 或 childRemoved 事件(不是值)。对于这些观察者,当添加、编辑或更改用户时,所有用户都会收到该单个数据节点的通知,并可以相应地更新他们的 UI。