set user location with GeoFire in Xcode 10 - firebase-realtime-database

My coding (as you will see) and familiarity with Xcode and Swift is basic, but I'm learning... I realize my nesting of the code isn't the greatest, if good at all. I am trying to integrate GeoFire within my Xcode 10 app. I have looked for a clear explanation, but I am still yet to find an answer. I have a registrationForm to register users. The initial Firebase setup was straightforward, and I have a fully functional form that registers the user in Firebase. Now I am trying to add GeoFire in order to track the user's location.
I have tried a bunch of approaches from YouTube videos and websites, none of which work.
import UIKit
import Foundation
import CoreLocation
import Firebase
import GeoFire
class SecondViewController: UIViewController {
//Firebase DB ref
var refDriver: DatabaseReference!
#IBOutlet weak var name: UITextField!
#IBOutlet weak var employeenumber: UITextField!
#IBOutlet weak var label: UILabel!
#IBAction func submit(_ sender: UIButton) {
addDriver()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//configuring firebase and ensures no error is returned
if(FirebaseApp.app() == nil){
FirebaseApp.configure()
}
//getting a reference to the node driver
refDriver = Database.database().reference().child("driver");
}
func addDriver(){
//generating a new key inside driver node
//and also getting the generated key
let key = refDriver.childByAutoId().key
//creating driver with the given values
let driver = ["id":key,
"name": name.text! as String,
"employeenumber": employeenumber.text! as String
]
//adding the driver to Firebase
refDriver.child(key!).setValue(driver)
//displaying submit message
label.text = "You are registered."
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

hey try importing the module after you install the pod
import GeoFire
**var geoFire: GeoFire?**
var myQuery: GFQuery?
in viewdidLoad() function call the setupGeofire()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupGeoFire()
}
func setupGeoFire() {
let geoFireRef = Database.database().reference().child("user_locations")
let geoFire = GeoFire(firebaseRef: geoFireRef)
self.geoFire = geoFire
loadUsersWithin(radius: 100)
}
in setupGeoFire i made a var geoFireRef this is just the node you want to save your users location info
let geoFireRef = Database.database().reference().child("user_locations")
then we make the geoFire object by passing in that node location, our geoFireRef var
then just making the geoFire object accessible everywhere using
``` self.geoFire = geoFire`` `
if you want to get users in a specific radius (km) you can check the func below too
func loadUsersWithin(radius: Double){
guard let uid = Auth.auth().currentUser?.uid else{return}
geoFire?.getLocationForKey(uid, withCallback: { (userLocation, err) in
guard let userLocation = userLocation else{return}
self.myQuery = self.geoFire?.query(at: userLocation, withRadius: radius)
self.myQuery?.observe(.keyEntered, with: { (key, location) in
if key == uid{
return
}
Database.database().reference().child("users").child(key).observeSingleEvent(of: .value, with: { (snapshot) in
guard let userDictionary = snapshot.value as? [String:Any] else{return}
let user = User(uid: key, dictionary: userDictionary)
self.user = user
self.usersArray.append(user)
self.nearbyCV.reloadData()
}, withCancel: { (err) in
print(err)
})
})
})
}
}
Here is my User.swift Data Model
import UIKit
class User{
var name: String
var userImgUrlString: String
var age: String
var uid: String
var aboutMeText:String
init(uid: String,dictionary: [String: Any]) {
self.name = dictionary["username"] as? String ?? ""
self.userImgUrlString = dictionary["profileImageUrlString"] as? String ?? ""
self.aboutMeText = dictionary["aboutMe"] as? String ?? ""
self.uid = uid
self.age = dictionary["age"] as? String ?? ""
}
}```
hope this helps yo it was hard to find info for me too when i just started but it gets better

Related

get user name from firebase xcode

Trying to get a user name from firebase and then displayed on a label.
Any help?
heres a sample of the code.
import Firebase
class HomeViewController: UIViewController {
#IBOutlet weak var userName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func getUserName(_ message:String) {
let uid = Auth.auth().currentUser?.uid
Database.database().reference().child("users").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
self.userName.text = dictionary["firstname"] as? String
}
})
}
}
Swift
let currentUserName = Auth.auth().currentUser?.displayName
print(currentUserName)
user.displayName It's in the firebase docs
You have To try this code...(After Authentication , You have to create display name ("Firstname + Lastname") inside mobile app using below mentioned code )
if let currentUser = Auth.auth().currentUser {
let changeRequest = currentUser.createProfileChangeRequest()
changeRequest.displayName = "Firstname" + "Lastname"
changeRequest.commitChanges(completion: { (error) in
if let error = error {
print("--> firebase user display name error:- ", error)
}
})
}
After completion of this code we got current user displayname.

Reading data from firebase in swift but not being able to display it. Possibly variable scope error [duplicate]

This question already has an answer here:
Returning data from function in Firebase observer code block swift
(1 answer)
Closed 4 years ago.
`
import UIKit
import FirebaseDatabase
import FirebaseAuth
class mapVC: UIViewController {
var name:String = ""
var number:String = ""
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.setHidesBackButton(true, animated:true);
var dict = fetchData()
print(dict)
}
func fetchData()->[String:String]{
var ref: DatabaseReference!
ref = Database.database().reference()
var uid:String=""
var email:String=""
let user = Auth.auth().currentUser
if let user = user {
let uid_local = user.uid
let email_local = user.email
uid=uid_local
email=email_local!
}
ref.child("Users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
self.name = value?["name"] as? String ?? ""
print(value?["name"] as? String ?? "")
self.number = value?["number"] as? String ?? ""
print(value?["number"] as? String ?? "")
})
return(["Name":name,"Number":number,"Email":email,"UID":uid]);
}
}
Reading data from firebase in Swift but not being able to display it. Possibly variable scope error. The output of print(value?["name"] as? String ?? "" is the expected result) but is not being assigned to the self.name variable. I believe this is a scope error but I don't know for sure yet. All and any help is greatly appreciated since I am comparitively new to iOS and Swift.
Because receiving data from Firebase is asynchronous task, you can't do it like this. You have to use completion handler for this.
First add completion handler as parameter of a function, also you don't need return type
func fetchData(_ completion: #escaping ([String:String]) -> ())
Then call completion when you receive your data
ref.child("Users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
...
completion(["Name":name,"Number":number,"Email":email,"UID":uid])
})
now you have access to data from Firebase in closure of fetchData function
override func viewDidLoad() {
...
fetchData { dict in
print(dict)
}
}
Note: you won't have these data until receiving data from Firebase isn't done, so it takes some time

