Get the Values of the Retrieved Nodes and Use them Globally! (Firebase and Swift) - swift

I'm trying to get some values from the UserProfile node from the dashboard and pass them into global variables to use them globally outside the observe function.
ref.child("UserProfile").child(FIRAuth.auth()!.currentUser!.uid).observeEventType(.Value , withBlock: {snapshot in
if let name = snapshot.value!.objectForKey("name") as? String {
print(name)
}
if let email = snapshot.value!.objectForKey("email") as? String {
print(email)
}
if let phone = snapshot.value!.objectForKey("phone") as? String {
print(phone)
}
if let city = snapshot.value!.objectForKey("city") as? String {
print(city)
}
})
I want to pass them outside the observe function so I can use them globally anywhere in the .swift file.

Since Firebase functions are supposed to be Asynchronous, you need to access these User properties inside the completionBlock of the function. And mind that They will only give retrieved value once the call has been completed.
var globalUserName : String!
var globalEmail : String!
var globalPhone : String!
var globalCity : String!
override func viewWillAppear(animated : Bool){
super.viewWillAppear(animated)
retrieveUserData{(name,email,phone,city) in
self.globalUserName = name
self.globalEmail = email
self.globalPhone = phone
self.globalCity = city
}
}
func retrieveUserData(completionBlock : ((name : String!,email : String!, phone : String!, city : String!)->Void)){
ref.child("UserProfile").child(FIRAuth.auth()!.currentUser!.uid).observeEventType(.Value , withBlock: {snapshot in
if let userDict = snapshot.value as? [String:AnyObject] {
completionBlock(name : userDict["name"] as! String,email : userDict["email"] as! String, phone : userDict["phone"] as! String, city : userDict["city"] as! String)
}
})
}

Related

Create User in firebase - Realtime Database

I stuck with creating a Firebase User.
After creating my database looks like this
I created a struct User but I don't know how to create the correct dictionary
struct User {
let email : String
let name : String
let lastname : String
let phone : String
let password : String
let uid: String
let car : [Car]
init(uid: String, dictionary: [String: Any] ) {
self.uid = uid
self.email = dictionary["email"] as? String ?? ""
self.name = dictionary["name"] as? String ?? ""
self.lastname = dictionary["lastname"] as? String ?? ""
self.phone = dictionary["phone"] as? String ?? ""
self.password = dictionary["password"] as? String ?? ""
self.car = dictionary["car"] as? String ?? "" <- this is wrong
}
struct Car {
let firstCar : [FirstCar]}
struct FirstCar {
let brandName : String
let capacity : String
let carName : String
let fueal : String
let model : String
let power : String
let year : String}
help
I think you're asking about how to populate the User object from the Realtime Database when there's a child node involved that is also a key: value pair.
My suggestion is to keep Firebase 'firebasey' as long as possible by using DataSnapshots and not cast to dictionaries; it makes getting data from child nodes much easier and cleaner.
Here's and example of reading a users node from Firebase
var userArray = [UserClass]()
func readAllUsers() {
let usersRef = self.ref.child("users") //self.ref points to my firebase
usersRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children.allObjects as! [DataSnapshot] {
let user = UserClass(userSnap: child)
self.userArray.append(user)
}
})
}
and then the UserClass with a child key: value pair with a car brand as a child
class UserClass {
var key = ""
var userName = ""
var userEmail = ""
var type = ""
var brand = ""
convenience init(userSnap: DataSnapshot) {
self.init()
self.key = userSnap.key
self.userName = userSnap.childSnapshot(forPath: "name").value as? String ?? "No Name"
self.userEmail = userSnap.childSnapshot(forPath: "email").value as? String ?? "No email"
let carSnap = userSnap.childSnapshot(forPath: "car") //retuns the car node
let firstCarSnap = carSnap.childSnapshot(forPath: "firstCar") //return the firstCar node
self.brand = firstCarSnap.childSnapshot(forPath: "brandName").value as? String ?? "No Brand"
}
}
It's a good idea to store users by their uid and also keep track of that in the object in case you need to reference that user or update their node elsewhere in your code.
Note this line in the above code
for child in snapshot.children.allObjects as! [DataSnapshot]
preserves the ordering of the users when they are read in.

How to get an array field from Firestore and write in struct field in Swift?

