Unable to retrieve Firestore Documents using Collection - swift

Are you please able to look at the following and help me understand why my documents fail to retrieve from Firestore? My code is below:
class ContactStore : ObservableObject {
#Published var datas = [contactDataType]()
private var db = Firestore.firestore()
func fetchData() {
db.collection("Contact Details").addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No documents")
return
}
self.datas = documents.compactMap { queryDocumentSnapshot -> contactDataType? in
return try? queryDocumentSnapshot.data(as: contactDataType.self)
}
}
}
}
My struct is as follows
struct contactDataType : Identifiable, Codable {
#DocumentID var id: String? = UUID().uuidString
var adno : String
var fullname : String
var firstname : String
var lastname : String
var registrationgroup : String
var priority1relation : String
var priority1fullname : String
var priority1maintelephone : String
var priority1mobile : String
var priority2relation : String
var priority2fullname : String
var priority2maintelephone : String
var priority2mobile : String
var priority3relation : String
var priority3fullname : String
var priority3maintelephone : String
var priority3mobile : String
var priority4relation : String
var priority4fullname : String
var priority4maintelephone : String
var priority4mobile : String
I am able to retrieve the documents using the following code,
class ContactStore : ObservableObject{
#Published var datas = [contactDataType]()
#AppStorage("selectedSchool") var selectedSchool: String = "selectedSchool"
init() {
let db = Firestore.firestore()
db.collection("School Name/\(selectedSchool/School Assets/\(**Struggling with this bit**)/Contact Details").getDocuments { (snap, err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
for i in snap!.documents{
let id = i.documentID
let adno = i.get("ID") as? String ?? ""
let fullname = i.get("Full Name") as? String ?? ""
let firstname = i.get("First Name") as? String ?? ""
let lastname = i.get("Last Name") as? String ?? ""
let registrationgroup = i.get("Registration Group") as? String ?? ""
let priority1relation = i.get("Priority 1 Relation") as? String ?? ""
let priority1fullname = i.get("Priority 1 Full Name") as? String ?? ""
let priority1maintelephone = i.get("Priority 1 Main Telephone") as? String ?? ""
let priority1mobile = i.get("Priority 1 Mobile") as? String ?? ""
let priority2relation = i.get("Priority 2 Relation") as? String ?? ""
let priority2fullname = i.get("Priority 2 Full Name") as? String ?? ""
let priority2maintelephone = i.get("Priority 2 Main Telephone") as? String ?? ""
let priority2mobile = i.get("Priority 2 Mobile") as? String ?? ""
let priority3relation = i.get("Priority 3 Relation") as? String ?? ""
let priority3fullname = i.get("Priority 3 Full Name") as? String ?? ""
let priority3maintelephone = i.get("Priority 3 Main Telephone") as? String ?? ""
let priority3mobile = i.get("Priority 3 Mobile") as? String ?? ""
let priority4relation = i.get("Priority 4 Relation") as? String ?? ""
let priority4fullname = i.get("Priority 4 Full Name") as? String ?? ""
let priority4maintelephone = i.get("Priority 4 Main Telephone") as? String ?? ""
let priority4mobile = i.get("Priority 4 Mobile") as? String ?? ""
self.datas.append(contactDataType(id: id, adno: adno, fullname: fullname, firstname: firstname, lastname: lastname, registrationgroup: registrationgroup, priority1relation: priority1relation, priority1fullname: priority1fullname, priority1maintelephone: priority1maintelephone, priority1mobile: priority1mobile, priority2relation: priority2relation, priority2fullname: priority2fullname, priority2maintelephone: priority2maintelephone, priority2mobile: priority2mobile, priority3relation: priority3relation, priority3fullname: priority3fullname, priority3maintelephone: priority3maintelephone, priority3mobile: priority3mobile, priority4relation: priority4relation, priority4fullname: priority4fullname, priority4maintelephone: priority4maintelephone, priority4mobile: priority4mobile))
}
}
}
}
**So overall, I'm struggling to either pass the document ID into the path in the second code or retreive my documents using the first. I know it would be better using the first, but I would really like to pass the document ID's into the document path rather than create collection group queries as it doesn't seem to match with would I would like to do.
I understand the naming structure isn't quite right and should avoid spaces**
At this moment in time I have set my rules to read for all so I know this is not a security issue.
Questions -
Am I able to pass the Document ID into a path? I'm not sure and I can't find any documentation on this although I have managed this through AppStorage for Selected School
Is Collection Group Queries the better way to go for this?
Thank you for any and all advice.

If you can't build the full path to the subcollection to query, including all of the nested document IDs, then you won't be able to query it directly. A full path is required - there are no wildcards.
If know the name of the subcollection, you can do a collection group query, and that will query all subcollections with that exact same name. If you don't want all of the documents among all of those subcollections, you can filter the query to find what you're looking for.

Related

Is there a way to map a sub-collection into a struct while querying the main collection? (Firestore)

Is there a way to map a sub-collection into a struct while querying the main collection?
My sub-collection structure is:
-conversations (collection)
--- "users" : [array of two users]
--- messages (subcollection)
------- "created by": "sender"
------- "date" : timestamp
------- "msg" : "help me guys I'm stuck with this damn chat feature"
I read somewhere the query are shallow meaning you'll only get the fields of the collecting you're querying, but maybe there is another way besides nested query loops after querying the first collection
func getFilteredConversations(query: String) {
if (user != nil) {
db.collection("conversations").whereField("users", arrayContains: user!.displayName)
.order(by: "createdTime")
.addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("no conversations found")
return
}
//mapping
self.chats = documents.map {(queryDocumentSnapshot) -> Conversation in
let data = queryDocumentSnapshot.data()
let docId = queryDocumentSnapshot.documentID
let users = data["users"] as? [String] ?? [""]
let unreadmsg = data["hasUnreadMessage"] as? Bool ?? false
//MARK: - GET MESSAGES
self.db.collection("conversations").document(docId).collection("messages")
.order(by: "date")
.addSnapshotListener{ (querySnapshot, err) in
guard let documents = querySnapshot?.documents else {
print("no messages found")
return
}
var mensajes = [Message]()
mensajes = documents.map {(queryDocumentSnapshot) -> Message in
let data = queryDocumentSnapshot.data()
let docId = queryDocumentSnapshot.documentID
let createdby = data["created_by"] as? String ?? ""
let msg = data["msg"] as? String ?? ""
let date = data["date"] as? Timestamp ?? Timestamp()
return Message(createdBy: createdby, msg: msg, date: date, id: docId)
}
}
print("Users: \(users)")
return Conversation(id: docId, users: users, messages: mensajes, hasUnreadMessage: unreadmsg)
}
}
}
}
this is the model
struct Conversation: Decodable, Identifiable {
//var id: UUID { person.id }
#DocumentID var id: String? = UUID().uuidString
var users: [String] = [""]
var messages: [Message] = []
var hasUnreadMessage : Bool = false
}
struct Message: Decodable {
var createdBy: String?
var msg: String?
#ServerTimestamp var date : Timestamp?
var id : String?
}
You've already found the answer yourself it seems: Firestore queries are shallow. There is no way to read from the subcollection while reading the parent document. The only way to query across collections is with a collection group query, which doesn't seem to apply here.
Consider if it's worth duplicating the most recent messages from the conversation in the parent document, either through the client-side code or in a Cloud Function. That way you can reduce the number of documents you have to read to get the initial messages to display to the user.

