Search code examples
swiftgoogle-cloud-platformgoogle-cloud-firestorecompletionhandlerquerying

How to populate cells with field names from different documents?


I have a subcollection called events for each of the documents in the collection "school_users". Firestore

When a school user creates an event it's created with the school_id field and placed into that subcollection. What I want to do is display events in the student user's table view controller that were created by the school with same ID (student users have a student ID that matches with a school ID).

I wasn't sure of how to go about this so I tried a couple of things.

    func getStudentID(completion: @escaping ((String?) -> ())) {
    db.collection("student_users").whereField("userID", isEqualTo: user!.uid).getDocuments { (querySnapshot, err) in
        if let err = err {
            print("There was an error fetching the documents: \(err)")
        } else {
            self.studID = querySnapshot!.documents.map { document in
                return StudentID(studentID: (document.get("school_id") as! String))
            }
            
            let fixedID = "\(self.studID)"
            let substring = fixedID.dropFirst(30).dropLast(3)
            let realString = String(substring)
            completion(realString)
            
        }
    }
}

I created a function based off what I've learned from my last questions asked trying to get the student ID as a string.

But before that I was using the same function above without a completion handler and returning a string and tried to input it into a query. Obviously it wouldn't work but i still tried anyways.

   //MARK: - Needs to be fixed
func getSchoolID() {
    db.collectionGroup("events").whereField("school_id", isEqualTo: getStudentID()).getDocuments { (querySnapshot, error) in
        if let error = error {
            print("There was an error fetching the events: \(error)")
        } else {
            self.events = querySnapshot!.documents.map { document in
                return EventName(eventName: (document.get("event_name") as? String) ?? "")
            }
            self.tableView.reloadData()
        }

    }
}

So then the last thing I tried was calling the completion handler in the cellForRowAt tableview method.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: Constants.CellDetails.cellName, for: indexPath)
    
  getStudentID { [weak self] (realString) in
   
        cell.textLabel?.text = self?.events[indexPath.row].eventName

    }
    
    cell.textLabel?.font = UIFont(name: Constants.CellDetails.fontName, size: 22)

    return cell
}

None of these ended up working and I just have a bunch of errors. To recap, basically what I want to do is populate the cells in the Student TableVC with the event names from the schools that they have matching IDs with. Going to throw in some extra photos for more clarification.

Events

So these are events created by the Santa Fe High School admin user and I want them to show up in a Santa Fe Highschool student user's tableview.

Student VC

This is the VC i want them to show up in if the school ID matches. The user signed in does have the same ID as Santa Fe High so I want them to display but that's what I can't figure out. Hopefully you understand what I'm trying to do. Feel free to ask more questions.


Solution

  • This for anyone who runs into the same problem down the road

    So basically what I did was put a whole query in a completion handler.

    Here's how it started.

       func getStudentID(completion: @escaping ((String?) -> ())) {
        db.collection("student_users").whereField("userID", isEqualTo: user!.uid).getDocuments { (querySnapshot, err) in
            if let err = err {
                print("There was an error fetching the documents: \(err)")
            } else {
                self.studID = querySnapshot!.documents.map { document in
                    return StudentID(studentID: (document.get("school_id") as! String))
                }
                
                let fixedID = "\(self.studID)"
                let substring = fixedID.dropFirst(30).dropLast(3)
                let realString = String(substring)
                completion(realString)
                
            }
        }
    }
    

    This function right here gets me the studentID in a string format which I need so I can put it in the query.

    As you can see its a completion handler function. I knew that I needed that exact value and knew I couldn't just do it in the isEqualTo: parameter in the query call, so I decided to test out the function by throwing a print statement in the viewDidLoad(). I was happy to see it come out on the console. So I realized that why not just do the same thing I did in this :

    getStudentID { (studentID) in
            if let id = studentID {
                print(id)
            }
        }
    

    but instead just make the id constant the value in isEqualTo: parameter of the query call.

    That lead me to do this...

    func getSchoolID() -> String {
        
        getStudentID { (studentID) in
            if let idForStudent = studentID {
                self.db.collectionGroup("events").whereField("school_id", isEqualTo: idForStudent).getDocuments { (querySnapshot, error) in
                    if let error = error {
                        print("There was an error fetching the events: \(error)")
                    } else {
                        self.events = querySnapshot!.documents.map { document in
                            return EventName(eventName: (document.get("event_name") as? String) ?? "")
                        }
                        self.tableView.reloadData()
                    }
    
                }
            }
        }
        
        return "\(events)"
    }
    

    This ended up working perfectly and I could remember repeating to myself oh my god countless times. This is the part of coding that I love most, solving that big issue.

    To end off, in order to populate the cell, all I had to was write this line of code

            cell.textLabel?.text = events[indexPath.row].eventName
    

    and it worked perfectly fine. This was my solution and I could've never been more proud.