How to get firebaseData into class/Struct and perform query operation - swift

Firebase Realtime db Json
In my code:
contain("University_1") is any way to get character search so i can get university_1 data only
I need class/Struct to store all [data] down below
Code:
let ref = Database.database().reference()
ref.child("chat").child("Student_2-University_1").observeSingleEvent(of: .value, with: { (snapshot) in
for child in (snapshot.children.allObjects as! [DataSnapshot]) {
if (child.key.contains("user2")){
print(child.value(forKey: "university_id"))
// print(child)
}
}
})
my struct look like
struct ChatModel {
let lastMessage : LastMesage
let user1 : StudentData
let user2 : UniversityData
struct LastMesage{
let lastMsg : String
let sendBy: String
let time: Int
}
struct StudentData {
let student_id, student_name: String
}
struct UniversityData {
let university_id, university_name: String
}
}

Related

Need acces from document to collection Firestore

I'm trying to do an iOS app and i've binded it with firebase, so I'm trying to get some posts ad fetch them, and this works fine, however this posts got 2 collections (likes and replies) and i'm trying to fetch likes, the thing is that I can't get the likes because for some reasons I can't a class for document forEach neither I can't access it, someone got any idea?
Code:
import Foundation
import Firebase
struct Post : Hashable {
var id : String
var dateAdded : String
var posterEmail : String
var posterUsername : String
var posterIcon : String
var postTitle : String
var postBody : String
var likes : [String]
var userLikedPost : Bool
}
struct Like {
var likeId : String
var likerEmail : String
}
class Likes {
var likes : [Like] = []
func fetchLikes() {
//Firestore.firestore()
}
}
class Posts : ObservableObject {
#Published var posts : [Post] = []
func fetchPosts() {
Firestore.firestore().collection("posts").getDocuments(completion: { (docPosts, error) in
if (error != nil) {
print("error fetching posts")
} else {
docPosts?.documents.forEach { (post) in
let id = post.documentID
let email = post.get("posterEmail") as! String
let username = post.get("posterUsername") as! String
let icon = post.get("posterIcon") as! String
let title = post.get("title") as! String
let body = post.get("body") as! String
// Here i want to insert the code that gets the likes class and access the likes variable
self.posts.append(Post(id: id, dateAdded:String(id.split(separator: "_").joined(separator: "/").prefix(10)) ,posterEmail: email, posterUsername: username, posterIcon: icon, postTitle: title, postBody: body,
likes: [],userLikedPost: false))
}
}
})
}
}
The Firestore structure was not included in the question so I will present one for use
user_wines
uid_0
name: "Jay"
favorite_wines:
0: "Insignia"
1: "Scavino Bricco Barolo"
2: "Lynch Bages"
uid_1
name: "Cindy"
favorite_wines
0: "Palermo"
1: "Mercury Head"
2: "Scarecrow"
And then the code to read all of the user documents, get the name, the wine list (as an array as Strings) and output it to console
func readArrayOfStrings() {
let usersCollection = self.db.collection("user_wines")
usersCollection.getDocuments(completion: { snapshot, error in
guard let allDocs = snapshot?.documents else { return }
for doc in allDocs {
let name = doc.get("name") as? String ?? "No Name"
let wines = doc.get("favorite_wines") as? [String] ?? []
wines.forEach { print(" ", $0) }
}
})
}
and the output
Jay
Insignia
Scavino Bricco Barolo
Lynch Bages
Cindy
Palermo
Mercury Head
Scarecrow
EDIT
Here's the same code using Codable
class UserWineClass: Codable {
#DocumentID var id: String?
var name: String
var favorite_wines: [String]
}
and the code to read data into the class
for doc in allDocs {
do {
let userWine = try doc.data(as: UserWineClass.self)
print(userWine.name)
userWine.favorite_wines.forEach { print(" ", $0) }
} catch {
print(error)
}
}

Getting an array of objects using FirebaseFirestoreSwift

I have a struct that looks like this:
struct Joint: Identifiable, Codable {
#DocumentID var id : String? = UUID().uuidString
var serviceType : Int
var businessName : String
var keywords: [String]
var menu : [DishItems]
enum CodingKeys : String, CodingKey {
case businessName
case serviceType = "jointType"
case keywords
case menu
}
}
struct DishItems: Codable {
var listOrder : Int
var menuItemName : String
var menuItemDescription : String
var menuItemPrice : Double
}
The DishItems is a custom object and I am using FirebaseFireStoreSwift JSON decoder to retrieve the documents.
let db = Firestore.firestore().collection("Joints")
db.getDocuments { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No restaurants")
return
}
let jointsArray : [Joint] = documents.compactMap {
return try? $0.data(as: Joint.self)
}
completion(jointsArray)
}
I am able to query all of the documents if I exclude "menu" from the parent struct but as soon as I include it, then nothing is queried. How do I go about obtaining the array of objects inside the JSON?

Why converting a Firestore querySnapshot into custom objects with compactMap returns empty although the querySnapshot contains documents?