The problem that I faced is, I can reach every field in Firestore and write in structs except array&map field.
My firestore data is something like:
let data : [String : Any] = [
"name" : "House A",
"price" : 2000,
"contents" : [
"water" : true,
"internet" : false
]
]
Here is getDocument function:
let docRef = db.collection("example").document("example")
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let data = Houses(Doc: document)
...
...
...
} else {
print(error, "Item not found")
}
}
Here is my structs :
struct Houses {
var name: String?
var price: Int
var contents : Contents
init(Doc: DocumentSnapshot){
self.name = Doc.get("name") as? String ?? ""
self.price = Doc.get("price") as! Int
self.contents = Doc.get("contents") as! Contents
}
}
struct Contents {
var water: Bool
var internet : Bool
init?(data: [String: Any]) {
guard let water = data["water"] as? Bool,
let internet = data["internet"] as? Bool else {
return nil
}
self.water = water
self.internet = internet
}
}
The other version of Contents :
struct Contents {
var water: Bool
var internet : Bool
init(Doc: DocumentSnapshot){
self.water = Doc.get("water") as! Bool
self.internet = Doc.get("internet") as! Bool
}
}
UPDATED
The problem solved with changing this line:
self.contents = Doc.get("contents") as! Contents
to;
self.contents = Contents(data: Doc.get("contents") as! [String : Any])
name and price returns what I expected but contents always return nil. I tried to configure Contents but results are same. I think, I have to configure struct named Contents.
Any help would be appreciated.

Fetch multi level node from Firebase

I am trying to fetch the "friends" from the node to be able to show them in UICollectionView afterwards. I now realized that I have to use a struct and place the Friends array inside. I am struggling now to understand how to fetch them into that array (you can see it at the bottom of the post). Data is stored in a firebase node. How can I grab the data and what would be the procedure to place it in UICollectionView afterwards? This is my function so far to retrieve.
UPDATE: (I think I am fetching correctly now but I don't get any results. Is there something that I should do in collection view? or what am I doing wrong?)
UPDATE: Here is my code for post fetching:
func fetchPosts3() {
ref.child("Users_Posts").child("\(unique)").queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
if snapshot.value as? [String : AnyObject] != nil {
let allPosts = snapshot.value as! [String : AnyObject]
self.posts.removeAll()
for (_, value) in allPosts {
if let postID = value["postID"] as? String,
let userIDDD = value["userID"] as? String
{
//ACCESS FRIENDS
ref.child("Users_Posts").child("\(unique)").child(postID).child("friends").queryOrderedByKey().observeSingleEvent(of: .value, with: { (snap) in
print("FRIENDS: \(snap.childrenCount)")
//var routine = self.postsWithFriends[0].friends
for friendSnap in snap.children {
if let friendSnapshot = friendSnap as? DataSnapshot {
let friendDict = friendSnapshot.value as? [String: Any]
let friendName = friendDict?["name"] as? String
let friendPostID = friendDict?["postID"] as? String
let postsToShow = PostWithFriends(id: userIDDD, friends: [Friend(friendName: friendName!, friendPostID: friendPostID!)])
self.postsWithFriends.append(postsToShow)
print("COUNTING: \(self.postsWithFriends.count)")
// then do whatever you need with your friendOnPost
}
}
})
}
}
//GET LOCATION
self.collectionView?.reloadData()
self.posts.sort(by: {$0.intervalPosts! > $1.intervalPosts!})
}
})
ref.removeAllObservers()
}
That's how the data looks at the database:
{
"-LN2rl2414KAISO_qcK_" : {
"cellID" : "2",
"city" : "Reading",
"date" : "2018-09-23 00:41:26 +0000",
"friends" : {
"UJDB35HDTIdssCtZfEsMbDDmBYw2" : {
"name" : "Natalia",
"postID" : "-LN2rl2414KAISO_qcK_",
"userID" : "UJDB35HDTIdssCtZfEsMbDDmBYw2"
},
"Vyobk7hJu5OGzOe7E1fcYTbMvVI2" : {
"name" : "Gina C",
"postID" : "-LN2rl2414KAISO_qcK_",
"userID" : "Vyobk7hJu5OGzOe7E1fcYTbMvVI2"
}
},
}
}
And this is my object that's stored into array
struct PostWithFriends {
var postID : String?
var friends: [Friend]
}
class Friend : NSObject {
var friendName: String?
var friendUserID: String?
var postID: String?
init(friendName: String, friendPostID: String) {
self.friendName = friendName
self.postID = friendPostID
}
}
Replace this
if let friend = snap.value as? [String : AnyObject] {
}
With this:
for friendSnap in snap.children {
if let friendSnapshot = friendSnap as? FIRDataSnapshot {
let friendOnPost = FriendOnPost()
let friendDict = friendSnapshot.value as? [String: Any]
friendOnPost.name = friendDict?["name"] as? String
friendOnPost.friendUserID = friendDict?["userID"] as? String
friendOnPost.postID = friendDict?["postID"] as? String
// then do whatever you need with your friendOnPost
}
}

