Getting duplicate Firestore documents on Tableview (Swaft) - arrays

I'm getting duplicate results even after emptying my array first can someone please explain why is this happening I have tried to empty this array DriverOffers in so many different places but still getting the same result duplicate tableView cells everywhere.
func newOrders(){
//self.DriverOffers = [] here same result
let fireStore = Firestore.firestore()
let doc = fireStore.collection("الطلبات")
doc.addSnapshotListener { (query, err) in
if err != nil {
print(err?.localizedDescription ?? "")
}
//self.DriverOffers = []
query?.documentChanges.forEach({ (change) in
//self.DriverOffers = []
switch change.type{
case .added:
for document in query!.documents{
self.DriverOffers = []
let snap = document.data()
let name = snap["name"] as? String ?? ""
let phone = snap["phone"] as? String ?? ""
let time = snap["time"] as? String ?? ""
let marketName = snap["marketName"] as? String ?? "موقع محدد"
let price = snap["amount"] as? String ?? ""
DispatchQueue.main.async {
//self.DriverOffers = []
let offer = driverOrdersData(userUid: userid, name: name, phone: phone, amount: price)
self.DriverOffers.append(offer)
self.DriverOrdersTV.reloadData()
}
}
}
case .removed:
print("removed")
case .modified:
break
}
})
}

Use this method inside the tableViewCell and check.
override func prepareForReuse() {
super.prepareForReuse()
self.nameLabel.text = nil
}

Related

why my firebase listener duplicate the record

in my project i'm try to implement a social app including a friends request using cloud firebase
I can't find a solution to avoid the fire base listener duplicate the record!
at the first opening, the list is correct and update, but if for any reason I add a friends the listener duplicate the record.
if I check my database Firestore the record is only one there is no double record.
if I exit and go back to the view all the record are ok.
here a picture of what happen when I add friends.
I attach my part of my code:
func testUpdatePendingUser(userLoggato: UserModel, utenteADDED: #escaping (UserModel)->(), vettoreUser: #escaping ([UserModel])->()) {
db.collection("user").document(userLoggato.userID).collection("pendingFriends")
.addSnapshotListener(includeMetadataChanges: false) { documentSnapshot, error in
var arrayUtentiStart = [UserModel]()
guard let snapshot = documentSnapshot else { return }
snapshot.documentChanges.forEach { (documentChange) in
switch documentChange.type {
case .added :
debugPrint("add")
let dict = documentChange.document.data()
let name = dict["name"] as? String ?? "na name"
let surname = dict["surname"] as? String ?? "na name"
let email = dict["email"] as? String ?? "na name"
let userLevel = dict["adminLevel"] as? String ?? "unable to get admin level"
let idUser = dict["userID"] as? String ?? "no ID"
let position1 = dict["position"] as? String ?? "na preferance position"
let position2 = dict["position2"] as? String ?? "na preferance position"
let vote = dict["vote"] as? Int ?? 0
self.downloadImageForAdmin(userID: idUser) { (urlImage) in
let utente = UserModel(name: name, surname: surname, email: email, userID: idUser, adminLevel: userLevel, immagine: urlImage, position: position1, position2: position2, vote: vote)
utenteADDED(utente)
arrayUtentiStart.append(utente)
vettoreUser(arrayUtentiStart)
}
case .modified :
debugPrint("mod")
case .removed :
debugPrint("rem")
}
}
}
}
this to be use on the contentView
func newPendinguser(userLoggato: UserModel){
usersPendingNEW = []
testUpdatePendingUser(userLoggato: userLoggato, utenteADDED: { (utenteCambiato) in
if !self.usersPendingNEW.isEmpty { // case is not empty , guess problem is here!!
self.usersPendingNEW.append(utenteCambiato)
}
}) { (vettoreIniziale) in
if self.usersPendingNEW.isEmpty{ // first view appear
self.usersPendingNEW = vettoreIniziale
}
}
}
i think this code will be duplicate your data.
utenteADDED(utente)
arrayUtentiStart.append(utente)
vettoreUser(arrayUtentiStart)
you design utenteADDED(utente) run when first time init, but when you have data in array, that code still run.
Check first run before run this code:
utenteADDED(utente)
Clear the array which is presenting the view and then assign the list received via listener

Why do the values in the array suddenly disappear? Swift