Can't retrieve data from Firebase with Swift

I am trying to create a view controller displaying information of the clicked shop from a UITableView on the previous view controller. However, I cannot retrieve data in the new view controller and I don't know how to solve this issue. Here is my database structure. Thank you for the help.
import UIKit
import Firebase
import FirebaseDatabase
class ShopViewController: UIViewController {
var name :String? // This is the name of the cell clicked on the previous viewcontroller
var ref: DatabaseReference!
#IBOutlet weak var shopName: UILabel!
#IBOutlet weak var shopType: UILabel!
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
shopName.text = name
let ref = Database.database().reference().child("shops").childByAutoId().child(name!).child("Details")
ref.observe(.childAdded, with: { snapshot in
let snapshotValue = snapshot.value as! NSDictionary
let imageUrlString = snapshotValue["imageURL"] as! String
print(imageUrlString)
let shoptype = snapshotValue["type"] as! String
self.shopType.text = shoptype
})
}
}
The reference that you have is wrong, childByAutoId() is used to generate a unique id in your database.
Also the id that you currently have is the userid, you need to retrieve the userid:
let user = Auth.auth().currentUser
let uid = user.uid
then the location should be:
let ref = Database.database().reference().child("shops").child(uid).child("Eat").child("Details")
So there's a couple of issues here, first off is your data structure.
Something along these lines would be much easier to read from. You shouldn't need the name field as far as I can tell, and is there a requirement to have the details nested?
Reading the data
Next up, you can refactor your data reference code. I'd firstly recommend extracting the logic into its own method rather than having it directly in viewDidLoad:
var ref: DatabaseReference!
override func viewDidLoad() {
shopName.text = name
self.ref = Database.database().reference()
getShopDetails()
}
func getShopDetails() {
guard let shopName = name else { return }
ref.child("shops").child(shopName).observe(.childAdded, with: { snapshot in
guard let shop = snapshot.value as? [String: AnyObject] else { return }
let imageUrlString = shop["imageURL"] as! String
print(imageUrlString)
let shoptype = shop["type"] as! String
self.shopType.text = shoptype
})
}
I've modified to set the database reference value as otherwise this would crash if you tried to use it elsewhere. I've also added a couple of guard statements to minimise the number of explicit unwraps.
I can't comment on your list code, but have you considered doing the call for all of the shops in this, and mapping the result to a data object, which you can then use to display the data in the screen we're detailing here, rather than doing a separate query every-time you open a detail screen.

