I am new to Firebase and I am trying to get the children from a DataSnapshot and add the keys to a list. But I am getting an error. Am i not unwrapping the data correctly or what am i doing wrong?
var arr: Array<String> = []
override func viewDidLoad() {
super.viewDidLoad()
populateArray()
print(arr)
func populateArray(){
ref.child("Sept24 - Oct 24").observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot.childrenCount)
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot {
//print(rest.key)
self.arr.append(rest.key)
}
DispatchQueue.main.async{
self.sideMenuTable.reloadData()
}
})
print(arr)
}
arr is a global array with type string and i am calling populateArray() in the viewDidLoad() function.
Related
I have a function that retrieves the "users" node of my Firebase database, and I am trying to assign a global variable equal to the number of users that are in this "users" node like so:
func getUsers(completionHandler:#escaping (Int) -> ()) {
let ref = Database.database().reference().child("users")
ref.observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = User()
user.id = snapshot.key
user.setValuesForKeys(dictionary)
self.users.append(user)
completionHandler(self.users.count)
}
}, withCancel: nil)
}
Then, in the viewDidLoad() function, I try to assign a global variable equal to the number of users in the "users" node like this:
var userCount: Int?
override func viewDidLoad() {
super.viewDidLoad()
getUsers(){ (count) in
self.userCount = count
}
print(userCount)
}
This prints nil, and it should be 12, which is the amount of users I added to my Firebase database. Since I am somewhat unfamiliar with completion handlers, I am unsure how to fix this.
Any and all help is greatly appreciated.
Your completion closure is #escaping that is async for that reason print(userCount) get executed before the completion block returns and you are getting nil.
Try This:
var userCount: Int?
override func viewDidLoad() {
super.viewDidLoad()
getUsers(){ (count) in
self.userCount = count
print(self.userCount)
}
}
Or you can remove #escaping and make your completion closure sync.
func getUsers(completionHandler:(Int) -> ()) {
let ref = Database.database().reference().child("users")
ref.observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = User()
user.id = snapshot.key
user.setValuesForKeys(dictionary)
self.users.append(user)
completionHandler(self.users.count)
}
}, withCancel: nil)
}
I've been struggling with this all day, it doesn't seem to make any sense because I have very similar code that is working fine. I've tried everything, I've tried making a separate method that returns a string array, but none of it has worked. Every time, the postIDs array is set to null when accessed outside of the bracket followed by the parentheses (after the line reading "print(self.postIDs)"). Thanks for any help you could give me.
var postIDs = [String]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
let ref = Database.database().reference()
let uid = Auth.auth().currentUser!.uid
ref.child("users").child(uid).child("saved").observeSingleEvent(of: .value, with: { snapshot in
var ids = [String]()
let saved = snapshot.value as! [String:AnyObject]
for (elem, _) in saved {
ids.append(elem)
}
self.postIDs = ids
print(self.postIDs) // returns the values I would expect
})
ref.removeAllObservers()
guard self.postIDs.count >= 1 else {return} // postIDs count is equal to 0 here, and when I print postIDs the result is []
It is because
ref.child("users").child(uid).child("saved").observeSingleEvent(of: .value, with: { snapshot in
var ids = [String]()
let saved = snapshot.value as! [String:AnyObject]
for (elem, _) in saved {
ids.append(elem)
}
self.postIDs = ids
print(self.postIDs) // returns the values I would expect
})
works on background and other line of code executes before the callback came
Check the following code
override func viewDidLoad() {
super.viewDidLoad()
usersTableView.dataSource = self
usersTableView.delegate = self
// getSnapShot()
let databaseRef = Database.database().reference()
databaseRef.child("Users").observe(.value, with: { (snapshot) in
if snapshot.exists() {
self.postData = snapshot.value! as! [String : AnyObject]
self.postData.removeValue(forKey: self.appDelegate.KeyValue)
// self.getUserNames(Snapshot: self.postData)
}
else{
print("No users")
}
print(self.postData) //Does not return nil
self.getSnapShot() //Take snapshot outside paranthesis
})
print(self.postData) //Returns nil
}
func getSnapShot() {
print(self.postData) //Value of Snapshot is printed
}
I've been trying to load in an array of team names that I want to put up on different table view cells, but it never seems to load them properly.
I've been trying to cause the program to wait until it gets values back, but it never seems to get any, since it always crashes, gets a loading error, or just freezes.
var ref: FIRDatabaseReference?
var teamData = [String]()
var teamCount : Int?
var hasfilled = false
var firstRun = false
var currentUser = FIRAuth.auth()?.currentUser
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
ref = FIRDatabase.database().reference()
//Load each team
let path = ref?.child("Users").child(currentUser!.uid).child("joinedTeams")
path?.observeSingleEvent(of: .value, with: { (snapshot) in
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? FIRDataSnapshot{
let teamName = rest.value as? String
if let teamNameData = teamName {
self.teamData.append(teamNameData)
}
}
self.teamCount = Int(snapshot.childrenCount)
})
if runTillCompletion() == true {
self.tableView.reloadData()
}
}
func runTillCompletion() -> Bool{
if self.teamCount == self.teamData.count {
return true
}
return runTillCompletion()
}
I've tried this several different ways, from a while loop to just reloading it a ton.
I want to reload the view so that it runs the table view methods that determine the number of cells/content of cells.
I'm certain that there a better way to do this, since using a while loop/the recursion function have been painfully messy.
Thanks so much!
Your runTillCompletion method is called earlier before the response from Firebase and hence the tableview is empty. You need to reload the table after fetching data from firebase.
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
ref = FIRDatabase.database().reference()
//Load each team
let path = ref?.child("Users").child(currentUser!.uid).child("joinedTeams")
path?.observeSingleEvent(of: .value, with: { (snapshot) in
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? FIRDataSnapshot{
let teamName = rest.value as? String
if let teamNameData = teamName {
self.teamData.append(teamNameData)
}
}
self.teamCount = Int(snapshot.childrenCount)
self.tableView.reloadData()
})
}
FIRDatabase.database().reference(withPath: "users/\(currentUser!.uid)/joinedTeams")
.observeSingleEvent(of: .value, with: { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
self.teamData = snapshots.flatMap { $0.value as? String }
self.tableView.reloadData()
}
}
)
I'm trying to read from the database and place the values into an array of strings. However, when I try to push the values into an array then print the array the app crashes.
var pets: [String]?
override func viewDidLoad() {
super.viewDidLoad()
let userRef = FIRDatabase.database().reference().child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("pets")
userRef.observeSingleEvent(of: .value, with: { snapshot in
if let snap = snapshot.value as? Bool {
print("no values")
} else if let snap = snapshot.value as? NSDictionary {
for value in snap {
print(value.key as! String) // Prints out data in the database
self.pets?.append(value.key as! String)
}
print(self.pets!)
}
})
Does anybody know why the print(value.key as! String) prints out the data but then when I print out the array the app crashes with unexpectedly found nil while unwrapping an Optional value?
You never initialize pets. You declare it as an optional but never assign it a value. Why not change your code to the following:
var pets = [String]() // start with an empty array
override func viewDidLoad() {
super.viewDidLoad()
let userRef = FIRDatabase.database().reference().child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("pets")
userRef.observeSingleEvent(of: .value, with: { snapshot in
if let snap = snapshot.value as? Bool {
print("no values")
} else if let snap = snapshot.value as? NSDictionary {
for value in snap {
print(value.key as! String) // Prints out data in the database
self.pets.append(value.key as! String)
}
print(self.pets)
}
})
Your array is nil when you try to force-unwrapping using:
print(self.pets!)
As you are using self.pets?.append() you don't have any problem because you are using the chaining of optionals, but your array, in fact, is nil at that time because you forgot to initialize it before use it. If you instead use self.pets!.append() you will see a runtime error instead.
So as #rmaddy suggest you can initialize the array at the beginning or just inside your viewDidLoad() it's up to you.
I hope this help you.
I defined the following function to get a list of zipcodes from a Firebase database in a class called Zipcodes
// Return an array of Zipcodes from the Firebase database
func getZipcodesArray(array: [String]) -> [String] {
var zipcodes = array
_ZIPCODE_REF.observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot.childrenCount) // I got the expected number of items
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? FDataSnapshot {
print("Zipcode: \(rest.value.stringValue)")
zipcodes.append(rest.value.stringValue)
}
})
return zipcodes.sort()
}
However, when I try to use the returned array as follows:
var arr = [String]()
var zipcodes = Zipcode.service.getZipcodesArray(arr).copy()
I get the following error:
Instance member 'arr' cannot be used on type ViewController
Zipcode.swift code:
import Foundation
import Firebase
class Zipcode {
static let service = Zipcode()
private var _BASE_REF = Firebase(url: "\(BASE_URL)")
private var _ZIPCODE_REF = Firebase(url: "\(BASE_URL)/zipcode")
var zipcodes = [String]()
var BASE_REF: Firebase {
return _BASE_REF
}
var USER_REF: Firebase {
return _ZIPCODE_REF
}
// Return an array of Zipcodes from the Firebase database
func getZipcodesArray() -> [String] {
_ZIPCODE_REF.observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot.childrenCount) // I got the expected number of items
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? FDataSnapshot {
print("Zipcode: \(rest.value.stringValue)")
self.zipcodes.append(rest.value.stringValue)
}
})
return zipcodes.sort()
}
}
UPDATE: added completion handler to the function
How do I return the array?
// Return an array of Zipcodes from the Firebase database
func getZipcodesArray(completionHandler: ([String]) -> Void) {
_ZIPCODE_REF.observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot.childrenCount) // I got the expected number of items
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? FDataSnapshot {
print("Zipcode: \(rest.value.stringValue)")
self.zipcodes.append(rest.value.stringValue)
}
})
completionHandler(self.zipcodes)
}