0

What doesn't work the first time?:

The order in which database entries that I fetch displays

I am running this

1st part In ViewDiDLoad

let thisUserRef = Database.database().reference().child("users").child(uid)
let myPeopleRef = thisUserRef.child("likers")
myPeopleRef.queryLimited(toLast: 30).observeSingleEvent(of: .value, with: { snapshot in
    let userArray = snapshot.children.allObjects as! [DataSnapshot]
    for person in userArray.reversed() where uid == Auth.auth().currentUser?.uid  {
        let personUid = person.value as! String
        self.printPersonInfo(uid: personUid) //// this calls a DispatchQueue.main.async that appends the data in the array and reloads
    }
})

  func printPersonInfo(uid: String) {
        print(uid)
        let usersRef = Database.database().reference().child("users")
        let thisUser = usersRef.child(uid)
        thisUser.observeSingleEvent(of: .value, with: { snapshot in
            let xx = snapshot.childSnapshot(forPath: "xx").value as? String ?? "No Entry"
            let yy = snapshot.childSnapshot(forPath: "yy").value as? String ?? "No Entry"
            let rr = snapshot.childSnapshot(forPath: "rr").value as? String ?? "No Entry"
            let zz = snapshot.childSnapshot(forPath: "zz").value as? String ?? "No Entry"
            let ll = snapshot.childSnapshot(forPath: "ll").value as? String ?? "No Entry"
            let p = Usery(xx: xx, yy: yy, rr: rr, zz: zz, ll: ll)
            self.person.append(p)
            print(self.person, "person")
            self.table.reloadData()
        })
}
//////this gets the various data elements of the users that get displayed. 
///////So to summarize, the query determines which users get displayed. This func determined which data of those users gets displayed.

Example of database entires of last 30

user K
user B
user F
user G
user K
user B
user K
.....

The only time this doesn't work is if you install the app clean for the first time. Then the order in which it displays is

user K
user K
user B
user B
user F
user G

JSON

users 
  uid 
   likers
      chailbyAutoID: uid1
      chailbyAutoID: uid2
  
florida27
  • 67
  • 1
  • 7
  • 1
    The correlation between the code in the question and the output is not clear. What does *self.printPersonInfo(uid: personUid)* have to do with the output? uid's are unique so those don't appear to be uid's. Can you clarify the question, the code and the output? – Jay May 12 '21 at 17:56
  • @Jay My bad. I forgot to comment in self.printPersonInfo(uid: personUid). I have just done so and added its code to the question. Basically it calls a DispatchQueue.main.async that appends the data in the array and reloads. You are right that those don't look like UIDs. I was just trying to show the form in which they erroneously display. I have just added **user** k etc to them to make it more clear what they are. – florida27 May 12 '21 at 21:11
  • You don't need this `DispatchQueue.main.async{` - Firebase closures are asynchronous and UI updates are always called on the main thread. That's really the main issue and what's causing the jumble. Also, you're going to want to populate the array first, and THEN reload your tableview. Otherwise it's reloading over and over which can cause flicker. – Jay May 12 '21 at 21:52
  • I removed `DispatchQueue.main.async{}` but is still acts that way on first install. What I find interesting is that is is putting it in the correct order, just with recurring users being lumped together – florida27 May 13 '21 at 15:23
  • Can you format and update your code? To get to an answer we need to see where you are? I still think it's an asynchronous issue. – Jay May 13 '21 at 15:40
  • Yeah the weird think for me is still that is groups the same users together in the ranking. In a pure async issue I'd expect it to be more random. I have just formatted and updated the code. I added more details where I previously put ..... in `func printPersonInfo` – florida27 May 13 '21 at 20:10
  • It still appears to be an asynch issue. The code says get data x, get data y, get data z using `observeSingleEvent` which is an asynchronous call. Suppose data x was 100gb and data y and 1k. Because it's asynchronous, y could return the data before x so the ordering will be intermittent. Order could be maintained by either populating and sorting in code, or leverage DispatchGroups. I also suspect something else; if the nodes are the same size, they will often return in the correct order. The example data on first load is K B F G which happens to match the order in the first example K B F G – Jay May 15 '21 at 13:20
  • Interesting. That makes sense in a way. I would be surprise, however, if the nodes are the same size. In tests with more repeats than the ones I mentioned here, the nodes have differing number of repeats. For example, x might have 5 repeats, whereas y might have 2. If they are then the same size, it would surprise me. On the dispatch groups, how would you use them here, because there in no code where I am currently separating the fetching across different users? Ie instruction to fetch data is applied to all at the same time. – florida27 May 16 '21 at 22:39

1 Answers1

0

One possible solution is to use DispatchGroups to control the loading of the data, which therefore would control the sequence in which a class var array is populated.

In this example, we'll read all of the users uids from a users node and populate an array. It would look like this

users
   uid_0
      name: "Wilbur"
   uid_1:
      name: "Orville"

We'll then go back and re-read additional user data from that node, using array.reversed() so they populate a result array in reversed order. We'll need a class to store each user data and then a class array to store all of the users are they are read in. Here's the class vars

struct PersonClass {
   var uid = ""
   var name = ""
}

var personArray = [PersonClass]()

and the code to populate them in reverse order

func readUserDataUsingDispatch() {
    let ref = self.ref.child("users") //self.ref points to my firebase
    ref.observeSingleEvent(of: .value, with: { snapshot in

        //this just builds and array of user id's - we'll iterate through them to read the rest of the data
        let idArray = snapshot.children.allObjects as! [DataSnapshot]
        var allKeys = [String]()
        for userSnap in idArray {
            allKeys.append( userSnap.key ) //[uid_0, uid_1] etc
        }

        let group = DispatchGroup()

        for uid in allKeys.reversed() {
            group.enter()
            let thisUser = ref.child(uid)

            thisUser.observeSingleEvent(of: .value, with: { userSnapshot in
                let name = userSnapshot.childSnapshot(forPath: "name").value as! String
                let person = PersonClass(uid: uid, name: name)
                self.personArray.append(person)
                print("added uid")
                group.leave()
            })
        }

        group.notify(queue: .main) {
            print("done")
        }
    })
}

when you run the code this is the output, indicating the data is loaded

added uid
added uid
done

and then when printing the class var personArray it's the users in reverse order.

uid_1 Orville
uid_0 Wilbur
Jay
  • 34,438
  • 18
  • 52
  • 81
  • Thanks.Haven't gotten it to work yet. The latter (mostly the last 10 of 30 still appear bundled). I think until the `for uid in allKeys..` part your answer and my code is consistent. I use ` snapshot.children.allObjects as! [DataSnapshot] ` which includes childbyAutoID and key, whereas your answer only uses key. But I don't think that is where the error come from. I think it is the part after. I can't format my code close to yours for that part because I need to call the function that gets data for users that need to get displayed. Their uids are under current user uid. I added JSON to Q. – florida27 May 19 '21 at 15:45