Fetch Firebase child data in Swift - ios

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.

Related

set user location with GeoFire in Xcode 10

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

Issue pulling content from Firebase into Swift

I'm trying to get quotes out of Firebase, and I'm struggling. Granted, I also have no idea what I'm doing. I could use some help!
In Firebase, my quotes are set up like :
root
--> quotes
--> quoteID
--> quoteText, quoteAttribution
I'm trying to pull quotes down from Firebase, add them to a local array (to later put in a dictionary), and then pull a random one to use in the app. I hope to put the quoteText into quoteLabel.text, and the quoteAttribution into authorLabel.text. I found this solution in another StackOverflow issue, but it throws the following error at line 43:
Could not cast value of type 'NSNull' (0x10f549740) to 'NSDictionary' (0x10f549178).
2018-07-21 22:49:50.241473-0400 Wavefully[72475:1126119] Could not cast value of type 'NSNull' (0x10f549740) to 'NSDictionary' (0x10f549178).
Does anyone have any tips for how I might pull quoteText and quoteAttribution out of Firebase to use in my app?
Here's my code:
class ViewController: UIViewController {
class quoteClass {
var uid = ""
var quote = ""
var author = ""
}
#IBOutlet weak var quoteLabel: UILabel!
#IBOutlet weak var authorLabel: UILabel!
var ref: DatabaseReference?
var databaseHandler: DatabaseHandle?
var quotesArray = [quoteClass]()
override func viewDidLoad() {
super.viewDidLoad()
// Set the reference to Firebase
ref = Database.database().reference()
let quotesRef = ref?.child("quotes")
quotesRef?.observeSingleEvent(of: .value, with: { (snapshot) in
for _ in snapshot.children {
let quoteSnap = snapshot
let quoteKey = quoteSnap.key
let thisQuoteRef = quotesRef?.child("quoteID")
thisQuoteRef?.observeSingleEvent(of: .value, with: { (quoteSnap) in
let singlequoteSnap = quoteSnap
let quoteDict = singlequoteSnap.value as! [String:AnyObject]
let quote = quoteDict["quoteText"]
let author = quoteDict["quoteAttribution"]
let aQuote = quoteClass()
aQuote.uid = quoteKey
aQuote.quote = quote as! String
aQuote.author = author as! String
print(aQuote.quote)
print(aQuote.author)
print(aQuote.uid)
self.quotesArray.append(aQuote)
})
}
})
let singleQuote = quotesArray.randomItem()!
print(singleQuote.uid)
print(singleQuote.quote)
print(singleQuote.author)}}
Thanks a ton for helping!
Alternatively you can also use your data by casting it into NSDictionary like below:
let dictionary = snapshot.value as? NSDictionary
let quote = dictionary["quoteText"] as? String ?? ""
Okay, so I was making it way harder than it needed to be. I did this, and it's working:
func grabData() {
ref = Database.database().reference()
ref?.child("quotes").observe(.value, with: {
snapshot in
for snap in snapshot.children.allObjects as! [DataSnapshot] {
guard let dictionary = snap.value as? [String:AnyObject] else {
return
}
let quote = dictionary["quoteText"] as? String
let author = dictionary["quoteAttribution"] as? String
let id = dictionary["quoteID"] as? String
self.quoteLabel.text = quote
self.authorLabel.text = author
print(quote!)
print(author!)
print(id!)
}
})
}
Now, I just have to call grabData() in my viewDidLoad to get a quote. Up next: randomize what quote is shown. Oh, and probably store it in Core Data or Realm for local storage. 🤓
Thanks for stopping by!

How to append to a segment of the Firebase realtime database using Xcode