How to query multiple fields with one value in Firebase?

I'm a newbie at firebase I have implemented a sample app that able to transfer point to each other after transfer success I also added two fields called "sender_name" and "receiver_name" but it's too difficult to get all transitions based on user login I found sample ways to do just add multiple where to it, its work fine if true both but that's not what I want I want whereOr like SQL as an example below
SELECT column1, column2, ...
FROM table_name
WHERE condition1 OR condition2 OR condition3 ...;
any solution help, please
func getUserTransition(){
// process
/*
1.get all transition from tm_members sender and receiver by current user login
2.
*/
guard let username = self.userSession?.username else {
return
}
print("username in user session : \(username)")
COLLECTION_TM_TRANSITIONS_UAT
.whereField("sender_name", isEqualTo: username)
.whereField("receiver_name", isEqualTo: username)
.getDocuments { documentSnapshot, error in
if error == nil {
guard let value = documentSnapshot?.documents else { return }
self.tmTransitions = value.map { (queryDocumentSnapshot) -> TmTransition in
let data = queryDocumentSnapshot.data()
let email = data["email"] as? String ?? ""
let is_sender = data["is_sender"] as? Bool ?? false
let point = data["point"] as? Int ?? 0
let username = data["username"] as? String ?? ""
let sender_id = data["sender_id"] as? String ?? ""
let receiver_id = data["receiver_id"] as? String ?? ""
let created_at = data["created_at"] as? Timestamp
let sender_name = data["sender_name"] as? String ?? ""
let receiver_name = data["receiver_name"] as? String ?? ""
print("username : \(email)")
return TmTransition(id: queryDocumentSnapshot.documentID, sender_id: sender_id, receiver_id: receiver_id, username: username, is_sender: is_sender, point: point, email: email,created_at: created_at,sender_name: sender_name,receiver_name: receiver_name)
}
}
else{
print("error during fetch data ")
}
}
}

