Display Firestore data into UIPicker Swift 4 - swift

I am still new in swift development, my problem is, I have Firestore structure as below:
the problem is to display the list of title from firestore into a uipicker, I need to get data into an array like below:
[firsProgramme, secondProgramme, thirdProgramme]
I managed to display all the "title" from firestore in the string, not in the array
below is the code:
func getprogram() {
let db = Firestore.firestore()
db.collection("Programme").getDocuments()
{
(querySnapshot, err) in
if let err = err
{
print("Error getting documents: \(err)");
}
else
{
for document in querySnapshot!.documents {
let data = document.data()
let program = data["title"] as? String ?? ""
// let agencyId = document.documentID
print(program)
//print("\(document.documentID) => \(document.data())");
}
}
}
}
result print(program) return as below :
firstprogramme
secondprogramme
thirdprogramme
the other part for UIPicker is already being managed well.
Thanks in advance.

In you class create an array variable to hold information about your programs:
var programsArray: [String] = []
When reading data from Firebase, instead of print(program) use programsArray.append(program). Call UIPickerView reload function after you have all data.
DispatchQueue.main.async {
self.your_picker_view.reloadAllComponents()
}
And of course, use this array inside your pickerview datasource methods.

Related

Straightforward way to add timestamp to Firebase in Swift?

so I've been researching a lot and apparently no method is working to add timestamp to my firebase data and then sort the data accordingly. I tried the traditional "timestamp": [".sv":"timestamp"] method and that only adds a value of .sv: Timestamp in firebase. Then I tried self.createdAt = Date(timeIntervalSince1970: timestamp/1000) and added that to where I add the item to firebase but that didn't help either.So doesn't anyone know what's the most straightforward method to add timestamp to firebase via swift? Would appreciate any input! Thanks.
EDIT:
Here's now I define the item being added to firebase:
struct Note: Identifiable, Codable {
var id: String
var content: String
var createdAt: String
}
Here's how I define fetching that item from firebase:
func fetchNotes () {
notes.removeAll()
let uid = Auth.auth().currentUser!.uid
let db = Firestore.firestore()
let ref = db.collection("userslist").document(uid).collection("actualnotes")
ref.order(by: "createdAt", descending: true).addSnapshotListener{ (querySnapshot, error) in
let documents = querySnapshot!.documents
if querySnapshot!.isEmpty {
let ref = db.collection("userslist").document(uid).collection("actualnotes")
ref.document().setData(["content": "Welcome", "createdAt": FieldValue.serverTimestamp() ])
}
else {
self.notes = documents.map { (queryDocumentSnapshot) -> Note in
let data = queryDocumentSnapshot.data()
let id = queryDocumentSnapshot.documentID
let content = data["content"] as! String ?? ""
let createdAt = data["createdAt"] as? String ?? "" // this is where
// it breaks with the error (Thread 1: signal SIGABRT)
let note = Note(id:id, content:content, createdAt: createdAt)
return (note)
}
}
}
}
And here's where I want the timestamp to appear in another view:
List{ ForEach(dataManager.notes) { note in
NavigationLink(destination: NoteView(newNote: note.content, idd: note.id ))
{
HStack {
Text(note.createdAt)
}})}}
The [".sv":"timestamp"] applies to the Realtime Database only, while you are using Cloud Firestore. While both databases are part of Firebase, they are completely separate and the API of one does not apply to the other.
For Firestore follow its documentation on writing a server-side timestamp, which says it should be:
db.collection("objects").document("some-id").updateData([
"lastUpdated": FieldValue.serverTimestamp(),
])
If that doesn't work, edit your question to show the exact error you get.

why is only one field being displayed?

