How to retrieve data held in an array from Firestore using Swift
Rather than storing each property in a separate array, you may want to consider representing it with a struct
. Something like:
struct Item {
var shoutText: String?
var postedBy: String?
var date: String?
var pollQuestions : [String]
}
Then, on your view controller, declare a property:
var items: [Item] = []
Then, in your snapshot listener, you can populate that array:
func getData() {
Firestore.firestore().collection("Posts").whereField("postedTo", arrayContains: userId).order(by: "date", descending: true).addSnapshotListener { (snapshot, error) in
if error != nil {
print(error?.localizedDescription)
} else {
if let snapshot = snapshot, !snapshot.isEmpty {
print("Posted data got")
self.items = snapshot.documents.map { document in
Item(shoutText: document.get("shout") as? String,
postedBy: document.get("postedBy") as? String,
date: document.get("date") as? String,
pollQuestions: document.get("pollQuestions") as? [String] ?? [])
}
self.receivedCollectionView.reloadData()
} else {
print("GET POSTED MESSAGES no data")
}
}
}
}
Later, you can access this data:
self.items[itemIndex].pollQuestions[pollQuestionIndex]
getting data out of a closure that retrieves data from firebase
That's because when you fetch data from Firebase the call is Asynchronous. What you can do:
Option 1 - Set your logic inside the closure (Like what you have that print the var inside the closure).
Option 2 - Define your own closure that going to receive your data like:
func myMethod(success:([String])->Void){
ref?.observeEventType(.Value, withBlock: { snapshot in
var newNames: [String] = []
for item in snapshot.children {
if let item = item as? FIRDataSnapshot {
let postDict = item.value as! [String: String]
newNames.append(postDict["name"]!)
}
}
success(newNames)
})
}
Option 3 - Use the delegate pattern
protocol MyDelegate{
func didFetchData(data:[String])
}
class MyController : UIViewController, MyDelegate{
func myMethod(success:([String])->Void){
ref?.observeEventType(.Value, withBlock: { snapshot in
var newNames: [String] = []
for item in snapshot.children {
if let item = item as? FIRDataSnapshot {
let postDict = item.value as! [String: String]
newNames.append(postDict["name"]!)
}
}
self.didFetchData(newNames)
})
}
func didFetchData(data:[String]){
//Do what you want
}
}
Get data from Firestore and properly append to array
let shelter = Shelter(id: Int(value.id), title: value.title, image: value.image, availableSpaces: value.available, distance: value.distance, gender: value.gender)
Here value
is of type [String:Any]
. So you cant do value.title
. You need to do value["title"] as? String ?? ""
and Similarly for id,image,distance,etc.
So the final code becomes:
let shelter = Shelter(id: Int(value["id"], title: value["title"], image: value["image"], availableSpaces: value["available"], distance: value["distance"], gender: value["gender"])
Downcast it accordingly.
UPDATE
replace your code with this
if let shelter = Shelter(id: value["id"] as? Int ?? -1, title: value["title"] as? String ?? "", image: value["image"] as? String ?? "", available: value["available"] as? Int ?? -1, distance: value["distance"] as? Double ?? -1, gender: value["gender"] as? String ?? "") {
self.shelters.append(shelter)
} else {
print("provided data is wrong.")
}
Firestore data is not displayed in listview. Identifiable not set up correctly?
Well after a good night of sleep I was cured of my temporary blindness and found that I was missing the @ObservedObject property in my view.
struct FIRBauthView: View {
@ObservedObject var firebaseCtrl: firebaseController = firebaseController()
instead of
struct FIRBauthView: View {
let firebaseCtrl: firebaseController = firebaseController()
Now the data is showing up as intended.
Firebase getDocument (querySnapshot) is not working
I think you may want to investigate a completion handler. The idea is to call an asynchronous function and then perform a task when that function has completed. The below code can almost be directly applied as a solution to your question.
So here's an example you can start with. Suppose we have an app with users stored in a users collection
users
uid_0
name: "Steve"
uid_1
name: "Danno"
uid_2
name: "Junior"
uid_3
name: "Tani"
and a new user wants to sign up, but the app doesn't allow for duplicate names. So we need to query our users collection to determine if the name is already in use. Here's the code that calls the query function.
self.doesUserNameExist(userName: "Steve" ) { doesExist in
if doesExist == true {
print("yep, it exists")
} else {
print("Nope - it's not there")
}
}
print("this code will execute before the above code")
That will print yep, it exists
and here's the actual function that performs the query. Note how we're using an completion handler thats @escaping the closure when it's done.
//determine if a user name exists in Firestore, return true if it does, false if not
func doesUserNameExist(userName: String, completion: @escaping( (Bool) -> Void) ) {
let usersCollection = self.db.collection("users")
usersCollection.whereField("name", isEqualTo: userName).getDocuments(completion: { querySnapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
guard let docs = querySnapshot?.documents else { return }
if docs.count > 0 {
completion(true)
} else {
completion(false)
}
})
}
This is the typical way to 'return' data from an asynchronous function as it allows the function to execute, the data to be valid and then pass that data back to the calling function in an asynchronous way.
Note that any code following the calling function, like this
print("this code will execute before the above code")
Will execute before the code in the function closure.
EDIT
This above called is called like this
self.doesUserNameExist(userName: "Jay", completion: { doesItExist in
print(doesItExist)
})
Related Topics
How to Run Timer Though the App Entered Background or Is Terminated
How to Change the Image Displayed in a Uiimageview Programmatically
How to Change "Initwithnibname" in Storyboard
Presentviewcontroller and Displaying Navigation Bar
Uiscrollview's Origin Changes After Popping Back to the Uiviewcontroller
Uiscrollview Adjusts Contentoffset When Contentsize Changes
What Does Warning "Mapping Architecture Arm64 to X86_64" Mean
What Dpi Resolution Is Used for an iPhone App
Facebook Logout Is Not Working in New Sdk V.4.1.0 in iOS
Uicollectionview Cell Spacing Based on Device Screen Size
Uitableviewcell Separator Disappearing in iOS7
iOS App Submission and Beta Review Process
Refresh Certain Row of Uitableview Based on Int in Swift
Improper Advertising Identifier [Idfa] Usage
How to Convert .Dae to .Scn Files in Scenekit
How to Update Uibutton Title/Text Programmatically
How to Import Private Framework Headers in a Swift Framework