Want to load user profiles onto tableView using Firebase and GeoFire - swift

I've been working on a ios App that uses Firebase and GeoFire. I was able to store user coordinates using GeoFire into firebase. I am now trying to load the user details onto a tableview with GeoFire but have been unsuccessful. Without Geofire I was able to load all the user details onto the table, but now it just shows a blank table. Would someone please give me some pointers?
var myQuery: GFQuery?
var geoFireRef: DatabaseReference?
var geoFire: GeoFire?
var user = Auth.auth().currentUser
var filteredUsers = [Users]()
var users = [Users]()
var isSearching = false
override func viewDidLoad() {
super.viewDidLoad()
geoFireRef = Database.database().reference(fromURL: "https://yala-2018.firebaseio.com/").child("user_locations")
geoFire = GeoFire(firebaseRef: geoFireRef!)
let userLat = UserDefaults.standard.value(forKey: "current_latitude") as! String
let userLong = UserDefaults.standard.value(forKey: "current_longitude") as! String
let location:CLLocation = CLLocation(latitude: CLLocationDegrees(Double(userLat)!), longitude: CLLocationDegrees(Double(userLong)!))
myQuery = geoFire?.query(at: location, withRadius: 100)
myQuery?.observe(.keyEntered, with: { (key, location) in
if key != Auth.auth().currentUser?.uid {
// print("KEY:\(String(describing: key)) and location:\(String(describing: location))")
let ref = Database.database().reference(fromURL: "https://yala-2018.firebaseio.com/").child("Users").child(key)
ref.observeSingleEvent(of: .value, with: { (DataSnapshot) in
if let dictionary = DataSnapshot.value as? [String: AnyObject] {
let user = Users()
user.name = DataSnapshot.key
// user.setValuesForKeys(dictionary)
user.name = dictionary["name"] as! String
user.age = dictionary["age"] as! String
user.sex = dictionary["sex"] as! String
user.profileImageUrl = dictionary["profilePicUrl"] as! String
self.users.append(user)
print(user.name, user.age, user.profileImageUrl)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
})
} else {
print("cannot retrieve users based on location")
}
})
tableView.dataSource = self
tableView.delegate = self
searchBar.delegate = self
searchBar.returnKeyType = UIReturnKeyType.done
tableView.reloadData()
}

Related

Retrieve data from Firebase before adding to a map

I'm building an application where a user can store companies information (like name, address, latitude, longitude to a Firebase real-time database).
With that information, those companies are then presented in a map as annotations.
My viewDidLoad is shown below
override func viewDidLoad() {
super.viewDidLoad()
// Definitions
let autenticacao = Auth.auth()
let idUsuarioLogado = (autenticacao.currentUser?.uid)!
let database = Database.database().reference()
let usuarios = database.child("usuarios")
let clinicas = database.child("clinicas")
// Map - User location
self.mapa.delegate = self
self.gerenciadorLocalizacao.delegate = self
self.gerenciadorLocalizacao.desiredAccuracy = kCLLocationAccuracyBest
self.gerenciadorLocalizacao.requestWhenInUseAuthorization()
self.gerenciadorLocalizacao.startUpdatingLocation()
// retrieve user information
usuarios.child(idUsuarioLogado).observe(DataEventType.value) { (snapshot) in
let dados = snapshot.value as? NSDictionary
let emailUsuario = dados?["email"] as! String
let nomeUsuario = dados?["nome"] as! String
let perfilUsuario = dados?["perfil"] as! String
let idUsuario = snapshot.key
let usuario = Usuario(email: emailUsuario, nome: nomeUsuario, uid: idUsuario, perfil: perfilUsuario)
print("User profile \(perfilUsuario)")
}
// Clinicas listeners
clinicas.observe(DataEventType.childAdded) { (snapshot) in
let dados = snapshot.value as? NSDictionary
//print("Dados na leitura \(dados)")
let clinica = Clinica()
clinica.identificador = snapshot.key
clinica.nome = dados?["nome"] as! String
clinica.endereco = dados?["endereco"] as! String
clinica.cidade = dados?["cidade"] as! String
clinica.cep = dados?["cep"] as! String
clinica.estado = dados?["estado"] as! String
clinica.latitude = dados?["latitude"] as! String
clinica.longitude = dados?["longitude"] as! String
clinica.urlImagem = dados?["urlImagem"] as! String
clinica.idImagem = dados?["idImagem"] as! String
self.clinicasR.append(clinica)
}
// add annotations to the map
for oneObject in self.todasAnotacoes {
print("Oneobj \(oneObject)")
let umaAnotacao = MinhaAnotacao()
var oneObjLoc: CLLocationCoordinate2D = CLLocationCoordinate2DMake(oneObject.objLat, oneObject.objLong)
umaAnotacao.coordinate = oneObjLoc
umaAnotacao.title = oneObject.objName
umaAnotacao.subtitle = oneObject.objDesc
umaAnotacao.category = oneObject.objCat
self.anotacaoArray.append(umaAnotacao)
self.mapa.addAnnotations(self.anotacaoArray)
}
}
My viewDidLoad is "structured" in 4 main blocks (even though they run in a different order given they are asynchronous):
Definition;
Retrieve user profile;
Retrieve companies information (name, latitude, longitude);
Add annotations to the map.
As those are asynchronous functions, they run in different order and this is what is causing trouble to me.
note: There is still one piece missing here that is to feed all annotations to the variable todasAnotacoes which I'll do once I can retrieve data before triggering add annotation "block".
As annotation info comes from the firebase database, I should only have it executed once the clinicas.observe(DataEventType.childAdded) { (snapshot) in is concluded.
As is today, the sequence Xcode runs is:
Definitions
Add annotations;
retrieve data from firebase with companies details
I've tried adding the add annotation block to the closure, adding a dispatchqueue but none of those really worked. I also did a lot os search in stackoverflow but I couldn't find anything that i can use (or I wasn't able to understand).
So, in summary I need to run the add annotations after retrieving all data from Firebase.
Any ideas on how I could do that?
EDIT 1 - Final code with the suggested updates
override func viewDidLoad() {
super.viewDidLoad()
// Retrieving Logger user data and hidding "add" button if applicable
ProgressHUD.show("Carregando...")
let autenticacao = Auth.auth()
let idUsuarioLogado = (autenticacao.currentUser?.uid)!
let database = Database.database().reference()
let usuarios = database.child("usuarios")
var isUserLoaded = false
var isClinicsLoaded = false
usuarios.child(idUsuarioLogado).observe(DataEventType.value) { (snapshot) in
let dados = snapshot.value as? NSDictionary
let emailUsuario = dados?["email"] as! String
let nomeUsuario = dados?["nome"] as! String
let perfilUsuario = dados?["perfil"] as! String
let idUsuario = snapshot.key
let usuario = Usuario(email: emailUsuario, nome: nomeUsuario, uid: idUsuario, perfil: perfilUsuario)
isUserLoaded = true
if (isUserLoaded && isClinicsLoaded) {
self.addAnnotationsToMap();
}
//print("\(usuario.email) e \(usuario.nome) e \(usuario.uid) e \(usuario.perfil)")
}
// Option to code above
/*if Auth.auth().currentUser != nil {
if let uid = (Auth.auth().currentUser?.uid) {
let database = Database.database().reference()
let usuarios = database.child("usuarios").child(uid)
usuarios.observe(.value) { (snapshot) in
let dados = snapshot.value as? NSDictionary
let emailUsuario = dados?["email"] as! String
let nomeUsuario = dados?["nome"] as! String
let perfilUsuario = dados?["perfil"] as! String
let idUsuario = snapshot.key
let usuario = Usuario(email: emailUsuario, nome: nomeUsuario, uid: idUsuario, perfil: perfilUsuario)
print("Got here \(usuario.email) e \(usuario.nome) e \(usuario.uid) e \(usuario.perfil)")
if perfilUsuario != "admin" {
self.navigationItem.rightBarButtonItems?.remove(at: 1)
print("Disable + Button")
}
}
}
}*/
// Map - User location
self.mapa.delegate = self
self.gerenciadorLocalizacao.delegate = self
self.gerenciadorLocalizacao.desiredAccuracy = kCLLocationAccuracyBest
self.gerenciadorLocalizacao.requestWhenInUseAuthorization()
self.gerenciadorLocalizacao.startUpdatingLocation()
let clinicas = database.child("clinicas")
// Clinicas listeners
clinicas.observe(DataEventType.value) { (snapshots) in
for child in snapshots.children {
let snapshot = child as! DataSnapshot
print("Clinicas Mapeadas - end")
let dados = snapshot.value as? NSDictionary
//print("Dados na leitura \(dados)")
let clinica = Clinica()
clinica.identificador = snapshot.key
clinica.nome = dados?["nome"] as! String
clinica.endereco = dados?["endereco"] as! String
clinica.cidade = dados?["cidade"] as! String
clinica.cep = dados?["cep"] as! String
clinica.estado = dados?["estado"] as! String
clinica.latitude = dados?["latitude"] as! String
clinica.longitude = dados?["longitude"] as! String
clinica.urlImagem = dados?["urlImagem"] as! String
clinica.idImagem = dados?["idImagem"] as! String
self.clinicasR.append(clinica)
self.todasAnotacoes.append((objLat: Double(clinica.latitude) as! CLLocationDegrees, objLong: Double(clinica.longitude) as! CLLocationDegrees, objName: clinica.nome, objDesc: clinica.endereco, objId: clinica.identificador))
}
isClinicsLoaded = true
if (isUserLoaded && isClinicsLoaded) {
self.addAnnotationsToMap();
}
}
/* NOT IN USE FOR NOW
let latitude = Double(-23.623558)
let longitude = Double(-46.580787)
let localizacao: CLLocationCoordinate2D = CLLocationCoordinate2D.init(latitude: latitude, longitude: longitude)
let span: MKCoordinateSpan = MKCoordinateSpan.init(latitudeDelta: 0.01, longitudeDelta: 0.01)
let regiao = MKCoordinateRegion.init(center: localizacao, span: span)
self.mapa.setRegion(regiao, animated: true)*/
ProgressHUD.dismiss()
}
// add annotations to the map
func addAnnotationsToMap() {
anotacaoArray = []
for oneObject in self.todasAnotacoes {
for oneObject in self.todasAnotacoes {
// print("Oneobj \(oneObject)")
let umaAnotacao = MinhaAnotacao()
var oneObjLoc: CLLocationCoordinate2D = CLLocationCoordinate2DMake(oneObject.objLat, oneObject.objLong)
umaAnotacao.coordinate = oneObjLoc
umaAnotacao.title = oneObject.objName
umaAnotacao.subtitle = oneObject.objDesc
umaAnotacao.identicadorMapa = oneObject.objId
self.anotacaoArray.append(umaAnotacao)
print("Annotation added \(todasAnotacoes.count) - end")
}
self.mapa.addAnnotations(self.anotacaoArray)
self.todasAnotacoes = []
self.anotacaoArray = []
// print("Annotations added 2 - end")
}
}
The second observer in your code is using .childAdded, which means that closure gets called for each individual child node under clinicas. This makes it hard to know when you're done with clinics, so I recommend first switching that observer over to .value, with something like:
clinicas.observe(DataEventType.value) { (snapshots) in
for child in snapshots.children {
let snapshot = child as! DataSnapshot
let dados = snapshot.value as? NSDictionary
//print("Dados na leitura \(dados)")
let clinica = Clinica()
clinica.identificador = snapshot.key
clinica.nome = dados?["nome"] as! String
clinica.endereco = dados?["endereco"] as! String
clinica.cidade = dados?["cidade"] as! String
clinica.cep = dados?["cep"] as! String
clinica.estado = dados?["estado"] as! String
clinica.latitude = dados?["latitude"] as! String
clinica.longitude = dados?["longitude"] as! String
clinica.urlImagem = dados?["urlImagem"] as! String
clinica.idImagem = dados?["idImagem"] as! String
self.clinicasR.append(clinica)
}
// At this point we're done with all clinics
}
As you'll have notice this code now uses a loop to iterate over snapshots.children. So we're processing all child nodes of clinicas in one closure, which makes it much easier to know when we're done.
Now with the above change we have two closures that get called individually, and you have some code that you want to run when both of them are done. There are a few ways to synchronize this code.
The first way is what you've already tried: putting the clinicas.observe block into the closure of usuarios.child(idUsuarioLogado).observe(. Now that the clinics observer uses .value, you'll find that this is much easier to get working.
But I'd like to show an alternative below, where we use two simple flags to determine if both closures are done. For this you will have to put the code that runs after the data is loaded into a separate function, something like this:
func addAnnotationsToMap() }
for oneObject in self.todasAnotacoes {
...
}
}
Now in each of the two closures, we're going to set a flag when they're done. And then at the end of each of the closures, we'll detect whether both flags are set, and then call the new function if we have all data we need.
So at the start of viewDidLoad we define our two flags:
let isUserLoaded = false
let isClinicsLoaded = false
And then at the end of loading the user:
usuarios.child(idUsuarioLogado).observe(DataEventType.value) { (snapshot) in
let dados = snapshot.value as? NSDictionary
let emailUsuario = dados?["email"] as! String
let nomeUsuario = dados?["nome"] as! String
let perfilUsuario = dados?["perfil"] as! String
let idUsuario = snapshot.key
let usuario = Usuario(email: emailUsuario, nome: nomeUsuario, uid: idUsuario, perfil: perfilUsuario)
isUserLoaded = true
if (isUserLoaded && isClinicsLoaded) {
addAnnotationsToMap();
}
}
And similar code (setting the isClinicsLoaded flag) to the other closure.
With these in place, you'll start loading the user data and clinics when the code runs, and then whichever one of them completes last, will call the new addAnnotationsToMap function.
Another alternative is to use a DispatchGroup, which is an Apple-specific construct that can also make this much simpler. Read more about it in this answer (and the links from there): Wait until swift for loop with asynchronous network requests finishes executing

How to Update CollectionView Model with Firebase?

I am trying to intitialize my Collection View Model which holds a list of [Users] with a firebase snap that holds data.
Here is the Collection View Model code:
typealias JSON = [String:Any]
class HomeDataSource: Datasource {
var users: [User]
init?(with snapshot:DataSnapshot) {
var users = [User]()
guard let snapDict = snapshot.value as? [String: [String:Any]] else {return nil}
for snap in snapDict {
guard let user = User(dict: snap.value) else {continue}
users.append(user)
}
self.users = users
}
}
User Model:
struct User {
let name: String
let username: String
init?(dict: JSON) {
guard let name = dict["name"] as? String,
let email = dict["email"] as? String
else {return nil}
self.name = name
self.username = email
}
}
Firebase Snap:
Snap (users) {
8CVeHMNHI6hZAWj1zhGHEjPwYYz1 = {
email = "Silviu#isidors.sjsj";
name = Bshdjdj;
};
9CuqgR4Es7TOPPJQEpSnQXlfYnm1 = {
email = "Test#silviu.com";
name = "Test#silviu.com";
};
DBqGWlpdJKME570euqUz2rqI5Z83 = {
email = "Test#test.test";
name = Test;
};
}
Fetch Function:
func fetchUser() {
let ref = Database.database().reference().child("users")
ref.observe(.childAdded, with: { (snapshot) in
let user = User(dict: snapshot.value as! JSON)
self.users.append(user!)
print(self.users)
let new = HomeDataSource(with: snapshot)
print(new)
DispatchQueue.main.async(execute: {
self.datasource = new
self.collectionView?.reloadData()
})
}, withCancel: nil)
}
Right now, I am getting an array of Users from Firebase, however my collection view won't update.
My question is how should I update my Collection View Model and Fetching Function so it can fetch data from Firebase and Populate the Collection View Correctly?
So the problem was the node I was targeting. Removed "child("users")" form fetching function and targeted the whole users node with uids. Then I was looping the snapshot.value while I casted it in [String:[String:Any]], because each snapshot.valuelooked like this (key: "vijwUkzAlbgcqjAammfy0JuVMB33", value: ["name": Silviu, "email": Office#isidors.com]) Finally, I updated the HomeDataShource Class like this:
class HomeDataSource: Datasource {
var users: [User]
init(with snapshot:DataSnapshot) {
var users = [User]()
let snapDict = snapshot.value as? [String:[String:Any]]
for snap in snapDict! {
guard let user = User(snap: snap.value) else {continue}
users.append(user)
}
self.users = users
}

How to get to a specifec node in firebase database real-time

I got some messages key that was created by ChildByAutoID i am trying to access them or make the code read this keys to use this key somewhere else to get the some data from using this keys from firebase database
I tried to observe the UID of user to get keys but i can't get access to them as in code it show me the snap is the UID of user while iam trying to read nodes under the UID
in chat Controller
func sendMessage() {
let currentUserID = Auth.auth().currentUser?.uid
let databaseRef = Database.database().reference().child("messages")
let timestamp = Date.timeIntervalSinceReferenceDate
let text = typingMessageText.text as AnyObject
let receiverid = user?.userid as AnyObject
let senderid = currentUserID as AnyObject
let time = timestamp as AnyObject
let value: [String:AnyObject] = ["text": text, "receiverid": receiverid, "senderid": senderid, "timestamp": time]
let childref = databaseRef.childByAutoId()
childref.updateChildValues(value) { (error, myRef) in
print(myRef.key as Any)
let messageID = myRef.key
let mnm = [messageID : 1]
let userMessageRef = Database.database().reference().child("usersmesssages").child(currentUserID!)
userMessageRef.updateChildValues(mnm)
}
}
Observe function in tableview controller
func observeMessages(){
let currentUserID = Auth.auth().currentUser?.uid
let newDB = Database.database().reference().child("usersmesssages").child(currentUserID!)
newDB.observe(.value) { (DataSnapshot) in
let key = DataSnapshot.key
print("the Key is \(DataSnapshot)")
let dbRef = Database.database().reference().child("messages").child(key)
dbRef.observe(.childAdded, with: { (snap) in
print("thesnap is \(snap)")
if let dictinoary = snap.value as? [String: AnyObject] {
guard let receiverid = dictinoary["receiverid"] ,let senderid = dictinoary["senderid"], let text = dictinoary["text"], let time = dictinoary["timestamp"] else {
return
}
let messageValues = Message.init(text: text as? String, receiverid: receiverid as? String, senderid: senderid as? String, timestamp: time as? TimeInterval)
self.messages.append(messageValues)
dbRef.keepSynced(true)
self.ChatRoomTable.reloadData()
}
})
}
}
UPDATE
print("the data is \(DataSnapshot)")
Result from it
the data is Snap (KxTbutCjzGQmIIb2OCGHrCyLNS53) {
"-LVx10-pBVxXTN-p88pT" = 1;
"-LVx10rtB0SL0y_mn4FU" = 1;
"-LVx4XosKZhkUDPgq0TI" = 1;
"-LVx4YT3NqYi1Tj12QFj" = 1;
"-LVx4_e8_OKwUrpAR_BM" = 1;
"-LVy2TEHGZD99ALhutbX" = 1;
"-LVy2dZB_mSIicC-qcSA" = 1;
"-LVy2xkg0oTT6bLbdC4T" = 1;
"-LVy3H6a__Tm_DKnM639" = 1;
}
How to access this nodes that (= 1) it self or save it to variables to use it.

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()));
}
}
});