Trying to print out the names of all the current users of the application, they print in the console however when the app runs only one of the names is displayed.
func getData() {
let db = Firestore.firestore()
// Get data
db.collection("users").getDocuments()
{
(snapshot, err) in
if let err = err
{
print("Error getting documents: \(err)");
}
else
{
snapshot!.documents.forEach({ (document) in
let firstname = document.data()["firstname"]
print(firstname!)
self.studentlbl.text = (firstname! as! String)
}
)
How can this be fixed?
Thanks!
It looks like you only have one label view, so each time this line runs you overwrite the previous value of that label:
self.studentlbl.text = (firstname! as! String)
If you want to show all values in that single label, you can do:
self.studentlbl.text = self.studentlbl.text + (firstname! as! String)

How to merge two queries using Firestore - Swift

I need to merge two queries with firebase firestore and then order the results using the timestamp field of the documents.
Online I didn't find much information regarding Swift and Firestore.
This is what I did so far:
db.collection("Notes").whereField("fromUid", isEqualTo: currentUserUid as Any).whereField("toUid", isEqualTo: chatUserUid as Any).getDocuments { (snapshot, error) in
if let error = error {
print(error.localizedDescription)
return
}
db.collection("Notes").whereField("fromUid", isEqualTo: self.chatUserUid as Any).whereField("toUid", isEqualTo: self.currentUserUid as Any).getDocuments { (snaphot1, error1) in
if let err = error1{
print(err.localizedDescription)
return
}
}
}
I added the second query inside the first one on completion but now I don't know how to merge them and order them through the field of timestamp.
On this insightful question It is explained that it's recommended to use a Task object but I don't find anything similar with swift.
There are many ways to accomplish this; here's one option.
To provide an answer, we have to make a couple of additions; first, we need somewhere to store the data retrieved from firebase so here's a class to contains some chat information
class ChatClass {
var from = ""
var to = ""
var msg = ""
var timestamp = 0
convenience init(withDoc: DocumentSnapshot) {
self.init()
self.from = withDoc.get("from") as! String
self.to = withDoc.get("to") as! String
self.msg = withDoc.get("msg") as! String
self.timestamp = withDoc.get("timestamp") as! Int
}
}
then we need a class level array to store it so we can use it later - perhaps as a tableView dataSource
class ViewController: NSViewController {
var sortedChatArray = [ChatClass]()
The setup is we have two users, Jay and Cindy and we want to retrieve all of the chats between them and sort by timestamp (just an Int in this case).
Here's the code that reads in all of the chats from one user to another creates ChatClass objects and adds them to an array. When complete that array is passed back to the calling completion handler for further processing.
func chatQuery(from: String, to: String, completion: #escaping( [ChatClass] ) -> Void) {
let chatsColl = self.db.collection("chats") //self.db points to my Firestore
chatsColl.whereField("from", isEqualTo: from).whereField("to", isEqualTo: to).getDocuments(completion: { snapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
guard let docs = snapshot?.documents else { return }
var chatArray = [ChatClass]()
for doc in docs {
let chat = ChatClass(withDoc: doc)
chatArray.append(chat)
}
completion(chatArray)
})
}
Then the tricky bit. The code calls the above code which returns an array The above code is called again, returning another array. The arrays are combined, sorted and printed to console.
func buildChatArray() {
self.chatQuery(from: "Jay", to: "Cindy", completion: { jayCindyArray in
self.chatQuery(from: "Cindy", to: "Jay", completion: { cindyJayArray in
let unsortedArray = jayCindyArray + cindyJayArray
self.sortedChatArray = unsortedArray.sorted(by: { $0.timestamp < $1.timestamp })
for chat in self.sortedChatArray {
print(chat.timestamp, chat.from, chat.to, chat.msg)
}
})
})
}
and the output
ts: 2 from: Cindy to: Jay msg: Hey Jay, Sup.
ts: 3 from: Jay to: Cindy msg: Hi Cindy. Not much
ts: 9 from: Jay to: Cindy msg: Talk to you later

Get Data from firebase for swift

I have below code to get data from my firebase database
db.collection("users").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
print("starting name display")
for document in (querySnapshot?.documents)! {
let documentUserId = document.get("uid") as?String
let temp = document.data()["displayName"]
print(temp)
}
}
}
The print statement displays as optional("test name")
Why am i keep getting optional in my string. Same displays on the screen as well.
You need to un-wrap because it's an Optional. Means it could have a value or it could not have a value. So this is one method to handle it:
let temp = document.data()["displayName"] ?? ""
print(temp)
You could also use if let or guard let statements if you need to handle the cases where the value is actually empty.
Note: Take a look at the basics of swift. There is a separate section for Optionals.

How do I build a custom object that consists of 2 custom objects for an expanding cell?

I am following along this tutorial here for collapsing UITableViewCells and the mechanics are quite straight forward but I am not quite sure how to populate my model arrays from Firestore. He has manually created the data for demo purposes so naturally as a beginner, I am stumbling since instead of that I am making a network call to Firebase.
My data structure is simple. The base collection (which would populate the title of the cell) extracts data from here: db.collection("Insurance_Plans") and contains the following strings:
- Holder name
- Holder contact etc.
And each insurance holder has multiple properties insured and this is the sub-collection i.e. db.collection("Insurance_Plans").document(planId).("Insured_Property") and data model consists of strings such as:
- Property type
- Property address etc.
What I am doing is creating the main struct:
struct cellData {
var opened = Bool()
var plans = [Plan]()
var properties = [Properties]()
}
and in the class itself I declare an instance of it it as:
var tableViewData = [cellData]()
Then I query the insurance meta data (which has its own function) as follows:
db.collection("Insurance_Plans").getDocuments() {
documents, error in
guard let snapshot = documents else {
let error = error
print("Error fetching documents results: \(error!.localizedDescription)")
return
}
let results = snapshot.documents.map { (document) -> Plan in
if let plan = Plan(dictionary: document.data(), id: document.documentID) {
self.plansArray.append(plan)
self.loadPropertyData(planId: document.documentID) // another function where property details are queried
return plan
} else {
fatalError("Unable to initialize type \(Plan.self) with dictionary \(document.data())")
}
}
self.plansArray = results
self.plansDocuments = snapshot.documents
self.plansTableView.reloadData()
}
Then I query the properties in each plan as such:
db.collection("Insurance_Plans").document(planId).collection("Properties").getDocuments() { documents, error in
guard let snapshot = documents else {
let error = error
print("Error fetching documents results: \(error!.localizedDescription)")
return
}
let results = snapshot.documents.map { (document) -> Property in
if let property = Property(dictionary: document.data()) {
return property
} else {
fatalError("Unable to initialize type \(Property.self) with dictionary \(document.data())")
}
}
self.propertiesArray = results
self.propertiesDocuments = snapshot.documents
self.ordersTableView.reloadData()
}
My question then is how do I enter the insurance meta data and the subsequent properties data into a cellData object and where do I do this?