Невозможно решить проблему асинхронного вызова API Firebase, связанную с извлечением данных, несмотря на использование блоков завершения - PullRequest
1 голос
/ 18 июня 2019

Моя база данных Firebase в реальном времени имеет следующую структуру:

 {
  "friends" : {
    "-LhZw8ryHbE-VIeh1kx6" : {
      "members" : [ "3rTK12GBEQf8WMbLEdAz4Pftkxs1", "guHd7whcqyfFjfkduPoCmryLe0I3" ],
      "title" : "Ansh’s Trials"
    },
    "-LhdBeCVRfDVQBtoAeuf" : {
      "members" : [ "3rTK12GBEQf8WMbLEdAz4Pftkxs1", "guHd7whcqyfFjfkduPoCmryLe0I3" ],
      "title" : "Trial 2"
    },
  },
  "users" : {
    "3rTK12GBEQf8WMbLEdAz4Pftkxs1" : {
      "email" : "anshgodha77@gmail.com",
      "fullname" : "Ansh Godha",
      "provider" : "Firebase"
    },
    "guHd7whcqyfFjfkduPoCmryLe0I3" : {
      "email" : "harvey@davidson.com",
      "fullname" : "Harvey Davidson",
      "provider" : "Firebase"
    }
  }
}

Теперь, в приложении, я создаю группы пользователей, и каждая группа добавляется в базу данных «друзья», как вы можете видеть. Я пытаюсь получить все группы в табличном представлении. В этом табличном представлении я установил заголовок группы в качестве значения «заголовок» для соответствующей группы в поддереве «друзья». Учитывая ключ группы, установка заголовка проста. Тем не менее, похоже, что у меня проблемы с получением имен людей из группы. Обратите внимание, что для сохранения группы в моей модели я храню UID пользователей в этой конкретной группе (см. Дерево JSON). Вот как я пытаюсь это сделать (см. Указатели ниже для получения дополнительной информации о коде):

func getUserFullname(forUID uid: String, handler: @escaping (_  username: String) -> ()) {
        print(5)

            self.REF_USERS.observeSingleEvent(of: .value) { (userSnapshot) in
                print(6)
                guard let userSnapshot = userSnapshot.children.allObjects as? [DataSnapshot] else { return }
                print(7)
                for user in userSnapshot {
                    print(8)
                    if user.key == uid {
                        handler(user.childSnapshot(forPath: "fullname").value as! String)
                    }
                }
            }
    }

func getFullnameList(fromUIDArray uidarr: [String], completion: @escaping (_ nameArr: [String]) -> ()){
    var namearr = [String]()

    print(1)

    for uid in uidarr {
        print(2)
        DataService.instance.getUserFullname(forUID: uid) { (returnedFullName) in
            namearr.append(returnedFullName)
        }
        print(3)
    }

    print(4)
    completion(namearr)

}
  • REF_USERS был определен как Database.database().reference().child("users")
  • DataService - это синглтон, в котором были определены эти 2 метода.
  • Я знаю, что логика моего кода верна, потому что когда я печатаю результаты, я вижу правильные имена. Просто я вижу имена после того, как все остальные части кода закончили выполняться. По сути, nameArr является пустым массивом даже в конце второго метода.

Итак, как мне сделать так, чтобы userFullNames были правильно переданы nameArr? Я попытался сослаться на все другие сообщения SO, и все они, похоже, использовали обработчик завершения таким образом. Спасибо!

PS: я чрезвычайно новичок в Swift и IOS AppDev, поэтому извините, если это все еще очень распространенный вопрос! Но я пытался реализовать разные вещи, которые я нашел в Интернете (например, DispatchQueues), но не смог добиться успеха :(. Кроме того, это одна из моих первых публикаций в StackOverflow, поэтому я извиняюсь, если не хватает подробностей. Будет публиковать дополнительную информацию, необходимую!

1 Ответ

0 голосов
/ 18 июня 2019

Я бы предложил другую структуру. Массивы по своей сути сложны в базах данных NoSQL, и часто есть лучшие варианты.

при условии, что пользовательский узел

users
   uid_0
      name: "Bill"
   uid_1
      name: "Ted"

и предлагаемая структура групп

groups
   group_0
      name: "My Most Excellent Group"
      members:
         uid_0: true
         uid_1: true

Затем какой-то код для чтения групп, распечатка заголовка, затем итерация по каждому дочернему элементу в списке участников, получение этого имени участника из узла users и его печать. Я добавил несколько комментариев, чтобы вы могли немного проще следовать коду.

func readGroups() {
    let groupsRef = self.ref.child("groups")
    groupsRef.observeSingleEvent(of: .value, with: { snapshot in //read all groups at once
        let allGroupsArray = snapshot.children.allObjects as! [DataSnapshot] //put each child into an array as a DataSnapshot
        for groupSnap in allGroupsArray { //iterate over the array so we can read the group name and group members
            let groupName = groupSnap.childSnapshot(forPath: "title").value as? String ?? "No Group Name"
            print("group: \(groupName) has the following members")
            let members = groupSnap.childSnapshot(forPath: "members") //get the members child as a DataSnapshot
            let allMembers = members.children.allObjects as! [DataSnapshot] //put the members into an array
            for memberSnap in allMembers {
                let memberUid = memberSnap.key //get the uid of each member
                let memberRef = self.ref.child("users").child(memberUid) //get a reference to the member
                memberRef.observeSingleEvent(of: .value, with: { childSnap in //read the member in, print name
                    let name = childSnap.childSnapshot(forPath: "name").value as? String ?? "No Name"
                    print("  name: \(name)")
                })
            }
        }
    })
}

и вывод будет

group: My Most Excellent Group has the following members
  name: Bill
  name: Ted
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...