Swift .oberve(.childAdded)在.observeSingleEvent(of:.值)未激活时生成错误
现在我有了Swift .oberve(.childAdded)在.observeSingleEvent(of:.值)未激活时生成错误,swift,xcode,firebase,Swift,Xcode,Firebase,现在我有了.observeSingleEvent(of:.value),它将我的所有注释加载到地图上: func loadAnnotations() { if let uid = Auth.auth().currentUser?.uid { uidRef.child(uid).child("annotations").observeSingleEvent(of: .value, with: { (snapshot) in for item in
.observeSingleEvent(of:.value)
,它将我的所有注释加载到地图上:
func loadAnnotations() {
if let uid = Auth.auth().currentUser?.uid {
uidRef.child(uid).child("annotations").observeSingleEvent(of: .value, with: { (snapshot) in
for item in snapshot.children {
// annotationListItem is a struct I created
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
let doubleLatitude = Double(annotationItem.mapViewLatitude!)
let doubleLongitude = Double(annotationItem.mapViewLongitude!)
let coordinate = CLLocationCoordinate2DMake(doubleLatitude!, doubleLongitude!)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = annotationItem.annotationTitle
annotation.subtitle = annotationItem.annotationSubtitle
self.mapView.addAnnotation(annotation)
}
}, withCancel: nil)
}
}
现在,我希望每次用户添加新注释时地图都会更新,因此我输入了完全相同的代码,但使用了。观察(.childAdded)
我得到的错误是:
无法将类型为“\uu NSCFString”(0x1060b84f0)的值强制转换为“NSDictionary”(0x1060b92d8)。
快照值的打印说明:
([String:AnyObject])快照值=变量不可用>
我如何解决这个问题
更新
。观察(.value)
工作。但是我仍然想知道为什么.childAdded不这里的问题是.observeSingleEvent(of:.value)
和。observeSingleEvent(.childAdded)
不返回相同的内容
当您调用.observe(.value)
时,它将返回事件发生时包含所有内容的整个节点
但是,当您使用.observe(.childAdded)
时,它将只返回添加到指定路径的内容(即添加的子级)
通过在这两种方法中执行print(snapshot)
,您可以看到这一点,并且您将很容易看到差异
因此,要使用.childAdded访问数据,不需要像使用.observeSingleEvent(of:.value)那样遍历所有子级。相反,你会:
guard let uid = Auth.auth().currentUser?.uid else {
return
}
uidRef.child(uid).child("annotations").observe(.childAdded, with: { (snapshot) in
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
let doubleLatitude = Double(annotationItem.mapViewLatitude!)
let doubleLongitude = Double(annotationItem.mapViewLongitude!)
let coordinate = CLLocationCoordinate2DMake(doubleLatitude!, doubleLongitude!)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = annotationItem.annotationTitle
annotation.subtitle = annotationItem.annotationSubtitle
self.mapView.addAnnotation(annotation)
}
})
另外,我不建议您强制将您的物品像物品那样强制转换为!DataSnapshot
因为如果你在数据库中遗漏了什么,应用程序就会崩溃
相反,我会这样做,使用guard语句:
guard let uid = Auth.auth().currentUser?.uid else {
return
}
uidRef.child(uid).child("annotations").observe(.childAdded, with: { [weak self] (snapshot) in
let annotationKey = snapshot.key
guard let longitude = snapshot.childSnapshot(forPath: "longitude").value as? NSNumber else { return }
guard let latitude = snapshot.childSnapshot(forPath: "latitude").value as? NSNumber else { return }
guard let title = snapshot.childSnapshot(forPath: "title").value as? String else { return }
// Here it really depends on how your database look like.
// ...
// Create your annotation item using the values from the snapshot :
let annotation = AnnotationListItem(id: annotationKey, title: title, longitude: longitue, latitude: latitude)
// Finally add the data to an array :
self?.annotationsArray.append(annotation)
// Or directly to your mapView :
self?.mapView.addAnnotation(annotation)
})
只要让我知道它是否有帮助;D
更新
例如,假设数据库中最初有3个子节点。它会这样做:
姓名首字母:观察员被呼叫:
你有一个孩子
你有孩子2
你有三个孩子
现在,如果添加了另一个子项:
新子级:观察员被调用:
你有四个孩子
等等。这里的问题是,.observeSingleEvent(of:.value)
和.observe(.childAdded)
不返回相同的内容
当您调用.observe(.value)
时,它将返回事件发生时包含所有内容的整个节点
但是,当您使用.observe(.childAdded)
时,它将只返回添加到指定路径的内容(即添加的子级)
通过在这两种方法中执行print(snapshot)
,您可以看到这一点,并且您将很容易看到差异
因此,要使用.childAdded访问数据,不需要像使用.observeSingleEvent(of:.value)那样遍历所有子级。相反,你会:
guard let uid = Auth.auth().currentUser?.uid else {
return
}
uidRef.child(uid).child("annotations").observe(.childAdded, with: { (snapshot) in
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
let doubleLatitude = Double(annotationItem.mapViewLatitude!)
let doubleLongitude = Double(annotationItem.mapViewLongitude!)
let coordinate = CLLocationCoordinate2DMake(doubleLatitude!, doubleLongitude!)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = annotationItem.annotationTitle
annotation.subtitle = annotationItem.annotationSubtitle
self.mapView.addAnnotation(annotation)
}
})
另外,我不建议您强制将您的物品像物品那样强制转换为!DataSnapshot
因为如果你在数据库中遗漏了什么,应用程序就会崩溃
相反,我会这样做,使用guard语句:
guard let uid = Auth.auth().currentUser?.uid else {
return
}
uidRef.child(uid).child("annotations").observe(.childAdded, with: { [weak self] (snapshot) in
let annotationKey = snapshot.key
guard let longitude = snapshot.childSnapshot(forPath: "longitude").value as? NSNumber else { return }
guard let latitude = snapshot.childSnapshot(forPath: "latitude").value as? NSNumber else { return }
guard let title = snapshot.childSnapshot(forPath: "title").value as? String else { return }
// Here it really depends on how your database look like.
// ...
// Create your annotation item using the values from the snapshot :
let annotation = AnnotationListItem(id: annotationKey, title: title, longitude: longitue, latitude: latitude)
// Finally add the data to an array :
self?.annotationsArray.append(annotation)
// Or directly to your mapView :
self?.mapView.addAnnotation(annotation)
})
只要让我知道它是否有帮助;D
更新
例如,假设数据库中最初有3个子节点。它会这样做:
姓名首字母:观察员被呼叫:
你有一个孩子
你有孩子2
你有三个孩子
现在,如果添加了另一个子项:
新子级:观察员被调用:
你有四个孩子
等等谢谢你!为了清楚起见,我最初认为,.childAdded
一次遍历所有现有的子级,然后充当每个添加的子级的侦听器。这不对吗?其次,我应该在viewdiload
中调用我的loadAnnotations
函数,然后在该函数下面调用annotationChildAdded
?不客气@zachenn!您是对的,它确实在一开始会遍历所有现有的子项一次,然后会为添加的每个子项触发。但在这两种情况下,它将只返回特定的子节点,而不是返回包含其所有内容的整个父节点。我更新了我的答案,以便更容易理解。对于第二个问题,我建议您在viewDidAppear方法中编写添加子对象的方法,并确保删除viewDidAppear方法中的所有观察者。此外,在调用childAdded observer之前,您需要删除数组中的所有元素,以避免每次出现视图控制器时重复。MyAnnotationListItem
不是MKAnnotation。没事吧?我想出来了。感谢您的回答,我最终使用.observeSingleEvent(of:.value)
来最初加载项目,然后调用您建议的更新的.observe(.childAdded)
。我现在也了解了很多关于守卫声明的内容,如果让我说的话,因为是你提出来的。我非常感激:)谢谢你!为了清楚起见,我最初认为,.childAdded
一次遍历所有现有的子级,然后充当每个添加的子级的侦听器。这不对吗?其次,我应该在viewdiload
中调用我的loadAnnotations
函数,然后在该函数下面调用annotationChildAdded
?不客气@zachenn!您是对的,它确实在一开始会遍历所有现有的子项一次,然后会为添加的每个子项触发。但在这两种情况下,它将只返回特定的子节点,而不是返回包含其所有内容的整个父节点。我更新了我的答案,以便更容易理解。对于第二个问题,我建议您在viewDidAppear方法中编写添加子对象的方法,并确保删除viewDidAppear方法中的所有观察者。此外,在调用childAdded observer之前,您需要删除数组中的所有元素,以避免每次查看时重复