How to use the variable of FirebaseReference.observeSingleEvent into another function - ios

I want to use variable_a in another function. Actually, I want to load these data into tableviewcell.
func readFIRData() {
var credentials:[String]
let userID = Auth.auth().currentUser?.uid
ref = Database.database().reference().child("usr").child(userID!)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let Name = value?["firstName"] as? String ?? ""
let PhoneNo = value?["mobile"] as? String ?? ""
var variable_a = [Name,PhoneNo]
self.tableView.reloadData()
}) { (error) in
}
}

You should have a callback (completion handler) in your readFIRData function, and pass variable_a as a parameter in that callback. Parse it into object which you use in table view, and reload tableView in the callback.
Function should look like this:
func readFIRData(_ completion: ([Name,PhoneNo]) -> ()) {
var credentials:[String]
let userID = Auth.auth().currentUser?.uid
ref = Database.database().reference().child("usr").child(userID!)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let Name = value?["firstName"] as? String ?? ""
let PhoneNo = value?["mobile"] as? String ?? ""
var variable_a = [Name,PhoneNo]
completion(variable_a)
}) { (error) in
}
}
and then have another function which is going to call readFIRData function:
func requestData() {
readFIRData() { [weak self] data in
guard let `self` = self else { return }
self.data = data
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
Mention how you get back to main thread to reload tableView. By self.data I assumed data which you will use in table view to instantiate cells.

Solved this way
func GetData(completion: #escaping(_ credentials: [String]) -> Void) {
let userID = Auth.auth().currentUser?.uid
ref = Database.database().reference().child("usr").child(userID!)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let Name = value?["firstName"] as? String ?? ""
let PhoneNo = value?["mobile"] as? String ?? ""
var variable_a = [Name,PhoneNo]
completion(variable_a)
self.tableView.reloadData()
}) { (error) in
}
}
Now, assign StringArray to some variable. Use below code to assign. Likewise variable StringArrayVariable can be used to populate tableview cells.
GetData { (StringArray) in
self.StringArrayVariable= StringArray
}

Related

Swift: Trying to set a ui button title using a completion handler

I am trying to use data from firebase to populate buttons on the UI. Everything works as expected except the button title is not updating. Any ideas on how to fix this?
#IBAction func addNewTapped(_ sender: Any) {
readOneDay2(lastMonday(trackerDate), completion: { message in
let lastHourRead = message
print(message)
self.lastHour1.setTitle(lastHourRead, for: UIControl.State.application)
})
}
func readOneDay2 (_ mydate: Date, completion: #escaping (_ message: String) -> Void){
var db: DatabaseReference!
db = Database.database().reference()
var totalComb: Double = 0.0
let userID = Auth.auth().currentUser?.uid
db.child("TimesheetData").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let dict = snapshot.value as? NSDictionary
for (key, value) in dict! {
let myvalue = value as? [String: Any]
let compDate: String = myvalue!["timeSheetDate"]! as! String
if compDate == dateStringer(mydate) {
let sHours: String = myvalue!["hours"]! as! String
let sOverTime: String = myvalue!["overTime"]! as! String
let sDoubleTime: String = myvalue!["doubleTime"]! as! String
let dHours: Double = Double(sHours)!
let dOverTime: Double = Double(sOverTime)!
let dDoubleTime: Double = Double(sDoubleTime)!
totalComb = totalComb + dHours + dOverTime + dDoubleTime
print(key)
}
}
print("First Sum " + String(totalComb))
DispatchQueue.main.async {
completion(String(totalComb))
}
}) { (error) in
print(error.localizedDescription)
}
}
As per #Virender said,
Change UIControl.State.application to UIControl.State.normal
DispatchQueue.main.async {
self.lastHour1.setTitle(lastHourRead, for:.normal)
}

Why does it overwritten data instead of adding more data in firebase?