Screenshot of a Firestore Document
I am using Swift, Xcode and a Firestore database.
I created a TableView and a Custom Object Class (MediumSample) with a dictionary and want to load my Firestore documents and show them in the TableView.
The documents (example in the screenshot) are loaded from Firestore correctly but the conversion into the object did not work. The list of objects returned from compactMap is always empty.
Here is my code snippets. It would be great, if someone has a hint on what is going wrong.
Custom Object Class (simplified):
import Foundation
import FirebaseFirestore
protocol MediumSampleDocumentSerializable {
init?(dictionary: [String:Any])
}
struct MediumSample {
var id: String
var field_t: String
var field_i: Int64
var field_b1: Bool
var field_b2: Bool
var field_g: FirebaseFirestore.GeoPoint
var field_d: Date
var field_a: [String]
var usecase: String
var dictionary: [String:Any] {
return [
"id": id,
"field_t": field_t,
"field_i": field_i,
"field_b1": field_b1,
"field_b2": field_b2,
"field_g": field_g,
"field_d": field_d,
"field_a": field_a,
"usecase": usecase
]
}
}
extension MediumSample: MediumSampleDocumentSerializable {
init?(dictionary: [String:Any]) {
guard let id = dictionary ["id"] as? String,
let field_t = dictionary ["field_t"] as? String,
let field_i = dictionary ["field_i"] as? Int64,
let field_b1 = dictionary ["field_b1"] as? Bool,
let field_b2 = dictionary ["field_b2"] as? Bool,
let field_g = dictionary ["field_g"] as? FirebaseFirestore.GeoPoint,
let field_d = dictionary ["field_d"] as? Date,
let field_a = dictionary ["field_a"] as? [String],
let usecase = dictionary ["usecase"] as? String else {return nil}
self.init (id: id, field_t: field_t, field_i: field_i, field_b1: field_b1, field_b2: field_b2, field_g: field_g, field_d: field_d, field_a: field_a, usecase: usecase)
}
}
Declaration of the database and array and calling the loading function:
import UIKit
import FirebaseFirestore
class MediumTableViewController: UITableViewController {
//MARK: Properties
var db: Firestore!
var mediumsamples = [MediumSample]()
override func viewDidLoad() {
super.viewDidLoad()
db = Firestore.firestore()
loadMediumSamples()
}
Function for loading the Firestore documents to fill the Array:
private func loadMediumSamples() {
//run the Firestore query
db.collection(Constants.MEDIUMS).whereField("usecase", isEqualTo: Constants.USECASE)
.getDocuments() { querySnapshot, err in
if let err = err {
print("Error getting documents: \(err)")
} else {
//initialise an array of medium objects with Firestore data snapshots
self.mediumsamples = querySnapshot!.documents.compactMap({MediumSample(dictionary: $0.data())})
//fill the tableView
DispatchQueue.main.async {
self.tableView.reloadData()
print(self.mediumsamples)
}
print("Mediums List", self.mediumsamples) // This line returns: Mediums List []
print("Mediums List size", (self.mediumsamples.count)) // This line returns: Mediums List size 0
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())") // This line returns the snapshot documents correctly!
}
}
}
}
Here is how the screenshot object object is added:
func addMediumSamples() {
let currentDateTime = Date()
let location = FirebaseFirestore.GeoPoint(latitude: 0, longitude: 0)
let mediumsample = MediumSample(id: "an id", field_t: "field_t", field_i: 10, field_b1: true, field_b2: false, field_g: location, field_d: currentDateTime, field_a: [Constants.SAMPLE_DEV], usecase: Constants.SAMPLE_DEV)
var ref: DocumentReference? = nil
ref = self.db.collection(Constants.MEDIUMS).addDocument(data: mediumsample.dictionary) {
error in
if let error = error {
print("Error writing city to Firestore: \(error)")
} else {
print("Document added with id : \(ref!.documentID)")
}
}
}
The problem is in the MediumSample struct, in the field_d type (Date).
The type of that field in your Cloud Firestore database is Timestamp.
The field "field_d" in the MediumSample struct expects a value of type Date.
You can change the type to the FirebaseFirestore.Timestamp, or you can convert it to Date when mapping and before passing to the MediumSample.
eg. for converting Timestamp to Date in Swift
let date = timestamp.dateValue()

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 })

Decode Json Data using JsonDecoder and Alamofire

I am trying decode Json data into my Model.
This is my model
struct Devices : Codable {
var id :String?
var description :String?
var status : Int?
}
var heroes = Devices()
print(DeviceId)
let loginParam: [String: Any] = [
"id": DeviceId
]
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 5
manager.request("http://13.13.13.004/website/api/Customer/DeviceControl", method: .post , parameters: loginParam, encoding: JSONEncoding.prettyPrinted)
.responseData { response in
let json = response.data
do{
let decoder = JSONDecoder()
//using the array to put values
heroes = try decoder.decode(Devices.self, from: json!)
}catch let err{
print(err)
}
this code doesn't get in catch block.
But heroes values return nill.
When ı try use NsDictionary
Its give result.
This is a common mistake: You forget the root object
struct Root : Decodable {
private enum CodingKeys: String, CodingKey { case resultCount, devices = "results" }
let resultCount : Int
let devices : [Device]
}
And name the device struct in singular form (devices is an array of Device instances) and declare the members as non-optional
struct Device : Decodable {
var id : String
var description : String
var status : Int
}
...
var heroes = [Device]()
...
let result = try decoder.decode(Root.self, from: json!)
heroes = result.devices