如何通过在swift中实现observer从Firebase实时数据库中获取嵌套数据



Firebase数据结构

  1. lastLocations(电池状态、lat、long、timestamp、uid(
  2. 配置文件(姓名、电话号码、图片、uid(
  3. userFriends(基于uid->有多少个朋友->conversationUid、friendStatus、notify、phoneNumber、uid(

我的代码:

  • 我已经为它创建了tableview和xib
  • 我已经为最后一个位置,个人资料,用户朋友创建了模型
  • 我已经拿到了朋友名单,但在观察上。ChildAdded
  • 我的uidzzV6DQSXUyUkPHgENDbZ9EjXVBj2
  • 链接:https://drive.google.com/file/d/19cnkY03MXjrTFgzzPCdvmOuosDRvoMx9/view?usp=sharing

问题:

  • 不知道如何获取位置&带有好友列表的个人资料是观察者的有效方式,因此任何变化都会反映出来。Firebase是异步进程
  • observer实现,因此数据不会每次都完全加载

要实现的结果:

  • 我需要根据我的uid在tableview上显示朋友列表(姓名、个人资料图片、电池状态、lat-long(地址(、timeStamp(

Firebase JSON

{
"lastLocations": {
"FTgzbZ9uWBTkiZK9kqLZaAIhEDv1": {
"batteryStatus": 22,
"latitude": 40.9910537,
"longitude": 29.020425,
"timeStamp": 1556568633477,
"uid": "FTgzbZ9uWBTkiZK9kqLZaAIhEDv1"
},
"zzV6DQSXUyUkPHgENDbZ9EjXVBj2": {
"batteryStatus": 88,
"latitude": 41.0173995,
"longitude": 29.1406086,
"timeStamp": 1571778174360,
"uid": "zzV6DQSXUyUkPHgENDbZ9EjXVBj2"
}
},
"profiles": {
"FTgzbZ9uWBTkiZK9kqLZaAIhEDv1": {
"fcmToken": "fp09-Y9ZAkQ:APA91bFgGB1phr4B9gZScnz7ngpqTb5MchgWRFjHmLCVmWGMJVsyFx0rtrz7roxzpE_MmuSaMc4is-XIu7j718qjRVCSHY4PvbNjL1LZ-iytaeDP0oa8aJgE02wET3cXqKviIRMH",
"name": "Skander",
"phoneNumber": "+95644125503",
"uid": "FTgzbZ9uWBTkiZK9kqLZaAIhEDv1"
},
"zzV6DQSXUyUkPHgENDbZ9EjXVBj2": {
"fcmToken": "enMneewiGgg:APA91bHyA4HypWUYhxGTUTTch8ZJ_6UUWhEIXRokmR-Y-MalwnrtV_zMsJ9p-sU_ZT4pVIvkmtJaCo7LFJYJ9ggfhc1f2HLcN9AoIevEBUqyoMN-HDzkweiUxAbyc84XSQPx7RZ1Xv",
"name": "Murad",
"phoneNumber": "+915377588674",
"picture": "profile/zzV6DQSXUyUkPHgENDbZ9EjXVBj2/a995c7f3-720f-45bf-ac58-b2df934e3dff.jpeg",
"uid": "zzV6DQSXUyUkPHgENDbZ9EjXVBj2"
}
},
"userFriends": {
"FTgzbZ9uWBTkiZK9kqLZaAIhEDv1": {
"zzV6DQSXUyUkPHgENDbZ9EjXVBj2": {
"conversationUid": "-L_w2yi8gh49GppDP3r5",
"friendStatus": "STATUS_ACCEPTED",
"notify": true,
"phoneNumber": "+915377588674",
"uid": "zzV6DQSXUyUkPHgENDbZ9EjXVBj2"
}
},
"zzV6DQSXUyUkPHgENDbZ9EjXVBj2": {
"FTgzbZ9uWBTkiZK9kqLZaAIhEDv1": {
"conversationUid": "-L_w2yi8gh49GppDP3r5",
"friendStatus": "STATUS_ACCEPTED",
"notify": true,
"phoneNumber": "+915644125503",
"uid": "FTgzbZ9uWBTkiZK9kqLZaAIhEDv1"
}
}
}
}

Swift功能:

func getFrndDataList(){
AppData.removeAll()
ref.child("userFriends").child("zzV6DQSXUyUkPHgENDbZ9EjXVBj2").observe(.childAdded, with: { (snapshot) in
guard let data = try? JSONSerialization.data(withJSONObject: snapshot.value as Any) else { return }
let frndList = try? JSONDecoder().decode(Friend.self, from: data)
self.AppData.append(frndList!)
self.tableView.reloadData()
print([frndList])
})
}

注意:写下这个答案后,我意识到它很长,但这是一个大问题,有很多元素需要解决。

我的第一个建议是改变结构,因为它对数据的处理过于复杂。此外,还有一些不需要的重复数据,因此也应该进行更改。例如,这里是您的配置文件节点

"profiles": {
"FTgzbZ9uWBTkiZK9kqLZaAIhEDv1": {
"fcmToken": "fp09-Y9ZAkQ:APA91bFgGB1phr4B9gZScnz7ngpqTb5MchgWRFjHmLCVmWGMJVsyFx0rtrz7roxzpE_MmuSaMc4is-XIu7j718qjRVCSHY4PvbNjL1LZ-iytaeDP0oa8aJgE02wET3cXqKviIRMH",
"name": "Skander",
"phoneNumber": "+95644125503",
"uid": "FTgzbZ9uWBTkiZK9kqLZaAIhEDv1" <- remove this, not needed.
},

正如您所看到的,每个子节点都有一个用户id的键。但是,您也将用户id存储为子节点。它们的关键字是uid,并且将始终可用,因此不需要在那里进行复制,并且应该删除子节点。

根据评论,这是一个更好的结构

/users
FTgzbZ9uWBTkiZK9kqLZaAIhEDv1
"batteryStatus": 22,
"latitude": 40.9910537,
"longitude": 29.020425,
"timeStamp": 1556568633477,
"fcmToken": "fp09-Y9ZAkQ:APA91bFgGB1phr4B9gZScnz7ngpqTb5MchgWRFjHmLCVmWGMJVsyFx0rtrz7roxzpE_MmuSaMc4is-XIu7j718qjRVCSHY4PvbNjL1LZ-iytaeDP0oa8aJgE02wET3cXqKviIRMH",
"name": "Skander",
"phoneNumber": "+95644125503",
"conversationUid": "-L_w2yi8gh49GppDP3r5",
"friendStatus": "STATUS_ACCEPTED",
"notify": true,
"phoneNumber": "+915377588674",

然后,为了跟踪用户的朋友,它变成了这个

/userFriends
zzV6DQSXUyUkPHgENDbZ9EjXVBj2 //this user
FTgzbZ9uWBTkiZK9kqLZaAIhEDv1: true //their friend
IRoo0lbhaihioSSuFETngEEFEeoi: true //another friend

为了加载这些用户朋友,我们读取/userFriends/this_users_id上的数据,然后迭代加载数据的子节点以在表中显示查看

让我们从一个将用于保存每个朋友数据的对象开始,然后是一个将用作表的数组查看数据源

class FriendClass {
var uid = ""
var name = ""
//var profilePic
//var batteryStatus
init(withSnapshot: DataSnapshot) {
self.uid = withSnapshot.key
self.name = withSnapshot.childSnapshot(forPath: "name").value as? String ?? "No Name"
}
}
var myFriendsDataSource = [FriendClass]()

然后,a函数读取users节点,迭代users-friends-uid,并读取每个用户的数据,填充FriendClass对象并将每个对象存储在数组中。注意self.ref指向我的燃烧基地。

func loadUsersFriends() {
let uid = "zzV6DQSXUyUkPHgENDbZ9EjXVBj2"
let myFriendsRef = self.ref.child("userFriends").child(uid)
myFriendsRef.observeSingleEvent(of: .value, with: { snapshot in
let uidArray = snapshot.children.allObjects as! [DataSnapshot]
for friendsUid in uidArray {
self.loadFriend(withUid: friendsUid.key)
}
})
}
func loadFriend(withUid: String) {
let thisUserRef = self.ref.child("users").child(withUid)
thisUserRef.observeSingleEvent(of: .value, with: { snapshot in
let aFriend = FriendClass(withSnapshot: snapshot)
self.myFriendsDataSource.append(aFriend)
})
}

现在我们有了要读取数据的代码,您还需要注意变化。有很多选择,但这里有两个。

1( 我称之为蛮力。

只需将一个.childChanged观察者附加到/users节点,如果有什么变化,则将该变化的节点传递给观察者。如果该节点的键与myFriendsDataSource数组中的键匹配,则更新数组中的该用户。如果没有匹配,则忽略它。

func watchForChangesInMyFriends() {
let usersRef = self.ref.child("users")
usersRef.observe(.childChanged, with: { snapshot in
let key = snapshot.key
if let friendIndex = self.myFriendsDataSource.firstIndex(where: { $0.uid == key} ) {
let friend = self.myFriendsDataSource[friendIndex]
print("found user (friend.name), updating")
//friend(updateWithSnapshot: snapshot) //leave this for you to code
}
})
}

2( 选择性观察

为此,我们只需在每个朋友节点上附加一个.childChanged观察器,这可以在上面的代码示例中完成

func loadFriend(withUid: String) {
let thisUserRef = self.ref.child("users").child(withUid)
thisUserRef.observeSingleEvent(of: .value, with: { snapshot in
let aFriend = FriendClass(withSnapshot: snapshot)
self.myFriendsDataSource.append(aFriend)
//add an observer to this friends node here.
})
}

最后一件事:我没有解决这个

"friendStatus": "STATUS_ACCEPTED",

我认为只有你接受的朋友在朋友列表中,所以用途有点不清楚。然而,如果你想使用它,你可以做这个

/userFriends
zzV6DQSXUyUkPHgENDbZ9EjXVBj2 //this user
FTgzbZ9uWBTkiZK9kqLZaAIhEDv1: "STATUS_ACCEPTED"
IRoo0lbhaihioSSuFETngEEFEeoi: "STATUS_DECLINED"

然后,当你在朋友上迭代加载时,忽略那些被拒绝的朋友。

如果你必须保留你当前的结构(我不建议(,这个答案中的技术也适用于该结构,然而,它将是更多的代码,你将移动大量不需要的额外数据,因此Firebase的费用将更高。

最新更新