I really want to know why the data is overwritten when the user types new data,
I want it to add more data to it not overwrite it the data
Also want to know how to read it
Thank you in advance
let oDB = Database.database().reference().child("Data")
let oDictionary = ["Data1" : strange.text! , "Data2" : stranger.text!]
let uid = Auth.auth().currentUser?.uid
oDB.child(uid!).setValue(oDictionary) {
(error, reference) in
if error != nil{
print(error!)
} else {
print("saved Sucessfully")
self.navigationController?.popViewController(animated: true)
}
}
//In another ViewController
func updateRequest() {
let uid = Auth.auth().currentUser?.uid
let yDb = Database.database().reference().child("Data").child(uid!)
postDb.observeSingleEvent(of: .value) { (snapShot) in
if let snapShotValue = snapShot.value as? Dictionary<String, String> {
let text = snapShotValue["Data1"]!
let case = snapShotValue["Data2"]!
let data = Data()
data.s= text
data.y = case
self.array.append(data)
self.table.reloadData()
}
}
}
setValue overwrites the old content , You may need childByAutoId
oDB.child(uid!).childByAutoId().setValue(oDictionary) {
(error, reference) in
if error != nil{
print(error!)
} else {
print("saved Sucessfully")
self.navigationController?.popViewController(animated: true)
}
This will give this structure
Data
> uid
> someKey1 <<<< auto generated
Data1:"---"
Data2:"---"
> someKey2 <<<< auto generated
Data1:"---"
Data2:"---"
Read
//In another ViewController
func updateRequest() {
let uid = Auth.auth().currentUser?.uid
let yDb = Database.database().reference().child("Data").child(uid!)
postDb.observeSingleEvent(of: .value) { (snapShot) in
if let snapShotValue = snapShot.value as? [String:[String:String]] {
Array(snapShotValue.values).forEach {
let data = Data()
data.s= $0["Data1"]!
data.y = $0["Data2"]!
self.array.append(data)
}
self.table.reloadData()
}
}
}

How to append values to a dictionary after a firebase query?

In the below code I am trying to append string uids to a dictionary the problem is when I do so it seems like the dict is reset after each iteration. How can I fix this?
func getFollowing() {
self.ref.child("Following").child(uid)
.observeSingleEvent(of: .value, with: { (snapshot) in
if let data = snapshot.value as? [String:[String : Any]] {
for key in data.keys {
var dict = (UIApplication.shared.delegate as! AppDelegate).usersCurrentUserIsFollowing
dict[key] = true
print(dict, " this is gtteh dict now dhfsakflk")
}
}
})
}
I was able to solve my problem by doing the following:
func getFollowing() {
self.ref.child("Following").child(uid)
.observeSingleEvent(of: .value, with: { (snapshot) in
var usersData = ["":true]
if let data = snapshot.value as? [String:[String : Any]] {
for key in data.keys {
usersData[key] = true
}
var dict = (UIApplication.shared.delegate as! AppDelegate).usersCurrentUserIsFollowing
dict = usersData
}
})
}

iOS Firebase asynchronous data retrieval into Array

I try to retrieve data from Firebase into Array. Because it runs asynchronously, the results that I want to show in my CollectionView is a delay until I switch back and forth. I am very new to asynchronous functions in iOS. Please help me to complete my code.
ref = Database.database().reference(withPath: "MyTest/Video")
ref?.observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.exists() { return }
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
let autoID = child.key as String //get autoID
let title = snapshot.childSnapshot(forPath: "\(autoID)/Title").value
let url = snapshot.childSnapshot(forPath: "\(autoID)/URL").value
let views = snapshot.childSnapshot(forPath: "\(autoID)/Views").value
self.arrayAllTitle.append(title as! String)
self.arrayAllId.append(url as! String)
self.arrayAllDesc.append(views as! String)
}
}
})
You need to reload the collection after you retrieve the data so after the for loop call reloadData()
for child in result {
}
self.collectionView.reloadData()
//
func getValueFromDatabase(completion: #escaping (_ status: Bool) -> Void){
ref = Database.database().reference(withPath: "MyTest/Video")
ref?.observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.exists() { return }
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
let autoID = child.key as String //get autoID
let title = snapshot.childSnapshot(forPath: "\(autoID)/Title").value
let url = snapshot.childSnapshot(forPath: "\(autoID)/URL").value
let views = snapshot.childSnapshot(forPath: "\(autoID)/Views").value
self.arrayAllTitle.append(title as! String)
self.arrayAllId.append(url as! String)
self.arrayAllDesc.append(views as! String)
}
completion(true)
}
else {
completion(false)
}
})
}
//
self.getValueFromDatabase { (status) in
if status {
// success
}
}
I'm working with Firebase in my project right now. I would suggest the following solution: wrap the database observer in a distinct function which gets completion block as a parameter.
func getValueFromDatabase(completion: ()->Void){
ref = Database.database().reference(withPath: "MyTest/Video")
ref?.observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.exists() { return }
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
let autoID = child.key as String //get autoID
let title = snapshot.childSnapshot(forPath: "\(autoID)/Title").value
let url = snapshot.childSnapshot(forPath: "\(autoID)/URL").value
let views = snapshot.childSnapshot(forPath: "\(autoID)/Views").value
self.arrayAllTitle.append(title as! String)
self.arrayAllId.append(url as! String)
self.arrayAllDesc.append(views as! String)
}
completion()
}
})
}
This way you can call the function from anywhere providing the desired action after fetching data from db is finished:
getValueFromDatabase(completion:{
self.collectionView.reloadData() //or any other action you want to fulfil
})