How can I retrieve data from firebase (Swift)

I am beginner and I would like to use swift as my programming language.
user can add register users and transfer money to each other, therefore when user type the photo number and email, the app can check if the typed number is registered in the firebase system
Any simple way to search if the user exist, thanks a lot
Here's the structure of the database
var ref: DatabaseReference!
var tref: DatabaseReference!
var handle : DatabaseHandle!
var usersArray = [NSDictionary?]()
var filteredUsers = [NSDictionary?]()
var user : NSDictionary?
override func viewDidLoad() {
super.viewDidLoad()
self.picker.isHidden = true
tref = Database.database().reference()
ref = Database.database().reference()
self.handle = self.ref?.child("users").child((Auth.auth().currentUser?.uid)!).child("contact").observe(.childAdded, with: { (snapshot) in
if let item = snapshot.value as? String {
self.pickerdata.append(item)
self.picker.reloadAllComponents()
}
})
self.picker.delegate = self
self.picker.dataSource = self
tref.child("users").queryOrdered(byChild: "phone").observe(.childAdded, with: {(snapshot) in
self.usersArray.append(snapshot.value as? NSDictionary)
})
print(usersArray.count)
}
#IBAction func ContactChange(_ sender: UITextField) {
filteredContent(searchText: contactText.text!)
print(filteredUsers.count)
print(usersArray.count)
print("ARRAY")
}
func filteredContent(searchText: String){
self.filteredUsers = self.usersArray.filter{ user in
let username = user!["phone"] as? String
return (username?.lowercased().contains(searchText.lowercased()))!
}
}
func findUsers(text: String){
self.handle = ref.child("users").queryOrdered(byChild: "phone").queryStarting(atValue: contactText.text!).queryEnding(atValue: contactText.text!+"\u{f8ff}").observe(.value, with: { snapshot in
if let item = snapshot.value as? String {
self.contact.append(item)
} else{
print("error")
}
})
}
The code above doesn't work much. Thanks so much for helping
The question is a little unclear but I think what the OP is asking is:
how to see if a user exists by their phone number?
If that's what's being asked, a node can be retrieved by query'ing for a child of that node. For example, let's create a query on the users node to find the child that contains the name: Kam querying by phone
let usersRef = firebaseRef.child("users")
let query = usersRef.queryOrdered(byChild: "phone").queryEqual(toValue: "2330504")
query.observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists() {
//need to iterate in case we find more than one match
for child in snapshot.children {
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let name = dict["name"] as! String
print("found \(name)")
}
} else {
print("not found")
}
}
When this code is run it would print
found Kam
assuming the phone number is 2330504