Download address from firebase in mapView

Trying to upload an address from firebase and post it on mapView. But for some reason the address doesn't want to unload. The address spelled out by the string in firebase example - Moscow, Street so-and-so, house 1. What could be the reason for not loading the data?
var allAddresses: String = ""
addressRef = Database.database().reference(withPath: "Address")
addressRef.observe(.value, with: { (snapshot) in
let value = snapshot.value as! NSDictionary
self.allAddresses = value["address"] as? String ?? ""
})
}
Firebase:
{
«Address» : {
«AddressOne» : {
"address" : "Москва, Пресненская набережная д.8, квартира 195, подъезд 94",
},
"AddressTwo» : {
"address" : "Москва, ул. Правды д.24 строение 3",
},
"AddressThree» : {
"address" : "Москва,ул.Электрозаводская д.21",
}
}
}
You're attaching a value observer to /Address, which means that you get a snapshot with all data at that location. Since there are multiple child addresses, your code will need to handle those.
The simplest way to do that is by listening for .childAdded instead of .value:
var allAddresses: String = ""
addressRef = Database.database().reference(withPath: "Address")
addressRef.observe(.childAdded, with: { (snapshot) in
let value = snapshot.value as! NSDictionary
self.allAddresses = value["address"] as? String ?? ""
})
Now your code gets triggered with each individual address.
You can also stick to observing .value and then loop over the results in the snapshot:
var allAddresses: String = ""
addressRef = Database.database().reference(withPath: "Address")
addressRef.observe(.value, with: { (snapshot) in
for address in snapshot.children.allObjects as [FIRDataSnapshot] {
let value = address.value as! NSDictionary
self.allAddresses = value["address"] as? String ?? ""
})
})
Use optional unwrapping and set breakpoints on some lines to see where your code doesn't do what you want :
var allAddresses: String = ""
addressRef.observe(.value, with: { (snapshot) in
if let value = snapshot.value as? [String:Any] {
if let address = value["address"] as? String {
self.allAddresses = address
} else {
print("no address in value")
}
} else {
print("no value from firebase")
}
})
}
You should also use swift types in swift, it's a good practice.

Missing argument for parameter 'dictionary' in call

This is my NSObject class file that I am using to populate my collectionView cell. I am fetching my data from firebase and populating the collectionViewCell with it. Xcode is giving this error "Missing argument for parameter 'dictionary' in call" I have tried all I can but I could not figure out what is missing. What is causing this error and how can I fix it?
class BusinessCategory: NSObject {
var ref: FIRDatabaseReference!
var name: String?
var logo: String?
var featurebusiness: [SampleBusinesses]?
var type: String?
init(dictionary: [String: Any]) {
self.name = dictionary["BusinessName"] as? String ?? ""
self.logo = dictionary["logo"] as? String ?? ""
}
static func sampleBusinessCategories() -> [BusinessCategory] {
var FinancialInstitutionCatergory = BusinessCategory()
FinancialInstitutionCatergory.name = "Financial Institutions"
var featurebusiness = [SampleBusinesses]()
//logic
FIRDatabase.database().reference().child("BusinessCategories/Banks").observeSingleEvent(of: .childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let financeApp = SampleBusinesses()
financeApp.setValuesForKeys(dictionary)
financeApp.name = dictionary["BusinessName"] as? String
featurebusiness.append(financeApp)
}
FinancialInstitutionCatergory.featurebusiness = featurebusiness
print(snapshot)
}, withCancel: nil)
return [FinancialInstitutionCatergory]
}
}
The error is stating you need to include the dictionary parameter in whatever line has the error; an example might look something like this:
var FinancialInstitutionCatergory = BusinessCategory(dictionary: [String : Any])
It's unclear which line in your code has the error; you'll need to include the parameter somewhere.