如何通过快速实现观察者从Firebase实时数据库中获取嵌套数据 [英] How to fetch nested data from Firebase realtime database by implementing observer in swift

查看:63
本文介绍了如何通过快速实现观察者从Firebase实时数据库中获取嵌套数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Firebase数据结构

  1. lastLocations(batteryStatus,lat,long,timestamp,uid)
  2. 个人资料(姓名,电话号码,图片,uid)
  3. userFriends(基于uid->多少个朋友-> sessionUid,friendStatus,notify,phoneNumber,uid)

我的代码:

  • I have already created tableview and xib for it.
  • I have created model for last location, profiles, userFriends.
  • I already fetched the friend list but on Observe .ChildAdded
  • My uid zzV6DQSXUyUkPHgENDbZ9EjXVBj2
  • Link: https://drive.google.com/file/d/19cnkY03MXjrTFgzzPCdvmOuosDRvoMx9/view?usp=sharing

问题:

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

要实现的结果:

  • 我需要根据自己的uid在桌面视图上显示朋友列表(姓名,个人资料图片,电池状态,经纬度(地址),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"
      }
    }
  }

}

快捷功能:

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])
        })
    }

推荐答案

注意:写完此答案后,我意识到它已经很长了,但这是一个很大的问题,需要解决的要素很多.

Note: After writing this answer I realized it was way long but this is a big question and there are a lot of elements to address.

我的第一个建议是更改结构,因为处理数据的过程过于复杂.此外,还有不需要的重复数据,因此也应进行更改.例如,这是您的个人资料节点

My first suggestion is to change the structure as it's overly complicated for what's being done with the data. Also, there is repetitive data that's not needed so that should be changed as well. For example, here's your profiles node

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

如您所见,每个子节点都有一个用户ID的键.但是,您还将用户ID也存储为子节点.它们的键是uid,并且将始终可用,因此不需要在那里重复,并且应该删除子节点.

As you can see, each child node has a key of the user id. But, you are also storing the user id as a child node as well. They key is the uid and will always be available so no need for duplication there and the child node should be removed.

基于评论,这是一个更好的结构

Based on comments, this is a much better structure

/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",

然后,要跟踪用户朋友,它就变成了

and then, to keep track of a users friends, it becomes this

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

要加载此用户好友,我们在/userFriends/this_users_id中读取数据,然后遍历加载数据的子节点,以便在tableView中显示

To load this users friends, we read the data at /userFriends/this_users_id and then iterate over the child nodes loading the data for display in the tableView

让我们从一个用于保存每个好友数据的对象开始,然后是一个用作tableView数据源的数组

Lets start with an object that will be used to hold each friends data, and then an array that will be used as a tableView Datasource

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]()

然后是一个函数,用于读取用户节点,遍历用户朋友uid并读取每个用户数据,填充FriendClass对象并将每个对象存储在数组中.请注意,self.ref指向我的Firebase.

Then a functions to read the users node, iterate over the users friends uid's and read in each users data, populating the FriendClass object and storing each in an array. Note that self.ref points to my firebase.

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)
    })
}

现在,我们有了要读取数据的代码,您还希望注意更改.有很多选择,但这是两个.

Now that we have the code to read in the data, you also want to watch for changes. There are a number of options but here's two.

1)我将其称为蛮力.

1) I'll call this brute force.

只需将.childChanged观察者附加到/users节点,并且如果发生更改,则将该更改的节点传递给观察者.如果该节点的密钥与myFriendsDataSource数组中的密钥匹配,请更新该用户.如果不匹配,请忽略它.

Simply attach a .childChanged observer to the /users node and if something changes, that changed node is passed to the observer. If the key to that node matches a key in myFriendsDataSource array, update that user in the array. If no match, then ignore it.

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观察者附加到每个朋友节点-即可在上面的代码示例中完成

For this, we simply attach an .childChanged observer to each friend node - and that can be done within the code example from above

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.
    })
}

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

One last thing: I didn't address this

"friendStatus": "STATUS_ACCEPTED",

我认为只有您接受的朋友才会出现在朋友列表中,因此用法尚不清楚.但是,如果您想使用它,可以这样做

I would think that only friends you accepted are in the friends list so the use is a tad unclear. However, if you want to use it you could do this

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

然后在您遍历好友进行加载时,请忽略那些被拒绝的好友.

and then as you're itering over friends to load, ignore the ones that are declined.

如果您必须保留当前的结构(我不建议这样做),那么此答案中的技术也将适用于该结构,但是,这将需要更多的代码,并且您将需要做很多事情多余的额外数据,因此Firebase的账单会更高.

If you MUST keep your current structure (which I do NOT recommend) the techniques in this answer will work for that structure as well, however, it will be a lot more code and you're going to be moving around a lot of unneeded extra data so the Firebase bill will be higher.

这篇关于如何通过快速实现观察者从Firebase实时数据库中获取嵌套数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