Swift Passing Data Structs and Arrays Firebase - swift

I am trying to send data from vc_1 to vc_2. But I am observing the submitted data is missing in vc_2 (Basket items). How can ı observe all data in vc_2?
Firebase node;
Pro_:
Basket:
ID_1:
cat: “Tech”
info:”iOS”
orderid:”Ref_1”
ID_2:
cat: “Tech”
info:”Android”
orderid:”Ref_2”
name:”Mike”
First_VC
var name_list = [Name_Struct]()
var SecondArray : [BasketArray] = []
func retrieve_data(){
ref = Database.database().reference()
self.ref.child(“Pro_”).observeSingleEvent(of:.value) { (snapshot) in
self.name_list.removeAll()
for child in snapshot.children{
let snap = child as! DataSnapshot
let dict = snap.value as! NSDictionary
let name_ = dict[“name”] as? String ?? ""
let baskets = (child as AnyObject).childSnapshot(forPath: “Basket”).value as? [String: Any] ?? [:]
for basket in baskets {
let pro_detail = basket.value as? [String: Any]
let info_ = basket?[“info”] as? String ?? ""
let cat_ = basket?[“cat”] as? String ?? ""
let orderid_ = basket?[“orderid”] as? String ?? ""
self.SecondArray.append(BasketArray(info: info_, cat: cat_, orderid: orderid_)
}
let names = Name_Struct(name: name_)
self.name_list.append(names)
DispatchQueue.main.async {
self.tableView.reloadData()
}}}}}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? Second_VC {
destination.SecondArray = SecondArray[indexPath.row]
print(“Send array:”,self.SecondArray)
}}
Send array output
Send array: [“cat”: “Tech”, “info”: “iOS”, “orderid”: “Ref_1”]
Send array: [“cat”: “Tech”, “info”: “Android”, “orderid”: “Ref_2”]
Second_VC
var SecondArray : BasketArray!
override func viewDidLoad() {
super.viewDidLoad()
table_view.delegate = self
table_view.dataSource = self
print(“retrieve:”,self.SecondArray)
}
Second_VC output;
retrieve: [“cat”: “Tech”, “info”: “Android”, “orderid”: “Ref_2”]

It's probably because in your most nested for loop in First_VC, you have
self.SecondArray = BasketArray(info: info_, cat: cat_, orderid: orderid_)
Because it's in a for loop, self.SecondArray is set to the ID_1 values, then after that on the second run of the loop you're overwriting it to be the ID_2 values. You want to be appending both the datas to the SecondArray, not overwriting them like you have here.

Related

How to execute two completion blocks in a single function and pass the data of the completion block to next view controller?

This is my database structure:
I'm using a function with closure, performing two completion blocks and store the data in two separate arrays. Once I get the data I want to pass the data to next view controller into different variables, but instead I'm getting same value for both arrays.
#IBAction func GoToAnswerPage(_ sender: Any) {
self.getData(refe:JoinCodeTextField.text!) { (array) in
self.performSegue(withIdentifier:"JoinToAnswerPage",sender:array)
}
}
func getData(refe: String, completion: #escaping (([Any]) -> ())) {
var questionArray = [Any]()
var answerArray = [Any]()
let ref = Database.database().reference(fromURL: "https://pollapp-30419.firebaseio.com/").child("Questions/\(refe)/")
ref.child("Question_And_Options").observeSingleEvent(of: .value,with: { snapshot in
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot, let value = rest.value{
questionArray.append(value)
}
completion(questionArray)
})
ref.child("Answer_Key").observeSingleEvent(of: .value,with: { snapshot in
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot, let value = rest.value{
answerArray.append(value)
}
completion(answerArray)
})
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let joinViewController = segue.destination as? JoinAnswerViewController
else {
return
}
joinViewController.answers = sender as! [String]
joinViewController.options = sender as! [String]
}
On the next view controller.
var options = [Any]()
var answers = [Any]()
This is the output I'm getting:
answers-["Test Q-1", "Test A-1", "Test A-2"]
questions-["Test Q-1", "Test A-1", "Test A-2"]
answers-["Test A-1"]
questions-["Test A-1"]
Instead I should get:
questions-["Test Q-1", "Test A-1", "Test A-2"]
answers-["Test A-1"]
Your completion handler will be called twice, once for "answers" and once for "questions". They could come in either order, so you should pass an additional type in the completion to know which you have received. Use a [String : [Any]] dictionary to collect the two arrays, and call self.performSegue(withIdentifier:sender:) when you've received both arrays and stored them in the dictionary arrays.
In prepare(for:sender:) unpack the sender dictionary and assign the values:
#IBAction func GoToAnswerPage(_ sender: Any) {
var arrays = [String : [Any]]()
self.getData(refe: JoinCodeTextField.text!) { (array, type) in
arrays[type] = array
if arrays.count == 2 {
self.performSegue(withIdentifier:"JoinToAnswerPage",sender: arrays)
}
}
}
func getData(refe: String, completion: #escaping (([Any], String) -> ())) {
var questionArray = [Any]()
var answerArray = [Any]()
let ref = Database.database().reference(fromURL: "https://pollapp-30419.firebaseio.com/").child("Questions/\(refe)/")
ref.child("Question_And_Options").observeSingleEvent(of: .value,with: { snapshot in
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot, let value = rest.value{
questionArray.append(value)
}
completion(questionArray, "question")
})
ref.child("Answer_Key").observeSingleEvent(of: .value,with: { snapshot in
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot, let value = rest.value{
answerArray.append(value)
}
completion(answerArray, "answer")
})
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let joinViewController = segue.destination as? JoinAnswerViewController
else {
return
}
guard let arrays = sender as? [String : [Any]],
let answers = arrays["answer"] as? [String],
let questions = arrays["question"] as? [String]
else { return }
joinViewController.answers = answers
joinViewController.options = questions
}
Note: When the user presses a button, they should get an immediate response. Since you are loading the data from the network, there may be a delay making the user wonder if anything is happening. It would be better to pass JoinCodeTextField.text! to JoinAnswerViewController and let it load the question/answer data. JoinAnswerViewController could display a UIActivityIndicatorView (spinner) while the data is loading to let the user know the data is coming. Once you have both arrays, you can set up the JoinAnswerViewController.

