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.
Related
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!
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];
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.
Having a real hard time figuring this out. So if anyone could help would appreciate it. I have some data I want to pull from google firebase and store them into a struct them populate a tableview. But I have having some issues with the table being called before the data is all pulled. Im not sure if I'm using the right database calls for firebase but this is what I have so far.
struct provStru {
var name = String()
var last = String()
}
var store = [provStru]()
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
var name = String()
var last = String()
var counter = 0
while counter<10 {
ref.child("Prov").child("\(counter + 1)").observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
if !snapshot.exists() {
print("doesnt exist")
} else {
let value = snapshot.value as? NSDictionary
name = value?["Name"] as! String
last = value?["Last"] as! String
self.store.append(provStru(name: name, last: last)
}
}) { (error) in
print(error.localizedDescription)
}
counter += 1
}
Then I would like to use that data from the struct to populate the tableview cells. But when I run this code the counter is like an infinite loop. Ive been trying to figure this out for a while. Thanks
This is how the data looks in firebase
This is what the database looks like then the next value would be 2 then 3 going down
I want to make a user query, removing the name of each, and go keeping the names in an array, then display them on a table. The problem is I can not keep them in the settlement, how can I fix it? Thus I make the query to extract the names of users:
var users = [String]()
let ref = Firebase(url:"https:mydatabase/users")
ref.queryOrderedByChild("name").observeEventType(.ChildAdded,
withBlock: { snapshot in
if let username = snapshot.value["name"] as! String {
self.users.append(username)
print("username")
}
})
So I have my users table in firebase
The username var does have the name, but when adding the content of the var the settlement, does not, at the time of execution of the application does not throw any errors.
There are just a few typo's your code
Try this:
let usersRef = self.myRootRef.childByAppendingPath("users")
usersRef.queryOrderedByChild("name").observeEventType(.ChildAdded, withBlock: { snapshot in
if let username = snapshot.value["name"] as? String {
self.userArray.append(username)
print("\(username)")
}
})
Be sure to define the userArray as a property of the class so it will be available to other functions.
class MyClass: NSObject {
let myRootRef = Firebase(url:"https://your-app.firebaseio.com")
var userArray = [String]()