Can't seem to save local array in Swift - ios

So I'm running into a problem where I can't seem to save the contents of a local array outside of a for loop. This code iterates through notifsTop (which is a dictionary) and saves the values into another array. However, outside of the for loop, the contents of tempnotifsarray are empty, which doesn't make sense to me since I appended them to an array that is outside of the loop block. I have been struggling with this for some time and can't figure out what is going on. Any help would be appreciated, thanks!
func createArray() -> [notificationLabel] {
let newUserInfo = Auth.auth().currentUser
let uid = newUserInfo?.uid
self.ref = Database.database().reference()
let practionerRef = self.ref.child("users").child(uid!)
var tempnotifsArray: [notificationLabel] = []
practionerRef.observeSingleEvent(of: .value, with: {(snapshot) in
let value = snapshot.value as? NSDictionary
if let notifsTop = value?["Notifications"] as? NSDictionary { //top of the notifications hierarchy
for (_, myValue) in notifsTop {
// Iterate in here
//self.notifications.append(myValue)
let notification = notificationLabel(label: myValue as! String)
tempnotifsArray.append(notification)
//if I print here the array is full with the values I want
}
}
})
print(tempnotifsArray) //comes out as []
return tempnotifsArray
}

Related

How to save Firebase Data into an Array

I have a Firebase database that is modeled as such:
: users
: some-random-id-1
- username:"user1"
- email:"email1#gmail.com"
: some-random-id-2
- username:"user2"
- email:"email2#gmail.com"
I am trying to iterate through all the users in the dictionary of data and append the username into a list in the file to be used for other purposes. I created a string array (userslist) variable and in viewdidload(), I wrote the following code below:
ref = Database.database().reference()
ref?.observe(.value, with: { (snapshot) in
let dataDict = snapshot.value as! NSDictionary
let x = dataDict["users"] as! NSDictionary
print(x)
print("--------")
for user in x{
let y = user.value as? [String: String]
let z = y!["username"]
print(z)
self.userslist.append(z!)
print(self.userslist)
print("NEXT")
}
})
print(self.userslist)
Inside the brackets of the snapshot, when I print self.userslist, I can see that each element is getting added, but when I print it a final time outside of the brackets, it shows it as an empty array. I think that the elements are only appended in the scope of those brackets so I cant access the filled array anywhere else. How do I get around this so I can use the data I appended?
you are using print(self.userslist) outside the observer and Firebase run in Async Mode
So, if you make use of breakpoints you will notice that
print(self.userslist) is Called before the control reach onside the Database handler ,
data is getting fetched you need to load your views inside that handler using Dispatch main queue
ref?.observe(.value, with: { (snapshot) in
let dataDict = snapshot.value as! NSDictionary
let x = dataDict["users"] as! NSDictionary
print(x)
print("--------")
for user in x{
let y = user.value as? [String: String]
let z = y!["username"]
print(z)
self.userslist.append(z!)
print(self.userslist)
print("NEXT")
}
/// Here is your data
print(self.userslist)
})
/// Called before Handler execution
print(self.userslist)

queryEqualTo is being skipped over

i'm trying to set up a viewCount for my app, when I set the breakpoints up and go through the code it always skips past the queryOrdered and i'm not exactly sure why
func increaseViewCount(username: String, time: NSNumber){
guard let uid = Auth.auth().currentUser?.uid else{
return
}
let refOfUserName = Database.database().reference().child("Users").child(uid)
refOfUserName.observeSingleEvent(of: .value, with: {(snapshot) in
let dictionaryOfUser = snapshot.value as? [String: AnyObject]
// let currentUsersName = dictionaryOfUser?["username"] as? String
let currentUsersName = "hello"
if username == currentUsersName {
print("this is the same user")
}else{
let postRef = Database.database().reference().child("HistoryOfPosts").child("post")
postRef.queryOrdered(byChild: "post").queryEqual(toValue: time).observeSingleEvent(of: .childAdded, with: {(snapshotPost) in
print(snapshotPost.exists())
print(snapshotPost)
let valString = snapshotPost.value
let number = valString as! NSNumber
var value = number.intValue
value = value + 1
let values = ["viewCount": value] as [String:Any]
postRef.updateChildValues(values)
})
}
})
}
The data is loaded from the Firebase Database asynchronously. Instead of waiting for that loading to complete, the program continues with the statement after you attach the observer.
In this case that means that the code pretty much exits increaseViewCount() straight after it attaches the observer. Then once the data comes back from the Firebase servers, the code in your callback block is executed.
To get into the callback block, place a breakpoint on the first statement in that block.

I need to know when firebase observation done completely