I am new to Swift and I am wondering why the array is empty at the end of the function at the print("array printed2: \(newArr)") line, when at the first print statement print("array printed1: \(newArr)") it is filled with data.
Does anyone have any corrections or suggestions on how to make the data in the array stay?
Thanks in advance, the code for the function is below.
func testRdArr(){
var newArr=[Establishment]()
print("Downloading club data...")
let db = Firestore.firestore()
db.collection("clubs").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let data = document.data()
let name = data["Name"] as? String ?? "Name Unknown"
let description = data["description"] as? String ?? "Description Uknown"
let imageURL1 = data["imgUrl"] as? String ?? "Image Unknown"
let coordX = data["coordX"] as? String ?? "Unknown"
let coordY = data["coordY"] as? String ?? "Unknown"
let openingTimes=data["openingTimes"] as? [String] ?? ["Unknown"]
let weeklyEvents=data["weekly events"] as? [String] ?? ["Unknown"]
let postcode=data["postcode"] as? String ?? "Unknown"
let id = document.documentID
let x = Establishment(name: name, imageURL1: imageURL1, coordX: coordX, coordY: coordY, postcode: postcode, openingTimes: openingTimes, weeklyEvents: weeklyEvents, description: description, type: "club", id: id)
newArr.append(x)
}
}
//line below prints the populated array
print("array printed1: \(newArr)")
print("end of printing array")
}
//at this point array becomes empty, why does this happen and how would you suggest
//fixing it?
print("array printed2: \(newArr)")
}
The download happens in the background and completes after your print at the bottom of the function. It did not become empty. That line happened before the download was done.
See: Waiting for Asynchronous function call to complete

How to append new value in index of the null value in NSArray in swift

I wrote an API which returns the username and amount and at what time the transaction was placed. I got the response from the backend as an array of objects and I stored it as NSArray in client side. And I want to attach that response to labels so I converted that NSArray into String and In Database some fields are empty so the response returns the null values whenever the response returns the null values I am getting this error like "Could not cast the value of NSNUll to NSString". What I thought is replace the null value in that array with string. I tried so much but it always shows the same error how to resolve that problem. This is my code :
let response = JSON as! NSDictionary
//example if there is an id
let deyaPaybalance = response.object(forKey: "Details")
print(deyaPaybalance!)
let Amount1:[Double] = (response.object(forKey: "Amount")) as! [Double]
print("Amount is",Amount1)
for ele:Double in Amount1 {
self.amo += [String(ele)]
}
print("amount is in string",self.amo)
let time = response.object(forKey: "Time") as! NSArray
print("time is", time)
let id = response.object(forKey: "TransactionID")
print("id is",id!)
let name:NSArray = response.object(forKey: "RName") as!NSArray
print("name is",name)
// let len = name.count
for (object) in name.enumerated() {
if let i = name.index(of: "null") {
print("hey it's nnull")
//name.index(of: "null")
}
else {
print("hello")
}
}
print("name",name)
// It is used to get the date from the time tsamp
for element in time {
let ele = element
let formatter = ISO8601DateFormatter()
//print("date is",date1!)
formatter.formatOptions = [.withFullDate,
.withTime,
.withDashSeparatorInDate,
.withColonSeparatorInTime]
self.date2 = formatter.date(from: ele as! String)!
self.anotherFormatter.dateFormat = "MMM dd yyyy, h:mm a" // It is used to to show the date in th form of month year and time
self.anotherFormatter.string(from: self.date2)
let b = self.anotherFormatter.string(from: self.date2)
let final = b.replacingOccurrences(of: ",", with: " ")
let trimmedString = final.trimmingCharacters(in: .whitespaces)
self.dt += [trimmedString] // it is stored the final date and time
print("final tim is",trimmedString)
}
// End of the for loop
print(self.dt)
let contactViewController = self.storyboard?.instantiateViewController(withIdentifier: "TransactionDetails")as! TransactionDetails
contactViewController.method = deyaPaybalance as! [String]
contactViewController.amount = self.amo
contactViewController.timestamp = self.dt
contactViewController.transactionid = id as! [String]
contactViewController.name = name as! [String]// I am getting error here.
self.navigationController?.pushViewController(contactViewController, animated: true)
self.dismiss(animated: false, completion: nil)
UIApplication.shared.endIgnoringInteractionEvents()
You need to use like this to handle null in swift.
if let arr_name = name as? [String]{
contactViewController.name = arr_name
}else{
contactViewController.name = []
}
For replacing nil value in [String]
var name = ["a",nil,"3"]
if let nil_index = name.index(of: nil){
name[nil_index] = "empty"
}
print(name as! [String]) // Output is ["a", "empty", "3"]

Firestore - appending data to a single value inside a struct

