Firestore - appending data to a single value inside a struct - arrays

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.

Related

Getting duplicate Firestore documents on Tableview (Swaft)

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
}

Swift, get data from [String: Any] type value

I have a value like this;
["id1": {
name = "iphone 6s";
price = 330;
}, "id2": {
name = iphone7s;
price = 500;
}]
I dont know what the id's are. But I need to get the name and the price data from that data.
EDIT: Here is the getting value code;
database.child("SortedItems").child("byName").observeSingleEvent(of: .value, with: { snapshot in
guard let value = snapshot.value as? [String: Any] else {
return
}
Assuming your data structure matches what you have quoted at the top, you should be able to cast your first line as:
guard let data = snapshot.value as? [String:[String:Any]]
If not, t would be helpful to see what the console looks like if you print snapshot.value
Then, the following from my previous answer should work:
let ids = data.keys //get just the ids/keys
//iterate through and print the name and price once they're cast to the correct types
data.forEach { (key,value) in
if let name = value["name"] as? String, let price = value["price"] as? Int {
print(name)
print(price)
}
}
//declare a model type that conforms to Codable
struct Phone : Codable {
var name : String
var price : Int
}
data.forEach { (key, value) in
do {
let jsonData = try JSONSerialization.data(withJSONObject: value, options: [])
//decode the Phone model
let phoneModel = try JSONDecoder().decode(Phone.self, from: jsonData)
print(phoneModel)
} catch {
//handle error
}
}
You may also want to look at the Firebase docs, which give some more detail about getting your data, including converting to custom objects: https://firebase.google.com/docs/firestore/query-data/get-data

Accessing Array outside of the loop [duplicate]

This question already has an answer here:
Assign value of a Firestore document to a variable
(1 answer)
Closed 2 years ago.
I have an Array where some different UserID's are stored.
Each UserID is connected with their corresponding data in Firestore.
So now I want to fetch all the JSON Data from the UserID's and make them accessible to decode them later.
Therefore the fetched Data (coming as a Dictionary) from each user must be accessible separately.
I tried it with that way:
var fetchedIDs = ["ID1", "ID2", "ID3"]
var finalArray = [[String: Any]]()
for id in fetchedIDs{
let docRef = Firestore.firestore().collection("Data").document(id)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let myDict: [String: Any] = document.data() as! [String: Any]
finalArray.append(myDict)
}
}
}
}
But the problem is that the finalArray (like finalArray[0]) is just accessible in the For-Loop.
It should be accessible outside of the loop and look like that:
finalArray[0] should have the Dictionary data from ID1
finalArray[1] should have the Dictionary data from ID2
Maybe I am thinking to complicated for that..
Can someone help me out ?
Is there generally a good source for learning about scopes and how the data should be accessed like in that scenario?
Thanks!
Finally get it working with the following code:
var fetchedIDs = ["ID1", "ID2", "ID3"]
func loadData(com:#escaping( ([[String: Any]]) -> ())){
var myArray = [[String: Any]]()
for id in fetchedIDs{
let refLike = db.collection("Data").document(id)
refLike.getDocument { (document, error) in
if let err = error {
print("Error getting documents: \(err)")
} else {
let myDict: [String: Any] = document?.data() as! [String: Any]
myArray.append(myDict)
}
com(myArray)
}
}
}
loadData(){ arr in
if (arr.count == fetchedIDs.count){
print ("User 1 has the following data: \(arr[0])")
print ("User 2 has the following data: \(arr[1])")
}
}

Reading an array in Firebase

I need some help in reading the "users" array in my Firebase structure as shown below.
I use the below function to grab the data from the channels table.
func observeChannels(channelId: String, onSuccess: #escaping (ChannelModel) -> Void) {
DB_REF_CHANNELS.child(channelId).observeSingleEvent(of: .value) { (snapshot) in
if let dict = snapshot.value as? [String: Any] {
let channel = ChannelModel.transformChannel(dict: dict, key: snapshot.key)
onSuccess(channel)
}
}
}
This is then passed to the ChannelModel class so I can grab each individual data.
class ChannelModel {
var channelId: String?
var channelName: String?
var ownerId: String?
var users: Dictionary<String, Any>? // Array of users }
extension ChannelModel {
static func transformChannel(dict: [String: Any], key: String) -> ChannelModel {
let channel = ChannelModel()
channel.channelId = key
channel.channelName = dict["channelName"] as? String
channel.ownerId = dict["ownerId"] as? String
channel.users = dict["users"] as? Dictionary<String, Any>
return channel
}}
The observeChannels function is then called which returns me an object of all the channels.
observeChannels(channelId: channelId, onSuccess: { (channel) in
self.channels.insert(channel, at: 0)
self.tableView.reloadData()
let user = channel.users!
print(user)
})
When I run the above function to get the users, my output is as follows:
["pZaEJ5aAAkR7WzgIJ4Wqf10jXot1": 1, "asdasldljAlsjkasldj": 1]
I was wondering how would I just get the key value - i.e:
["pZaEJ5aAAkR7WzgIJ4Wqf10jXot1", "asdasldljAlsjkasldj"]
Thanks in advance!
Try this:
let usersDictionary = channel.users!
usersArray = Array(usersDictionary.keys)
print(usersArray)
instead of:
let user = channel.users!
print(user)

Getting Data from JSON Swift

Can Anyone Help me with this
my data after parsing a JSON URL is
{
AREA = (
{
"area_name" = "Bhaktamadhu Nagar";
"city_id" = 4;
id = 31;
price = "100.00";
},
{
"area_name" = "Gandamunda";
"city_id" = 4;
id = 32;
price = "100.00";
}
);
}
and there is a lot more.
I want to fetch only area_name and price values in an array
my code is something like that
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! NSDictionary
print(parsedData)}
I am getting my Upper format in the parsedData
What is the exact code for getting my area_name and price which should store in two separate arrays as aname[] and price[]
Please don't mark it as a duplicate already searched a lot before posting this.
Your JSON data is converted into [String: AnyObject].
AREA data is [[String: AnyObject]] so create a [String: AnyObject] array. and getting a one by one value from array.
How to fetch JSON data from a url using URLSession?
try this code. it's helpfull
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let jsonData = data {
do {
let parsedData = try JSONSerialization.jsonObject(with: jsonData, options: .mutableLeaves) as! [String: AnyObject]
if let area = parsedData["AREA"] as? [[String: AnyObject]] {
for a in area {
areaNameArr.append(a["area_name"])
priceArr.append(a["price"])
print(a)
}
}
}
catch let error {
debugPrint(error)
}
}
else {
debugPrint(error as Any)
}
}.resume()
Use the SwiftyJSON Lib.
It’s easy and fast.
I am using it and it’s very helpful in this way:
let session = URLSession(configuration: URLSessionConfiguration.ephemeral)
self.Task = session.dataTask(with: RequestLink as URLRequest , completionHandler: { (data,response,error) in
if error != nil {
print(error as Any)
}
let ReadJson4Rest = JSON(data: data!)
if let Rest_Details = ReadJson4Rest["Result"].array{
for Details in Rest_Details {
let Comment = Details.dictionaryValue["Comment"]!
let UserName = Details.dictionaryValue["User_ID"]!
if Comment != nil {
let FirstChar = UserName.description.characters.first
self.GetUserImage(UserName: UserName.string! ,AlphabetCat: (FirstChar?.description)!)
DispatchQueue.main.async {
self.CommentValue.append(Comment.string!)
self.UserNames.append(UserName.string!)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "Load"), object: nil)
}
}
}
}
})
self.Task?.resume()
}

Resources