Fetch Firebase child data in Swift

I already have setup Firebase on Xcode 9.
My question:
I want to retrieve data from Firebase. This works but when I add another text in a group it wont retrieve.
Example
FIREBASE:
lang :
"Deutsch"
"Englisch"
Firebase Image Example because it's difficult to explain:
import UIKit
import FirebaseDatabase
class ViewController: UIViewController {
#IBOutlet weak var outputone: UILabel!
// #IBOutlet weak var outputtwo: UILabel!
var dbReference: DatabaseReference?
var dbHandle: DatabaseHandle?
override func viewDidLoad() {
super.viewDidLoad()
dbReference = Database.database().reference()
// dbReference?.child("lang").childByAutoId().setValue("Deutsch")
// dbReference?.child("lang").childByAutoId().setValue("Englisch")
// retrieve data
dbHandle = dbReference?.child("lang").observe(.childAdded, with:{ (snapshot) in
let lang:String? = snapshot.value as? String
self.outputone.text = (lang)
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I want that "Deutsch" displays in outputone and "Englisch" in outputtwo
Can someone help ?
Right now you retrieve the entire lang node. So the snapshot you get from Firebase contains both "de": "Deutch" and "en": "English". When you now do snapshot.value as? String, it returns nil since the snapshot doesn't have a single, simple string value.
To get the specific values, you first need to tell the snapshot which child property you want: de or en, and then you can get the string value.
dbHandle = dbReference?.child("lang").observe(.value, with:{(snapshot) in
let de:String? = snapshot.childSnapshot(forPath:"de").value as? String
let en:String? = snapshot.childSnapshot(forPath:"en").value as? String
self.outputone.text = (de)
self.outputtwo.text = (en)
})
when retrieving the data from Firebase, it's come as a chunk. This is when the downCasting comes in handy.
dbHandle = dbReference?.child("lang").observe(.childAdded, with:{(snapshot) in
guard let dictionary = snapshot.value as? [String:Any],
let de = dictionary["de"] as? String,
let en = dictionary["en"] as? String else {
"error occurs when down casting.."
return}
self.outputone.text = de
self.outputwo.text = en
})
whenever dealing with a dictionary approach, it's dangerous to hardcode string key every time. it's error-prone. I recommend you create a struct holding each key so you don't need to type string every time needed.

adding a child to a child in Firebase Database with Swift

I am working with the following Firebase Database:
I add new chatIDs with the following code:
DatabaseReference.users(uid: self.uid).reference().child("chatIds/\(chat.uid)").setValue(chat.uid)
I need to add a single child to the individual "chatIDs" that is a random string that I will generate but I haven't worked with Firebase for that long so I am not sure how to do add children this far in. How can I write the code to do this?
Based on your database structure, a possible implementation of you want would be:
let ref = Database.database().reference()
// Generating the chat id
let refChats = ref.child("chats")
let refChat = refChats.childByAutoId()
// Accessing the "chatIds branch" from a user based on
// his id
let currentUserId = self.uid
let refUsers = ref.child("users")
let refUser = refUsers.child(currentUserId)
let refUserChatIds = refUser.child("chatIds")
// Setting the new Chat Id key created before
// on the "chatIds branch"
let chatIdKey = refChat.key
let refUserChatId = refUserChatIds.child(chatIdKey)
refUserChatIds.setValue(chatIdKey)
I think what you're looking for is this
let key = firebaseRef.child("users").child("\(String(describing: uid))").child("chatIds").childByAutoId().key
let timestamp = Int(Date().timeIntervalSince1970)
let child = ["key":key,
"name": name as String,
"date": birth as String,
"created": "\(timestamp)"]
firebaseRef.child("users").child("\(String(describing: uid!))").child("chatIds").child(key).setValue(child)
as example I'm saving the key, name, date, and created, as Childs of chatIds, with the childByAutoId, that generates you a random key, so you can locate it when searching the object.
import UIKit
import Firebase
class ChatListVC: UIViewController {
var ref: FIRDatabaseReference!
var messages: [FIRDataSnapshot]! = []
fileprivate var _refHandle: FIRDatabaseHandle?
override func viewDidLoad() {
super.viewDidLoad()
self.userDetail()
}
func userDetail(){
_refHandle = self.ref.child("users").child("child id").observe(.value, with: { [weak self] (snapshot) -> Void in
guard let strongSelf = self else { return }
guard let dict = snapshot.value as? [String:Any] else { return }
//access data from dict
let MyName = dict["MyName"] as? String ?? ""
})
}

Resources