Problems Retrieving Data from Firebase in Swift 3 - swift

I'm trying to retrieve data from firebase, store it in an array which will then be used as a reference for another firebase query. Unfortunately, I can't seem to get it to work properly. The print function at the bottom is always empty. I have tried to call each successive function after the for loops, but I'm probably doing it wrong.
I have 2 branches in firebase that looks a bit like this:
-id
|
Group1
|
ID01: Created on 01.01.16
-idDetails
|
ID01
//name: name01
//description: description01
This is my code:
var array = [String]()
var id = [String]()
var items = [Item]() //Item Class Array
override func viewDidLoad() {
super.viewDidLoad()
getIdFromFirebase()
}
//first task
func getIdFromFirebase(){
for index in array //index is being used as part of path query
{
FireDbase_Main.child("Group1").child("id").child(index).observe(FIRDataEventType.childAdded, with: { (snapshot) in
self.id = (snapshot.key)
id.append(id)
})
getDataForItems()
}
}
//second task
func getDataForItems() {
let newItem = Item()
for index in id //i is being used as part of path query
{
FireDbase_Main.child("idDetails").child(index).observeSingleEvent(of: .value, with: { (snapshot) in
newItem.itemID = snapshot.key
newItem.name = (snapshot.value as! NSDictionary)["name"] as! String
newItem.desc = (snapshot.value as! NSDictionary)["description"] as! String
self.items.append(newItem)
}
printItemsArray(
}
//3rd Task: For now I have put a print here just as a placeholder task.
func printItemsArray(){
print(items.map {$0.itemID})
print(items.map {$0.name})
print(items.map {$0.desc})
}

Related

Cannot fetch / read data from firebase real-time database to UITableViewController

I am able to successfully write data from my application to my firebase real-time database. I cannot retrieve the data and display it in a UIViewController.
I have search stack, YouTube and firebase docs and no luck. I printed out the count and it returns 0. I believe the For loop isn't iterating or its not appending to my array correctly. I have trie force wrapping and unwrapping refRepairs, and placing databaseHandle in from of it.
override func viewDidLoad() {
super.viewDidLoad()
refRepairs = Database.database().reference().child("repairs");
//observing the data changes
refRepairs!.child("repairs").observe(DataEventType.value, with: { (snapshot) in
//if the reference have some values
if snapshot.childrenCount > 0 {
//clearing the list
self.repairList.removeAll()
//iterating through all the values
for repairs in snapshot.children.allObjects as! [DataSnapshot] {
//getting values
let repairObject = repairs.value as? [String: AnyObject]
let brand = repairObject?["brand"]
let id = repairObject?["id"]
let modelNumber = repairObject?["modelNumber"]
//creating artist object with model and fetched values
let repair = RepairModel(id: id as! String?, brand: brand as! String?, modelNumber: modelNumber as! String?)
//appending it to list
print(snapshot.childrenCount)
self.repairList.append(repair)
}
//reloading the tableview
self.doListTableView.reloadData()
}
})
}
I made the changes and it was working!!! Now, I am trying to add records to the database by uid, and I have successfully done it. Now I have the same problem where I can't display the records. I printed the count and it returns 1 record which is correct. Here is the new code.
override func viewDidLoad() {
super.viewDidLoad()
refRepairs = Database.database().reference().child("repairs");
//observing the data changes
//refRepairs!.child("uid").observe(DataEventType.value, with: { (snapshot) in
//observing the data changes
refRepairs!.observe(DataEventType.value, with: { (snapshot) in
//if the reference have some values
if snapshot.childrenCount > 0 {
//clearing the list
self.repairList.removeAll()
//iterating through all the values
for repairs in snapshot.children.allObjects as! [DataSnapshot] {
//getting values
let repairObject = repairs.value as? [String: AnyObject]
let brand = repairObject?["brand"]
let uid = repairObject?["uid"]
let id = repairObject?["id"]
let modelNumber = repairObject?["modelNumber"]
//creating artist object with model and fetched values
let repair = RepairModel(uid: uid as! String?, id: id as! String?, brand: brand as! String?, modelNumber: modelNumber as! String?)
//appending it to list
print(snapshot.childrenCount)
self.repairList.append(repair)
}
//reloading the tableview
self.doListTableView.reloadData()
}
})
}
You are looking another second child called repairs when you are "observing the data changes". Try this:
override func viewDidLoad() {
super.viewDidLoad()
refRepairs = Database.database().reference().child("repairs");
//observing the data changes
refRepairs!.observe(DataEventType.value, with: { (snapshot) in
...
If that don't work. Use the observeSingleEvent method from Firebase

Trouble using child snapshot inside for loop

I have a Firebase DB with "post/(randID)" structure, and Post class that inherits from an Item class. I already wrote a snapshot function that properly takes the value of all child nodes, but am now trying to only take a snapshot of post/ children that match elements of a name array I already have.
I'm properly getting values but not correctly appending temp values to my Item array at the breakpoint. Any help would be much appreciated
----------- CODE -----------
func getWavePosts() {
self.tempPosts = []
for name in self.tempNames {
var postRef = Database.database().reference().child("posts/\(name)")
postRef.observe(.value, with: {snapshot in
var test = snapshot.value as? [String:Any]
var author = test!["author"] as? [String:Any]
var uid = author!["uid"] as? String
var username = author!["username"] as? String
var photoURL = author!["photoURL"] as? String
var url = URL(string: photoURL!)
var imageURL = test!["imageURL"] as? String
var text = test!["text"] as? String
var timestamp = test!["timestamp"] as? Double
var userProfile = UserProfile(uid: uid!, username: username!, photoURL: url!)
var post = Post(id: name, author: userProfile, text: text!, timestamp: timestamp!, imageURL: imageURL!)
self.tempPosts.append(post)
//print(self.tempPosts)
//self.items = self.tempPosts
})
//self.items = self.tempPosts
}
print(self.tempPosts.count)
print(self.items.count)
}
First, your function should have completion with array of Post as parameter
func getWavePosts(_ completion: #escaping ([Post]) -> () )
...now let's meet with DispatchGroup.
First declare new DispatchGroup before foreach loop. Then before you observe postRef enter to dispatchGroup and after you append received Post to an array (define this array within function, don't use global variable) leave dispatchGroup. When every Post is added to an array, call completion in closure of dispatchGroup.notify(queue:)
func getWavePosts(_ completion: #escaping ([Post]) -> () ) {
var tempPosts = []
let dispatchGroup = DispatchGroup()
for name in self.tempNames {
dispatchGroup.enter()
var postRef = Database.database().reference().child("posts/\(name)")
postRef.observe(.value, with: { snapshot in
...
tempPosts.append(post)
dispatchGroup.leave()
})
}
dispatchGroup.notify(queue: .main) {
completion(tempPosts)
}
}
Then you have access to your received posts in closure of this method when you call it
getWavePosts { posts in
... // do whatever you want to
}

How can I pick three random elements out of a dictionary in Swift 4.1

I am having a problem picking three random elements out of a dictionary.
My dictionary code:
query.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let childSnap = child as! DataSnapshot
var dict = childSnap.value as! [String: Any]
}
})
You can use an array if keys are integers.
if you want to use a dictionary only then below mentioned code might be helpful for you
var namesOfPeople = [Int: String]()
namesOfPeople[1] = "jacob"
namesOfPeople[2] = "peter"
namesOfPeople[3] = "sam"
func makeList(n: Int) -> [Int] {
print(namesOfPeopleCount)
return (0..<n).map { _ in namesOfPeople.keys.randomElement()! }
}
let randomKeys = makeList(3)
You can try this for older version Of Swift where randomElement() is not available
let namesOfPeopleCount = namesOfPeople.count
func makeList(n: Int) -> [Int] {
return (0..<n).map{ _ in Int(arc4random_uniform(namesOfPeopleCount)
}
#Satish answer is fine but here's one which is a bit more complete and selects a random user from a list of users loaded from Firebase ensuring a user is only selected once.
We have have an app with two buttons
populateArray
selectRandomUser
and we have a UserClass to store our user data for each user.
class UserClass {
var uid = ""
var name = ""
init(withSnapshot: DataSnapshot) {
let dict = withSnapshot.value as! [String: Any]
self.uid = withSnapshot.key
self.name = dict["Name"] as! String
}
}
and an array to store the users in
var userArray = [UserClass]()
When the populateArray button is clicked this code runs
func populateArray() {
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let user = UserClass(withSnapshot: snap)
self.userArray.append(user)
}
print("array populated")
})
}
and then to select a random user use this code.
func selectRandomUser() {
if let someUser = userArray.randomElement() {
print("your random user: \(someUser.name)")
let uid = someUser.uid
if let index = userArray.index(where: { $0.uid == uid } ) {
userArray.remove(at: index)
}
} else {
print("no users remain")
}
}
This code ensures the same user is not selected twice. Note that this is destructive to the array containing the users so if that's unwanted, make a copy of the array after it's populated and work with that.

Retrieving data from Firebase with Swift 3.0 from a random child path

I'm trying to retrieve an array of items from a directory with a random folder using an existing array of strings as reference
My data looks like this:
Items
- RandomID
-title : "text"
-subtitle: "text"
So far this is what I have tried, but it isn't working:
var array = [String]() //array to use as reference
var returnedItems = [Item]() //array of item objects
func retrieveData()
{
for i in array
{
let ref = main.child("Items")
let query = ref.queryEqual(toValue: i)
query.observeSingleEvent(of: .value, with: { (snapshot) in
let item = Item!
if snapshot.hasChild("title")
{
item.title = (snapshot.value as! NSDictionary)["title"] as? String
}
if snapshot.hasChild("subtitle")
{
item.subtitle = (snapshot.value as! NSDictionary)["subtitle"] as? String
}
returnedItems.append(item)
self.tableView.reloadData()
print("Item: \(self.returnedItems.map { $0.title})")
})
}
}
Any help will be greatly appreciated!
Thanks in advance ;)
If you're trying to retrieve all children, you can just use a single listener (modeled after this example in the documentation):
var array = [String]() //array to use as reference
var returnedItems = [Item]() //array of item objects
func retrieveData() {
query.observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children {
let item = Item!
if child.hasChild("title") {
item.title = (snapshot.value as! NSDictionary)["title"] as? String
}
if child.hasChild("subtitle") {
item.subtitle = (snapshot.value as! NSDictionary)["subtitle"] as? String
}
returnedItems.append(item)
self.tableView.reloadData()
print("Item: \(self.returnedItems.map { $0.title})")
})
}
// returnedItems will still be empty here, since the data hasn't
// been loaded yet. See the note after this code snippet.
}
But note that reading the data will still happen asynchronously here, so returnedItems will still be empty when retrieveData returns.

Append Firebase Data into [String]() in Swift

I have data like below
I want to get the value of all objectIds and append it to a [String]() in Swift. Though when I use the append function, it first adds one, then two, and then three and so on. Below is the code I'm using right now.
var ObjectID: [String]?
override func viewDidLoad() {
super.viewDidLoad()
self.ObjectID = [];
let ref = Firebase(url:"https://blazing-heat-3676.firebaseio.com/results")
ref.queryOrderedByChild("objectId").queryLimitedToLast(201).observeEventType(.ChildAdded) { (snap: FDataSnapshot!) -> Void in
let objectId = snap.value["objectId"] as! String
self.ObjectID?.append(objectId)
print(self.ObjectID)
}
}
What modifications should I make for all objectIds to be in the array.
Firebase have no arrays but if the data looks like an array, Firebase clients will render it as an array. Therefore you can simply convert the result into an array and work with each individual object of this array.
let firebase = Firebase(url: "https://blazing-heat-3676.firebaseio.com/results")
firebase.observeSingleEventOfType(.Value) { (snapshot: FDataSnapshot!) -> Void in
guard let jsonArray: [JSON] = snapshot.value as? [JSON] else {
return
}
var objectIds: [String] = []
for json in jsonArray {
if let id = json["objectId"] as? String {
objectIds.append(id)
}
}
// Print result
print(objectIds)
}
Where JSON is
public typealias JSON = [String : AnyObject]
As an alternative solution - you can model this into query but you get the idea.
var myString: String = ""
ref.observeEventType(.Value, withBlock: { snapshot in
for child in snapshot.children {
let name = child.value.objectForKey("ObjectId") as! String
myString += name
}
print(myString)
})
Also, you may want to re-think your keys (node names) as numeric sequential indexes are hard to work with. You should check into childByAutoId.
Also, Firebase does support arrays via NSArray however, there are usually much better alternatives.