How to convert Array of Struct to List Realm?

i want to convert Array from struct to List Realm .
static func mapGenreResponsetoGenreEntity( input genre: [GenreModel]) -> List {
var list = List<GenreEntity>()
return genre.map { result in
let newGenre = GenreEntity()
newGenre.gamesCount = result.gamesCount ?? 0
newGenre.id = result.id ?? 0
newGenre.imageBackground = result.imageBackground ?? "Unknown"
newGenre.name = result.name ?? "Unknown"
newGenre.slug = result.slug ?? "Unknown"
list.append(newGenre)
return list
}
}
and the genre is
struct GenreModel: Codable {
let gamesCount : Int?
let id : Int?
let imageBackground : String?
let name : String?
let slug : String?
}
How can i convert from array genre (Struct) to List realm which is GenreEntity ?
This should just be a matter of adding the new GenreEntity objects to an array and then return the entire array once done.
This should do it
func convertToList(genreArray: [GenreClass]) -> List<GenreEntityRealmModel> {
let genreEntityList = List<GenreEntityRealmModel>()
genreArray.forEach { model in
let genreEntity = GenreEntity()
genreEntity.gamesCount = model.gamesCount
genreEntityList.append(genreEntity)
}
return genreEntityList
}

Randomly generated Firebase child key in document path SwiftUI