Problem fetching data from firebase by using struct file

struct UserClass {
var babyName: String!
var babyHeight: String!
var babyWeight: String!
var babyURL: String!
var uid: String!
var reference:DatabaseReference!
var key: String!
init?(snapshot: DataSnapshot?) {
guard let value = snapshot?.value as? [String:AnyObject],
let uid = value["uid"] as? String,
let babyName = value["BabyName"] as? String,
let babyURL = value["BabyURL"] as? String,
let babyHeight = value["BabyHeight"] as? String,
let babyWeight = value["BabyWeight"] as? String else {
return nil
}
self.key = snapshot?.key
self.reference = snapshot?.ref
self.uid = uid
self.babyURL = babyURL
self.babyName = babyName
self.babyHeight = babyHeight
self.babyWeight = babyWeight
}
func getuserData() -> String {
return ("BabyName = \(babyName)")
}
}
func fetchCurrentUserInfo() {
var currentUserRef = Database.database().reference().child("Users").child("\(userID)")
handler = currentUserRef.queryOrderedByKey().observe(DataEventType.value, with: { (snapshot) in
print("User data = \(snapshot.value)")
let user = UserClass(snapshot: snapshot)
print(user?.babyName)
self.babyName.text = user?.babyName
})
}
I am getting user data but not user.babyName. How can I fix this?
May be this will help you, as the db structure is not mentioned in question. but you have to iterate children one by one and then use for loop to fetch the exact data from firebase.
reference = FIRDatabase.database().reference()
reference.child("Users").queryOrderedByKey().observe(DataEventType.value, with: { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots
{
let userId = child.childSnapshot(forPath: "userID").value! as! String
print(userId)
}
}
})

Passing Firebase database reference data