I am trying to observe multiple data at once using firebase, the observation block keeps looping until it fetchs all the data. I need to know when it is actually done so I can execute another block. How can I do that?
databaseRef.child("First_Secondary_Grade").child("0").child("0").queryOrderedByKey().observe(.childAdded, with: {
(snapshot) in
if let dictoinary = snapshot.value as? [String: AnyObject] {
let dataofthequsation = structofthedata()
dataofthequsation.setValuesForKeys(dictoinary)
}
})
i think i figured it out
let databaseRef = FIRDatabase.database().reference()
var gotitall = 0
// First you need to observe single event to get the real count of children in swift 3 observe will count the keys inside a child. That's why!
databaseRef.child("First_Secondary_Grade").child("0").child("0").observeSingleEvent(of:.value, with:{ (snap) in
gotitall = Int(snap.childrenCount)
databaseRef.child("First_Secondary_Grade").child("0").child("0").observe(.childAdded, with: {
snapshot in
if let dictoinary = snapshot.value as? [String: AnyObject] {
let dataofthequsation = structofthedata()
dataofthequsation.setValuesForKeys(dictoinary)
self.dataofthequsation.append(dataofthequsation)
// this is will run when the block runs through all children
if gotitall == self.dataofthequsation.count {
completion()
}
}
})
})

Read values from database into Array

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.

How to ensure that the data is not retrieved and appended as a whole each time a new entry is added?

func generateDataForRecents() {
if URLArrayStringThisSeason.count == 0 {
self.activityIndicator2.isHidden = false
self.activityIndicator2.startAnimating()
}
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("palettes").queryLimited(toFirst: 100).observe(.value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict as [String:AnyObject]{
let URL = each.value["URL"] as! String
self.URLArrayStringRecents.append(URL)
//print(self.URLArrayString.count)
//print(snapshot)
//let pictureTitle = each.value["title"] as! String
print(self.URLArrayStringRecents.count)
}
}
self.whatsNewCollectionView?.reloadData() //Reloads data after the number and all the URLs are fetched
self.activityIndicator2.stopAnimating()
self.activityIndicator2.isHidden = true
})
}
The following code does a retrieval of data each time the function is called, or when a new data is added.
This is extremely useful when the app is first started up or closed and then restarted. However, when the app is running, whenever a new entry is added, the code seemed to run again and thus appending twice the amount of new data.
For example, when there are already 15 entries identified and then suddenly a new entry is added, the array of the URL would contain 15+16 thus amounting to a total of 31.
How do I make it such that the new data is added to the array instead of adding the entire snapshot in?
You do that by listening for .childAdded events, instead of listening for .value:
var query = databaseRef.child("palettes").queryLimited(toFirst: 100)
query.observe(.childAdded, with: { (snapshot) in
let URL = snapshot.childSnapshot(forPath/: "URL").value as! String
self.URLArrayStringRecents.append(URL)
}
Since you have a limit-query, adding a 101st item means that one item will be removed from the view. So you'll want to handle .childRemoved too:
query.observe(.childRemoved, with: { (snapshot) in
// TODO: remove the item from snapshot.key from the araay
})
I recommend that you spend some time in the relevant documentation on handling child events before continuing.
Please check below method. I have use this method not getting any duplicate entry.
func getallNotes()
{
let firebaseNotesString: String = Firebase_notes.URL
let firebaseNotes = FIRDatabase.database().referenceFromURL(firebaseNotesString).child(email)
firebaseNotes.observeEventType(.Value, withBlock: { snapshot in
if snapshot.childSnapshotForPath("Category").hasChildren()
{
let child = snapshot.children
self.arrNotes = NSMutableArray()
self.arrDictKeys = NSMutableArray()
for itemsz in child
{
let childz = itemsz as! FIRDataSnapshot
let AcqChildKey : String = childz.key
if AcqChildKey == AcqIdGlobal
{
if (childz.hasChildren() == true)
{
let dictChild = childz.value as! NSMutableDictionary
self.arrDictKeys = NSMutableArray(array: dictChild.allKeys)
for i in 0..<self.arrDictKeys.count
{
let _key = self.arrDictKeys.objectAtIndex(i).description()
print(_key)
let dictData : NSMutableDictionary = NSMutableDictionary(dictionary: (dictChild.valueForKey(_key)?.mutableCopy())! as! [NSObject : AnyObject])
dictData.setObject(_key, forKey: "notesId")
self.arrNotes.addObject(dictData)
}
}
}
}
self.tableviewNote.reloadData()
}
})
}
As for the query for removed child,
query.observe(.childRemoved, with: { (snapshot) in
print(snapshot)
let URL = snapshot.childSnapshot(forPath: "URL").value as! String
self.URLArrayStringThisSeason = self.URLArrayStringThisSeason.filter() {$0 != URL}
self.thisSeasonCollectionView.reloadData()
})
it will obtain the URL of the removed child and then update the array accordingly.

Resources