I've had a look around and although there is some answers which are similar I can't find what I'm specifically looking for.
My data is stored in Firebase (Cloud Firestore) and I need to read that data back which is fine and working. My issue is I have changed the setup of the document file path to further increase security by adding a randomly generated id in the file path.
Firstly, is this possible to add the wildcard into a document path within Xcode? Secondly, could this be perhaps done as a string?
I have added the code below including file path.
Thanks in advance.
class getContactData : ObservableObject{
#Published var datas = [contactDataType]()
#AppStorage("selectedSchool") var selectedSchool: String = "selectedSchool"
init() {
let db = Firestore.firestore()
db.collection("/School Name/\(selectedSchool)/School Assets/**randomPath**/Contact Details").getDocuments { (snap, err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
for i in snap!.documents{
let id = i.documentID
let adno = i.get("ID") as! String
let fullname = i.get("Full Name") as! String
let firstname = i.get("First Name") as! String
let lastname = i.get("Last Name") as! String
let registrationgroup = i.get("Registration Group") as! String
let priority1relation = i.get("Priority 1 Relation") as! String
let priority1fullname = i.get("Priority 1 Full Name") as! String
let priority1maintelephone = i.get("Priority 1 Main Telephone") as! String
let priority1mobile = i.get("Priority 1 Mobile") as! String
let priority2relation = i.get("Priority 2 Relation") as! String
let priority2fullname = i.get("Priority 2 Full Name") as! String
let priority2maintelephone = i.get("Priority 2 Main Telephone") as! String
let priority2mobile = i.get("Priority 2 Mobile") as! String
let priority3relation = i.get("Priority 3 Relation") as! String
let priority3fullname = i.get("Priority 3 Full Name") as! String
let priority3maintelephone = i.get("Priority 3 Main Telephone") as! String
let priority3mobile = i.get("Priority 3 Mobile") as! String
let priority4relation = i.get("Priority 4 Relation") as! String
let priority4fullname = i.get("Priority 4 Full Name") as! String
let priority4maintelephone = i.get("Priority 4 Main Telephone") as! String
let priority4mobile = i.get("Priority 4 Mobile") as! String
self.datas.append(contactDataType(id: id, adno: adno, fullname: fullname, firstname: firstname, lastname: lastname, registrationgroup: registrationgroup, priority1relation: priority1relation, priority1fullname: priority1fullname, priority1maintelephone: priority1maintelephone, priority1mobile: priority1mobile, priority2relation: priority2relation, priority2fullname: priority2fullname, priority2maintelephone: priority2maintelephone, priority2mobile: priority2mobile, priority3relation: priority3relation, priority3fullname: priority3fullname, priority3maintelephone: priority3maintelephone, priority3mobile: priority3mobile, priority4relation: priority4relation, priority4fullname: priority4fullname, priority4maintelephone: priority4maintelephone, priority4mobile: priority4mobile))
}
}
}
}
struct contactDataType : Identifiable {
var id : String
var adno : String
var fullname : String
var firstname : String
var lastname : String
var registrationgroup : String
var priority1relation : String
var priority1fullname : String
var priority1maintelephone : String
var priority1mobile : String
var priority2relation : String
var priority2fullname : String
var priority2maintelephone : String
var priority2mobile : String
var priority3relation : String
var priority3fullname : String
var priority3maintelephone : String
var priority3mobile : String
var priority4relation : String
var priority4fullname : String
var priority4maintelephone : String
var priority4mobile : String
In Firebase, if you add a new .document() without specifying the name of the document, it will create a document with a random generated ID. This is not a wildcard, but it will be a random path.
let firstCollection = "School Name"
let selectedSchool = "Selected School"
let secondCollection = "School Assets"
let thirdCollection = "Contact Details"
// Adding a document:
let newDocument = db.collection(firstCollection).document(selectedSchool).collection(secondCollection).document()
let randomPath = newDocument.documentID // You will need to store this if you need to access it directly later
let finalPath = newDocument.collection(thirdCollection)
finalPath.addDocument(data: ["info" : "info"], completion: nil)
Side note: it's best practice to keep the names of your collections and documents in the database lowercased and without spaces to avoid typing String errors ("School Name" -> "school_name").
Firestore does not support wildcards in queries.
If you want to query all documents among all subcollections named "Contact Details" anywhere in the database, then you can use a collection group query.
db.collectionGroup("Contact Details").getDocuments { (snap, err) in
// ...
}

How do I remove "Optional()" from object in an array

So im using CloudKit and fetching all the data into an array as [StartDay], my StartDay class looks like this:
import UIKit
import CloudKit
class StartDay {
var recordID: CKRecord.ID!
var wakeUp: String!
var sleptWell: String!
var dNN: String!
var created: String! {
get {
return created
}
}
}`
My function loads get an arraylist, which contains information received from the database. In my database it stands like this: "22.01.09:
func checkIfButtonShouldBeEnabled(startDayList: [StartDay]){
let startDayDates = startDayList.map{$0.created}
for i in 0..<startDayDates.count {
print(startDayDates)
}
}`
OUTPUT:
Optional("22.01.2019")
Optional("22.01.2019")
I want to remove "Optional()", so it only says "22.01.2019", how can I do so?
UPDATE: FETCH FUNC
func loadStartDay() -> [StartDay]{
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "StartDay", predicate: predicate)
let operation = CKQueryOperation(query: query)
var startDays: [StartDay] = []
operation.desiredKeys = ["wakeUp", "wellSlept", "dNN", "recordID", "createdDato"]
operation.recordFetchedBlock = { (record:CKRecord) in
let newStartDay = StartDay()
newStartDay.wakeUp = record.object(forKey: "wakeUP") as? String
newStartDay.sleptWell = record.object(forKey: "sleptWell") as? String
newStartDay.dNN = record.object(forKey: "dNN") as? String
newStartDay.recordID = record.object(forKey: "recordID") as? CKRecord.ID
newStartDay.created = record.object(forKey: "createdDato") as? String
print(newStartDay.created)
startDays.append(newStartDay)
}
You can use print(startDayDates!) or print(startDayDates ?? "default value").
But I recommend usage of startDayList.compactMap() instead of startDayList.map()to ensure your array doesn't contain nil values.
You can also do like this:
startDayList
.compactMap { $0.created }
.forEach { print($0) }
As you designed the database model you exactly know which record attributes always exist. Declaring class properties as implicit unwrapped optional as an alibi not to write an initializer is very bad practice.
Assuming every attribute in a record does have a value declare the properties as non-optional and write an initializer.
At least created and recordID are supposed to have always a value!
import UIKit
import CloudKit
class StartDay {
var recordID: CKRecord.ID
var wakeUp: String
var sleptWell: String
var dNN: String
var created: String
init(record : CKRecord) {
// recordID can be retrieved directly
self.recordID = record.recordID
self.wakeUp = record.object(forKey: "wakeUP") as! String
self.sleptWell = record.object(forKey: "sleptWell") as! String
self.dNN = record.object(forKey: "dNN") as! String
self.created = record.object(forKey: "createdDato") as! String
}
}
and create instances with
operation.recordFetchedBlock = { record in
startDays.append(StartDay(record: record))
}
Now the Optional has gone.
print(startDayList.map{ $0.created })