I'm looking to pass an array that contains user info pulled from Firebase from one controller to another using a segue. I'm able to do it when everything is in a tableview, but not when it's in a regular controller view. Can someone help plz?
View Controller
var userArray = [User]()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showGuestView" {
let guestVC = segue.destination as! GuestUserViewController
guestVC.ref = userArray.ref //this would work using userArray[indexPath.row].ref if it was a tableview
//ref represents DatabaseReference?
}
}
DatabaseClass.fetchUser() { (user) in
if let user = user {
self.userArray.append(user)
}
Database Class
func fetchUser(completion: #escaping (User?)->()){
let currentUser = Auth.auth().currentUser!
let postRef = Database.database().reference().child("getinfo").child(currentUser.uid)
postRef.observe(.value, with: { (snapshot) in
for childSnapshot in snapshot.children.allObjects as! [DataSnapshot] {
let request = childSnapshot.key
let userRef = self.databaseRef.child("users").child(request)
userRef.observeSingleEvent(of: .value, with: { (currentUser) in
let user: User = User(snapshot: currentUser)
completion(user)
})
}
})
}
User Structure
struct User {
var firstname: String!
var uid: String!
var ref: DatabaseReference!
init(firstname: String, uid: String){
self.firstname = firstname
self.uid = uid
self.ref = Database.database().reference()
}
init(snapshot: DataSnapshot){
if let snap = snapshot.value as? [String:Any] {
self.firstname = snap["firstname"] as! String
self.uid = snap["uid"] as! String
}
self.ref = snapshot.ref
}
func toAnyObject() -> [String: Any]{
return ["firstname":self.firstname, "uid":self.uid]
}
}
I can see userArray is an array of users, hence you could not use userArray.ref because the array doesn't have any property like ref in its definition. In the table view, it is working because you pulled a user object and then passed ref.
Try to get a selected user instance before passing to the guest view controller.
let user = userArray[selected user index];
guestVC.ref = user.ref

How to clear TableView and reload data again?

I have a function like this and when I reload with button this function collectionViewTable shows same data second time .How can I solve it ??
func getWallpaperFromDB(){
let databaseRef = Database.database().reference()
databaseRef.child("wallpapers").observe(DataEventType.childAdded) { (snapshot) in
if let value = snapshot.value! as? [String: Any] {
let categoryID = value["categoryID"] as! String
let categoryName = value["categoryName"] as! String
let wallpaperName = value["wallpaperName"] as! String
let wallpaperId = snapshot.key
let DBWallpaper = Wallpaper(categoryID: categoryID, categoryName: categoryName, wallpaperId: wallpaperId, wallpaperName: wallpaperName)
self.wallpapers.append(DBWallpaper)
self.collectionViewTable.reloadData()
}
}
}
#IBAction func slideMenuButton(_ sender: Any) {
getWallpaperFromDB()
}
You need to clear every call
#IBAction func slideMenuButton(_ sender: Any) {
wallpapers.removeAll()
getWallpaperFromDB()
}
You can empty your wallpapers array inside the closure function. That way every time the function is called the wallpapers array will be empty before the data is being fetched again. That way you won't have duplicate data.
func getWallpaperFromDB(){
let databaseRef = Database.database().reference()
databaseRef.child("wallpapers").observe(DataEventType.childAdded) { (snapshot) in
self.wallpapers = []
if let value = snapshot.value! as? [String: Any] {
let categoryID = value["categoryID"] as! String
let categoryName = value["categoryName"] as! String
let wallpaperName = value["wallpaperName"] as! String
let wallpaperId = snapshot.key
let DBWallpaper = Wallpaper(categoryID: categoryID, categoryName: categoryName, wallpaperId: wallpaperId, wallpaperName: wallpaperName)
self.wallpapers.append(DBWallpaper)
self.collectionViewTable.reloadData()
}
}
}
#IBAction func slideMenuButton(_ sender: Any) {
getWallpaperFromDB()
}

Code only retrieving one value from data in Firebase

As the title suggests, I'm trying to retrieve some data from firebase database, but my code's not working. I have three children (I guess that's how you call them) inside "Posts" called "title", "description" and "username" and I'm trying to get all of them and append them into a variable to use them later, but it only retrieves the first value of each of them, despite the fact that there are like 5 values. Anyone knows why?
By the way, I'm calling upon this function on my ViewDidLoad.
let postDB = Database.database().reference().child("Posts")
postDB.queryOrderedByKey().observeSingleEvent(of: .value) { (snapshot) in
if let snapshotValue = snapshot.value as? NSDictionary {
let postTitle = snapshotValue["title"] as? String
let postUsername = snapshotValue["username"] as? String
let postDesc = snapshotValue["description"] as? String
let postArray = postStruct(title: postTitle, description: postDesc, username: postUsername)
self.newPost.insert(postArray, at: 0)
}
import UIKit
import FirebaseDatabase
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// MARK: - variables
var postDB: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
// getting a node from database //
postDB = Database.database().reference().child("Posts")
// observing data changes //
postDB.observe(DataEventType.value) { (dataSnapshot) in
self.postArray.removeAll()
if dataSnapshot.childrenCount > 0 {
for post in dataSnapshot.children.allObjects as! [DataSnapshot] {
let object = post.value as! [String: Any]
let description = object["description"] as! String
let title = object["title"] as! String
let userName = object["username"] as! String
let model = postStruct(title: title, description: description, username: userName))
self.postArray.append(model)
}
}
self.tableView.reloadData()
}
}
}
Try this – the code replaces what you currently have in the snapshot handler.
if let firebaseList = snapshot.children.allObjects as? [FIRDataSnapshot] {
if let swiftList = snapshot.value as? [String:AnyObject] {
for firebaseItem in firebaseList {
let childID = firebaseItem.key as String
let swiftItem = swiftList[childID]
let postTitle = swiftItem?["title"] as? String
let postUsername = swiftItem?["username"] as? String
let postDesc = swiftItem?["description"] as? String
let postArray = postStruct(title: postTitle, description: postDesc, username: postUsername)
self.newPost.insert(postArray, at: 0)
}
}
}
}
Worked for me. It gets all the values now you just have to put them in an array
postDB.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull #NotNull Task<DataSnapshot> task) {
if(!task.isSuccessful()){
Log.e("firebase", "Error getting data; ", task.getException());
}else{
Log.d("firebase", String.valueOf(task.getResult().getValue()));
}
}
});