As a summer project, I'm making an iOS quiz app that has predetermined users. However, the amount of quizzes and users will change; therefore, I want to take my realtime database that looks like this:
and make it so that for every quiz a quiz branch is created and for every student a students branch is created underneath that quiz branch as shown below:
Even though my experience with both firebase and Xcode is limited, I know how to read from the database and set a branch to a specific value. Thus the core of the question is how to make new branches or append old branches of a firebase realtime database from Xcode
Xcode ViewController segment below:
import UIKit
import Firebase
class ViewController: UIViewController {
#IBOutlet weak var greetingLbl: UILabel!
#IBOutlet weak var textField: UITextField!
var students = [Student]()
var quizzes = [Quiz]()
var squizzes = [SpecQuiz]()
var passsedQuizzes = [SpecQuiz]()
var loggedStud = Student(nme: "", id: "")
var count = 0
//variables that are not used are used in different functions not shown for readability
var ref = Database.database().reference()
var refStudents: DatabaseReference!
var refQuizzes: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
refStudents = Database.database().reference().child("Students")
refStudents.observe(DataEventType.value, with: {(snapshot) in
if snapshot.childrenCount > 0 {
self.students.removeAll()
for student in snapshot.children.allObjects as![DataSnapshot]{
let studentObject = student.value as? [String: AnyObject]
let studentName = studentObject?["Name"]
let studentID = studentObject?["ID"]
let stud = Student(nme: (studentName as! String?)!, id: (studentID as! String?)!)
//Student is a class that has a name and id attribute
self.students.append(stud)
}
}
})
refQuizzes = Database.database().reference().child("Quizzes")
refQuizzes.observe(DataEventType.value, with: {(snapshot) in
if snapshot.childrenCount > 0 {
self.quizzes.removeAll()
for quiz in snapshot.children.allObjects as![DataSnapshot]{
let QuizObject = quiz.value as? [String: AnyObject]
let QuizName = QuizObject?["Name"]
let QuizAvailable = QuizObject?["Available"]
let quiz = Quiz(nme: (QuizName as! String?)!, id: (QuizAvailable as! Bool?)!)
//Quiz is a class that has a name and available attribute
self.quizzes.append(quiz)
}
}
})
for quiz in quizzes{
//make new quiz branch
for student in students{
//make new student branch
}
}
// Do any additional setup after loading the view, typically from a nib.
}
You need to make a mutable copy of the data, append your new data, then push those changes to Firebase.
Obj-C example:
[[[[[FIRDatabase database] reference] child:#"Some_Root"] child:#"Some_Child"] setValue:SOME_DICTIONARY];

How do I convert string type data to integer type data from firebase database in ios & swift language

Now I'm struggling with convert string type data into integer type data and calculate in swift language. Now it is possible to bring the data from the firebase Realtime database but when I get off from "code a" part then the value is gone. And another problem is to bring two string type data from firebase Realtime database and convert them into integer to use minus calculation. And the final goal is to show the result of the calculation. I already try to fix the error by double click the error message. But I can't solve the problem. Please give me the answer to solve the problem.
import UIKit
import Firebase
class MainViewController:UIViewController{
var dbRef : DatabaseReference?
var databaseHandle:DatabaseHandle?
override func viewDidLoad(){
super.viewDidLoad()
// Do any additional setup after loading the view.
dbRef = Database.database().reference()
var getData = [String]()
var data = [String]()
var calDate = 0
// code a -------
databaseHandle = dbRef?.child("bluetooth_No").observe(.childAdded,with:
{ (snapshot) in //code
let now = snapshot.value as? String
if let actualData = now {
getData.append(actualData)
}
})
// code a------
calData = Int(getData[2]) - Int(getData[3]) //This line is the error
}
In your code calculation done before data received from firebase, because firebase observers are async.
so do calculation after you get data from firebase.
please check below code, may it help you.
databaseHandle = dbRef?.child("bluetooth_No").observe(.childAdded,with:
{ (snapshot) in //code
let now = snapshot.value as? String
if let actualData = now {
getData.append(actualData)
}
if getData.count > 3{
calData = Int(getData[2])! - Int(getData[3])!
}
})

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.

Resources