How can I update a single value inside of a struct. Currently I'm fetching all of the data inside multiple documents of a collection with the below function. the data structure is as follows:
People - collection
DocumentID
Name: "Joe"
Friends (Object)
1 (Object)
Name: "Sally"
2 (Object)
Name: "Sam"
DocumentID
Name: "Emily"
Friends (Object)
1 (Object)
Name: "Peter".
If I run the below code it jut creates a new array, whereas I would like to merge the two together. Any help is greatly appreciated. Many thanks!!
func loadData() {
let userRef = db.collection("Users").document(user!)
let docRef = userRef.collection("People")
docRef.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
if let snapshot = querySnapshot {
for document in snapshot.documents {
let data = document.data()
let name = data["name"] as? String ?? ""
let newPeople = People(name: name, friends: [:])
self.peopleArray.append(newPeople)
if let friends = data["Friends"] as? [String: Any] {
for (key, _) in friends {
let number = friends[key] as? [String: Any] ?? [:]
let friendsName = number["name"] as? String ?? ""
\\ The code in which I want to update the value of friendsName into the previous set array
let newFriends = People(name: name, friendsName: friendsName)
self.peopleArray.append(newFriends)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}
}
What I would like the array to look like:
[ name: Joe, friends: ["name": Sally, "name": Sam]], [name: Emily, friends: [name: Peter]]
Updated Code
var friendsName: [String: [String: Int]] = [:]
var friendsArray = [String: Int]()
func loadData() {
let userRef = db.collection("Users").document(user!)
let docRef = userRef.collection("People")
docRef.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
if let snapshot = querySnapshot {
for document in snapshot.documents {
let data = document.data()
let name = data["name"] as? String ?? ""
if let friends = data["Friends"] as? [String: Any] {
for friend in friends {
let key = friend.key
let values = friend.value
let friendsDict = friends[key] as? [String: Any] ?? [:]
let friendsNameString = friendsDict["name"] as? String ?? ""
self.friendsArray.updateValue(friendsNameString, forKey: key)
}
self.friendsName.updateValue(self.friendsArray, forKey: "name")
let newPeople = People(name: name, friends: self.friendsName)
self.peopleArray.append(newPeople)
self.friendsArray.removeAll()
self.friendsName.removeAll()
}
}
}
}
}
}
The way you are doing it seems a little too complex to what you require. Firestore has quite fast queries so you can have different collections for people and friends.
Lets say for example that in your "people" collection you have the names of all the users and other properties that you may need... Email, age, gender, etc etc.
Then in a second collection you can add the relations for each user. For example, "Friends" contains documents with fields friendA: AAA, FriendB: BBB. Then you won't have any duplicates in your data.
The way you are doing it creates too many duplicated that you don't need. For example, in your Joe document you have set that Sally is a friend. But then in your Sally document you will have to set that Joe is a friend. This makes the database more difficult to maintain and with a lot of duplicates.
If you make two different collections for People and Friends the code for fetching also becomes simpler.

Passing a struct to construct an array of Structs

I'm trying to create an array of structs by creating a function to fetch data from firestore and then passing the three structs fetched to an array of structs. Here is my code:
func fetchUsers() {
var user1: User
var user2: User
var user3: User
docRef = Firestore.firestore().document("users/user1")
docRef.getDocument { (docSnapshot, error) in
guard let docSnapshot = docSnapshot, docSnapshot.exists else { return }
let myData = docSnapshot.data()
let fName = myData!["name"] as? String ?? ""
let fUsername = myData!["username"] as? String ?? ""
let fBioText = myData!["bioText"] as? String ?? ""
let user = User(name: fName, username: fUsername, bioText: fBioText, profileImage: #imageLiteral(resourceName: "mauricioprofileimage"))
user1 = user
}
self.docRef = Firestore.firestore().document("users/user2")
self.docRef.getDocument { (docSnapshot, error) in
guard let docSnapshot = docSnapshot, docSnapshot.exists else { return }
let myData = docSnapshot.data()
let fName = myData!["name"] as? String ?? ""
let fUsername = myData!["username"] as? String ?? ""
let fBioText = myData!["bioText"] as? String ?? ""
let user = User(name: fName, username: fUsername, bioText: fBioText, profileImage: #imageLiteral(resourceName: "trumpprofileimage"))
user2 = user
}
self.docRef = Firestore.firestore().document("users/user1")
self.docRef.getDocument { (docSnapshot, error) in
guard let docSnapshot = docSnapshot, docSnapshot.exists else { return }
let myData = docSnapshot.data()
let fName = myData!["name"] as? String ?? ""
let fUsername = myData!["username"] as? String ?? ""
let fBioText = myData!["bioText"] as? String ?? ""
let user = User(name: fName, username: fUsername, bioText: fBioText, profileImage: #imageLiteral(resourceName: "amandaprofileimage"))
user3 = user
}
return [user1, user2, user3]
}
let users: [User] = fetchUsers()
The problem I'm getting is that when I try to fill my users array, it says that it doesn't recognize the user1, user2 and user3 that I created in the Fetchfuncion.
Ps: I'm using firestore. Thank you for all the help!
Put each request in a group, and only once all three users have been retrieved, return the array of users. What's happening with your code is that it's returning an array of empty users (or perhaps 1-3 inconsistently), due to the fact that these calls are asynchronous so the data returned from a firebase request isn't guaranteed to exist when the array is returned.
let group = DispatchGroup()
group.enter()
make_async_request_1 {
// Process response
group.leave()
}
group.enter()
make_async_request_2 {
// Process response
group.leave()
}
group.enter()
make_async_request_3 {
// Process response
group.leave()
}
group.notify(queue: .main) {
// This will run after all 3 group.leave() calls are made
}

Resources