How to Append Filter Data on Array from Firebase in Swift

Please find my code below. How can we append filter data on array from Firebase?
var childrenList = [DatabaseList]()
let ref = Database.database().reference(withPath: "Messages")
let query = ref.queryOrdered(byChild: "VideoID").queryEqual(toValue: "12345").observe(.value, with: { (snapshot) in
for childSnapshot in snapshot.children{
print(childSnapshot)
self.childrenList.append(snapshot)
}
})
DispatchQueue.main.async {
self.tableView.reloadData()
}
let ref = Database.database().reference(withPath: "Messages")
let query = ref.queryOrdered(byChild: "VideoID").queryEqual(toValue: "12345").observe(.value, with: { (snapshot) in
print(snapshot)
for (childSnapshotId, childSnapshotValue) in snapshot {
if let dataListDict = childSnapshotValue as? [String: AnyObject] {
//Init you newModel with the dataListDict here
let newModel = DatabaseList(dict: dataListDict)
print(childSnapshot)
self.childrenList.append(newModel)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
class DatabaseList : NSObject {
var messageBody : String?
var name : String?
var videoID : String?
init(dict: [String: AnyObject]) {
messageBody = dict["MessageBody"]
name = dict["Name"]
videoID = dict["videoID"]
}
}
Your query is correct but there are few mistakes in finishing block.
self.childrenList.append(snapshot) snapshot is an instance of DataSnapshot not a DatabaseList so you can not append it like this.
for childSnapshot in snapshot.children {
/// childSnapshot is an instance of DataSnapshot not a dictionary but its value will be
guard let data = (childSnapshot as! DataSnapshot).value else {continue}
let dataDict = data as! Dictionary<String, Any>
/// Initializing the new object of DatabaseList and passing the values from data
let list: DatabaseList = DatabaseList()
list.messageBody = dataDict["MessageBody"] as? String
list.name = dataDict["Name"] as? String
list.videoID = dataDict["VideoID"] as? String
/// This is correct, and now you can append it to your array.
childrenList.append(list)
}
Apart from this you will have to reload the tableView inside the finishing block not below the block because this is an asynchronous request and data will come later.
Also its always better to check the data existence. snapshot.exists().
One more suggestion if you want to fetch the data just once then do not use .observe use .observeSingleEvent instead. .observe will fire the block every time there is any change at this node.
Here is the full code snippet.
let query = ref.queryOrdered(byChild: "VideoID").queryEqual(toValue: "12345").observe(.value, with: { (snapshot) in
if !snapshot.exists() {
// Data doesn't exist
return
}
for childSnapshot in snapshot.children {
guard let data = (childSnapshot as! DataSnapshot).value else {continue}
let dataDict = data as! Dictionary<String, Any>
let list: DatabaseList = DatabaseList()
list.messageBody = dataDict["MessageBody"] as? String
list.name = dataDict["Name"] as? String
list.videoID = dataDict["VideoID"] as? String
childrenList.append(list)
}
/// Reload your tableView here
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
And expecting the class model like below:
class DatabaseList: NSObject {
var messageBody: String?
var name: String?
var videoID: String?
}

Resources