Как получить вложенные данные из базы данных Firebase в реальном времени, внедрив обозреватель в swift - PullRequest
2 голосов
/ 24 октября 2019

Структура данных Firebase

  1. lastLocations (batteryStatus, lat, long, timestamp, uid)
  2. профили (имя, номер телефона, фотография, uid)
  3. userFriends (основано на uid -> сколько друзей -> разговорUid, friendStatus, notify, phoneNumber, uid)

Мой код:

  • Я уже создал tableview и xib для него.
  • Я создал модель для последнего местоположения, профилей, друзей пользователя.
  • Я уже получил список друзей, но в Observe .ChildAdded
  • Мой uid zzV6DQSXUyUkPHgENDbZ9EjXVBj2
  • Ссылка: https://drive.google.com/file/d/19cnkY03MXjrTFgzzPCdvmOuosDRvoMx9/view?usp=sharing

Проблемы:

  • Невозможно понять, как получить местоположение и профиль с помощью списка друзей эффективным способом с наблюдателем, поэтому любые изменения отражаются. Firebase - это асинхронный процесс.
  • реализация наблюдателя, поэтому данные загружаются не полностью каждый раз

Достигнутых результатов:

  • Мне нужно показать список друзей(имя, изображение профиля, состояние батареи, широта (адрес), метка времени) в табличном представлении на основе моего идентификатора пользователя.

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

1 Ответ

1 голос
/ 25 октября 2019

Примечание: после написания этого ответа я понял, что это было очень долго, но это большой вопрос, и есть много элементов для рассмотрения.

Мое первое предложение - изменить структуру, так как она слишком сложна длячто делается с данными. Кроме того, есть повторяющиеся данные, которые не нужны, поэтому их также следует изменить. Например, вот ваш узел профилей

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

Как видите, у каждого дочернего узла есть ключ идентификатора пользователя. Но вы также сохраняете идентификатор пользователя как дочерний узел. Они являются ключом 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 и затем перебираем дочерние узлы, загружая данные для отображения вtableView

Давайте начнем с объекта, который будет использоваться для хранения данных каждого друга, а затем массива, который будет использоваться в качестве источника данных tableView

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 указывает на мою базу данных.

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Счет будет выше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...