unable to send Firebase document ID in from VC to VC - swift

i'm running a query to firebase and i'm able to obtain the documentID but i'm not able to send the documentID over to another VC. I get everything int he query with no issues. I'm not sure what i'm missing. Any suggestions are greatly appreciated.
First VC
#IBAction func getDataTapped(_ sender: Any) {
SVProgressHUD.show()
if HOSP != (hospNameTxt.text!) {
ptListQuery = ptListCollectionRef?.whereField("hosp", isEqualTo: (hospNameTxt.text!))
}
ptListQuery?.getDocuments { (snapshot, error) in
if let err = error {
debugPrint("error getting data: \(err)")
} else {
guard let snap = snapshot else { return }
for document in snap.documents {
let data = document.data()
let ptName = data[PTNAME] as? String ?? ""
let assignedMd = data[ASSIGNEDMD] as? String ?? ""
let officeMd = data[OFFICEMD] as? String ?? ""
let assignedDate = data[ASSIGNEDDATE] as? String ?? ""
let seeNoSee = data[SEENOSEE] as? String ?? ""
let room = data[ROOM] as? String ?? ""
let app = data[APP] as? String ?? ""
let documentId = document.documentID
let username = data[USERNAME] as? String ?? ""
let userId = data[USER_ID] as? String ?? ""
let newPtList = PTList(ptName: ptName,
assignedMd: assignedMd,
officeMd: officeMd,
assignedDate: assignedDate,
seeNoSee: seeNoSee,
room: room,
app: app, documentId: documentId,
username: username, userId: userId
)
self.ptListInCell.append(newPtList)
print("docID", documentId) //this shows the documentID in the console
print(document.data()) //this shows everything in the array but the document ID
}
}
self.performSegue(withIdentifier: "goToResults", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToResults" {
let vc = segue.destination as! ResultsdataVC
vc.ptListFromCell = ptListInCell
SVProgressHUD.dismiss()
}
}
Second VC
class ResultsdataVC: UIViewController, UITableViewDataSource, UITableViewDelegate, PatListCellDelegate {
#IBOutlet weak var resultsTableView: UITableView!
#IBOutlet weak var patientFilter: UISegmentedControl!
var ptListFromCell = [PTList]()
var ptDatasToPass = [PTData]()
var selectedFilter = FilterCategory.hosp.rawValue
var patientListener: ListenerRegistration!
var patientCollectionRef: CollectionReference!
//segmentedControl
var dataFilter = 0
var tableDataSee : [String] = ["Yes"]
var tableDataNoSee : [String] = ["No"]
override func viewDidLoad() {
super.viewDidLoad()
resultsTableView.delegate = self
resultsTableView.dataSource = self
resultsTableView.rowHeight = 110
}
here is PTList model:
class PTList {
private(set) var ptName: String!
private(set) var assignedMd: String!
private(set) var officeMd: String!
private(set) var assignedDate: String!
private(set) var seeNoSee: String!
private(set) var room: String!
private(set) var app: String!
private(set) var documentId: String!
private(set) var username: String!
private(set) var userId: String!
init(ptName: String, assignedMd: String, officeMd: String, assignedDate: String, seeNoSee: String, room: String, app: String, documentId: String, username: String, userId: String) {
self.ptName = ptName
self.assignedMd = assignedMd
self.officeMd = officeMd
self.assignedDate = assignedDate
self.seeNoSee = seeNoSee
self.room = room
self.app = app
self.documentId = documentId
self.username = username
self.userId = userId
}
}
here is my db structure

Related

problem with adding element to swift array

I am creating an application. I make a request to the firebase store, after that I add the result to the array, but in the end, when the array is displayed from viewDidLoad or other functions, I get an empty array. But if you make a conclusion immediately after the request, then everything is displayed correctly
`
import UIKit
import Firebase
import FirebaseStorage
import FirebaseFirestore
class CatalogVC: UIViewController {
struct Item: Codable {
var title: String
var price: Int
var description: String
var imageUrl: String
}
#Published var items: [Item] = []
let database = Firestore.firestore()
#IBOutlet weak var textViewCatalog: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
let settings = FirestoreSettings()
Firestore.firestore().settings = settings
itemsList()
print(items)
showCatalogVC()
}
#IBAction func showCatalogTapped() {
}
private func showCatalogVC() {
print("SHOW CATALOG")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let dvc = storyboard.instantiateViewController(withIdentifier: "CatalogVC") as! CatalogVC
self.present(dvc, animated: true, completion: nil)
}
func itemsList(){
database.collection("catalog")
.getDocuments { (snapshot, error) in
self.items.removeAll()
if let snapshot {
for document in snapshot.documents{
let docData = document.data()
let title: String = docData["title"] as? String ?? ""
let imageUrl: String = docData["imageUrl"] as? String ?? ""
let description: String = docData["description"] as? String ?? ""
let price: Int = docData["price"] as? Int ?? 0
let item: Item = Item(title: title, price: price, description: description, imageUrl: imageUrl)
self.items.append(item)
}
}
}
}
}
`
I am creating an application. I make a request to the firebase store, after that I add the result to the array, but in the end, when the array is displayed from viewDidLoad or other functions, I get an empty array. But if you make a conclusion immediately after the request, then everything is displayed correctly
Getting data from Firebase is asynchronous process. So here you should make everything after loading data in closure database.collection("catalog").getDocuments {...}.
func itemsList(){
database.collection("catalog")
.getDocuments { (snapshot, error) in
self.items.removeAll()
if let snapshot {
for document in snapshot.documents{
let docData = document.data()
let title: String = docData["title"] as? String ?? ""
let imageUrl: String = docData["imageUrl"] as? String ?? ""
let description: String = docData["description"] as? String ?? ""
let price: Int = docData["price"] as? Int ?? 0
let item: Item = Item(title: title, price: price, description: description, imageUrl: imageUrl)
self.items.append(item)
}
}
print(self.items) //print items to see them
//here use items data
}
}

Why is my Firestore data not displaying as table view?

I am currently working on a project with Xcode (User Interface: Storyboard) and Google Firebase. I cannot get my app to display the information like this:
Desired output
The way I got this picture was by starting a test project selecting SwiftUI as my User Interface, and only having ONE single view controller. In my app a user will only arrive to this page after they have logged in and click a button that takes them to the table view.
My app currently prints the result at the bottom of Xcode:
Current output
import UIKit
import FirebaseFirestore
class AssetViewController: UIViewController {
#IBOutlet weak var assetList: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let db = Firestore.firestore()
db.collection("assets").getDocuments { (snapshot, error) in
if error != nil {
print("error")
} else {
for document in (snapshot?.documents)! {
if let brand = document.data() ["brand"] as? String {
if let description = document.data() ["description"] as? String {
if let date = document.data() ["date"] as? String {
if let cost = document.data() ["cost"] as? String {
if let serial = document.data() ["serial"] as? String {
if let warranty = document.data() ["warranty"] as? String {
if let warranty_cost = document.data() ["warranty_cost"] as? String {
print("\(document.documentID) => \(document.data())") }
}
}
}
}
}
}
}
}
}
}
}
I have the following class:
import Foundation
import FirebaseFirestore
class AssetsViewModel: ObservableObject {
#Published var assets = [Asset] ()
private var db = Firestore.firestore()
func fetchData() {
db.collection("assets").addSnapshotListener { (QuerySnapshot, error) in
guard let documents = QuerySnapshot?.documents else {
print("No assets in here")
return
}
self.assets = documents.map { (QueryDocumentSnapshot) -> Asset in
let data = QueryDocumentSnapshot.data()
let brand = data["brand"] as? String ?? ""
let description = data["description"] as? String ?? ""
let date = data["date"] as? String ?? ""
let cost = data["cost"] as? String ?? ""
let serial = data["serial"] as? String ?? ""
let warranty = data["warranty"] as? String ?? ""
let warranty_cost = data["warranty_cost"] as? String ?? ""
return Asset(brand: brand, description: description, date: date, cost: cost, serial: serial, warranty: warranty, warranty_cost: warranty_cost)
}
}
}
}
And I have the following structure:
import Foundation
struct Asset: Identifiable {
var id: String = UUID().uuidString
var brand: String
var description: String
var date: String
var cost: String
var serial: String
var warranty: String
var warranty_cost: String
}
My main goal is to display the information like the first picture. I would appreciate any help given.
This is the code that I used to display the first picture:
import SwiftUI
struct ContentView: View {
#ObservedObject private var viewModel = AssetsViewModel()
var body: some View {
NavigationView {
List(viewModel.assets) { asset in
VStack(alignment: .leading) {
Text(asset.brand)
.font(.headline)
Text(asset.description)
.font(.subheadline)
Text(asset.date)
.font(.subheadline)
Text(asset.cost)
.font(.subheadline)
Text(asset.serial)
.font(.subheadline)
Text(asset.warranty)
.font(.subheadline)
Text(asset.warranty_cost)
.font(.subheadline)
}
}
.navigationBarTitle("Assets")
.onAppear() {
self.viewModel.fetchData()
}
}
}
}
struct AssetViewSwiftUIView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The post below helped me. The error I was getting was because Swift did not like me naming the ViewTable "assetList". Once I changed the name to "tableView" and changed in the code, it worked well. I added extra code to make the cells auto adjust from here: why UITableViewAutomaticDimension not working?.
Thank you very much for your help!
You need something like this:
import Foundation
import UIKit
import FirebaseFirestore
class myTableCell: UITableViewCell {
#IBOutlet weak var brand: UILabel!
#IBOutlet weak var description: UILabel!
#IBOutlet weak var date: UILabel!
#IBOutlet weak var cost: UILabel!
#IBOutlet weak var serial: UILabel!
#IBOutlet weak var warranty: UILabel!
#IBOutlet weak var warrantyCost: UILabel!
}
class IndexAssets {
var brand = ""
var description = ""
var date = ""
var cost = ""
var serial = ""
var warranty = ""
var warrantyCost = ""
init(brand: String, description: String, date: String, cost: String, serial: String, warranty: String, warrantyCost: String)
{
self.brand = brand
self.description = description
self.date = date
self.cost = cost
self.serial = serial
self.warranty = warranty
self.warrantyCost = warrantyCost
}
}
class AssetViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var assetList: UITableView!
var dataArray: [IndexAssets] = [IndexAssets]()
override func viewDidLoad() {
super.viewDidLoad()
downloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! myTableCells
cell.brand?.text = dataArray[indexPath.row].brand
cell.description?.text = dataArray[indexPath.row].description
cell.date?.text = dataArray[indexPath.row].date
cell.cost?.text = dataArray[indexPath.row].cost
cell.serial?.text = dataArray[indexPath.row].serial
cell.warranty?.text = dataArray[indexPath.row].warranty
cell.warrantyCost?.text = dataArray[indexPath.row].warrantyCost
return cell
}
func downloadData()
{
let db = Firestore.firestore()
db.collection("assets").getDocuments { (snapshot, error) in
if error != nil
{
print("error")
}
else
{
for document in (snapshot?.documents)!
{
let brand = document.data() ["brand"] as? String
let description = document.data() ["description"] as? String
let date = document.data() ["date"] as? String
let cost = document.data() ["cost"] as? String
let serial = document.data() ["serial"] as? String
let warranty = document.data() ["warranty"] as? String
let warrantyCost = document.data() ["warranty_cost"] as? String
if let brand = brand, let description = description, let date = date, let cost = cost, let serial = serial, let warranty = warranty, let warrantyCost = warrantyCost
{
let insert = IndexAssets(brand: brand, description: description, date: date, cost: cost, serial: serial, warranty: warranty, warrantyCost: warrantyCost)
self.dataArray.append(insert)
}
}
self.assetList.reloadData()
}}
}
}
Also, remember in Storyboard to:
Assign "cell" to your Dynamic Cell identifier
Select myTableCell as Class for that cell
Drag labels from the class to connect them to storyboard
Drag your table to your View Controller (Yellow Icon on top) and select DataSource and Delegate.

Return from initializer without initializing all stored properties error Firebase

I am working with Firebase and am trying to make a posts class. I keep getting the error "Return from initializer without initializing all stored properties" when I try to initialize the variables. I am not really sure how to fix this error and not entirely sure what the problem is. I was wondering if anyone could help. Thank you.
import Foundation
import Firebase
import FirebaseDatabase
class Posts {
private var _username: String!
private var _userImg: String!
private var _postImg: String!
private var _likes: Int!
private var _postKey: String!
private var _postRef: DatabaseReference
var username: String{
return _username
}
var userImg: String{
return _userImg
}
var postImg: String{
get{
return _postImg
}set{
_postImg = newValue
}
}
var likes: Int{
return _likes
}
var postKey: String!{
return _postKey
}
init(imgUrl: String, likes: Int, username: String, userImg: String){
_postImg = imgUrl
_likes = likes
_username = username
_userImg = userImg
_postKey = nil
} ///WHERE I GET THE ERROR
init(postKey: String, postData: Dictionary<String, AnyObject>) {
_postKey = postKey
if let username = postData["username"] as? String{
_username = username
}
if let userImg = postData["userImg"] as? String{
_userImg = userImg
}
if let postImage = postData["imageUrl"] as? String{
_postImg = postImage
}
if let likes = postData["likes"] as? Int {
_likes = likes
}
_postRef = Database.database().reference().child("posts")
}
}
Thank you guys for the comments. After staring at if for a while, I realized I did not have an exclamation mark right after " private var _postRef: DatabaseReference
" so, I just added the "!" at the end and it worked.
OLD:
private var _postRef: DatabaseReference
NEW:
private var _postRef: DatabaseReference!

Firebase Database post error : Cannot invoke initializer for type 'Post' with an argument list of type

I am trying to Post something to my Firebase Database but the post needs the uid so i know from who the post is. I keep getting this error : `Cannot invoke initializer for type 'Post' with an argument list of type '(username: String!, postId: String, postText: String!, postGame: String!, postDate: (NSNumber), postType: String, uid: String!)' But I dont know how i can fix this. Can Someone please help me! Thanks!
import UIKit
import FirebaseAuth
import FirebaseStorage
import FirebaseDatabase
class AddPostViewController: UIViewController,UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var textView: UITextView!
#IBOutlet weak var GameText: UITextField!
var currentUser: User2!
var dataBaseRef: DatabaseReference! {
return Database.database().reference()
}
var storageRef: Storage {
return Storage.storage()
}
func loadUserInfo(){
let userRef = dataBaseRef.child("users/\(Auth.auth().currentUser!.uid)")
userRef.observe(.value, with: { (snapshot) in
self.currentUser = User2(snapshot: snapshot)
}) { (error) in
print(error.localizedDescription)
}
}
#objc func savePost(){
var text: String!
var Game: String!
if let postText = textView.text {
text = postText
}
if let postGame = GameText.text {
Game = postGame
}
let newPost = Post(username: self.currentUser.username, postId: NSUUID().uuidString, postText: text, postGame: Game, postDate: (NSDate().timeIntervalSince1970 as NSNumber), postType: "TEXT", uid: self.currentUser.uid)
let postRef = self.dataBaseRef.child("posts").childByAutoId()
postRef.setValue(newPost.toAnyObject(), withCompletionBlock: { (error, ref) in
if error == nil {
self.navigationController!.popToRootViewController(animated: true)
}else {
print(error!.localizedDescription)
}
})
}
}
Here is my Post model:
import Foundation
import UIKit
import FirebaseDatabase
import FirebaseAuth
import Firebase
struct Post {
var username: String!
var Game: String!
var Console: String!
var ExtraInfo: String!
var uid: String!
var postId: String!
var postType: String!
var postText: String!
var postDate: NSNumber!
var ref: DatabaseReference!
var key: String?
init(snapshot: DataSnapshot){
self.ref = snapshot.ref
self.key = snapshot.key
self.username = (snapshot.value! as! NSDictionary)["username"] as! String
self.Console = (snapshot.value! as! NSDictionary)["Console"] as! String
self.ExtraInfo = (snapshot.value! as! NSDictionary)["ExtraInfo"] as! String
self.Game = (snapshot.value! as! NSDictionary)["Game"] as! String
self.postId = (snapshot.value! as! NSDictionary)["postId"] as! String
self.postType = (snapshot.value! as! NSDictionary)["postType"] as! String
self.postDate = (snapshot.value! as! NSDictionary)["postDate"] as! NSNumber
self.postText = (snapshot.value! as! NSDictionary)["postText"] as! String
self.uid = (snapshot.value! as! NSDictionary)["uid"] as! String
}
init(username: String, postId: String, Game: String, Console: String, ExtraInfo: String, postText: String, postDate: NSNumber, postType: String, uid: String){
self.username = username
self.Game = Game
self.Console = Console
self.ExtraInfo = ExtraInfo
self.postText = postText
self.postType = postType
self.uid = uid
self.postDate = postDate
self.postId = postId
}
func toAnyObject() -> [String: Any] {
return ["username": username, "postId":postId,"Game": Game , "Console": Console,"ExtraInfo": ExtraInfo ,"postType":postType, "postDate":postDate, "postText":postText,"uid": uid]
}
}
The problem is here:
let newPost = Post(username: self.currentUser.username, postId: NSUUID().uuidString,
postText: text, postGame: Game, postDate: (NSDate().timeIntervalSince1970 as NSNumber),
postType: "TEXT", uid: self.currentUser.uid)
You're trying to initialize a Post object, but according to the init you created, it needs to have these components:
init(username: String, postId: String, Game: String, Console: String,
ExtraInfo: String, postText: String, postDate: NSNumber, postType: String,
uid: String)
So instead, for newPost, it should have the correct initializer like:
let newPost = Post(username: self.currentUser.username, postId: NSUUID().uuidString,
Game: Game, Console: /*I don't know what goes here */, ExtraInfo: String,
postText: text, postDate: (NSDate().timeIntervalSince1970 as NSNumber),
postType: "TEXT", uid: self.currentUser.uid)

how to use If let in optional photo?

So, my app crash because it forces to unwrap the "photo" even if it is optional and it has nothing inside. How do I use the if let statement, like if the photo has a picture then it will show, and if none it will be nil but the app wont crash.
I have this struct User this is where my data is saved I am using firebase.
struct User {
var fullName: String!
var username: String?
var email: String!
var country: String?
var photoURL: String?
var biography: String?
var uid: String!
var ref: FIRDatabaseReference?
var key: String?
init(snapshot: FIRDataSnapshot) {
key = snapshot.key
ref = snapshot.ref
fullName = (snapshot.value! as! NSDictionary) ["fullName"] as! String
email = (snapshot.value! as! NSDictionary) ["email"] as! String
uid = (snapshot.value! as! NSDictionary) ["uid"] as! String
country = (snapshot.value! as! NSDictionary) ["country"] as! String?
biography = (snapshot.value! as! NSDictionary) ["biography"] as! String?
photoURL = (snapshot.value! as! NSDictionary) ["photoURL"] as! String?
username = (snapshot.value! as! NSDictionary) ["username"] as! String?
}
}
this is where the app crashes, because of the "self.storageRef.reference(forURL:imageURL!)" it forces to unwrap even it has nothing inside.
func loadUserInfo() {
#IBOutlet weak var userImageView: UIImageView!
#IBOutlet weak var addButton: UIButton!
#IBOutlet weak var fullName: UILabel!
#IBOutlet weak var username: UILabel!
#IBOutlet weak var country: UILabel!
#IBOutlet weak var biography: UILabel!
let userRef = dataBaseRef.child("users/\(FIRAuth.auth()!.currentUser!.uid)")
userRef.observe(.value, with: { (snapshot) in
let user = User(snapshot: snapshot)
self.username.text = user.username
self.country.text = user.country
self.biography.text = user.biography
self.fullName.text = user.fullName
var imageURL = user.photoURL
self.storageRef.reference(forURL:imageURL!).data(withMaxSize: 1 * 1024 * 1024, completion: { (imgData, error) in
if error == nil {
DispatchQueue.main.async {
if let data = imgData {
self.userImageView?.image = UIImage(data: data)
}
}
} else {
print(error!.localizedDescription)
}
})
})
{ (error) in
print(error.localizedDescription)
}
}
first I think you should change the init of the User, you should do:
let data = snapshot.value as! NSDictionary;
fullName = data["fullName"] as! String;
if you not sure the country whether exist, you could do:
country = data["country"] as? String;
then you could use let to keep save when you use the use.photoURL, just like:
if let photoURL = user.photoURL {
//...retrive photo of the url from somewhere
}
finally, I wanna say, you maybe make a mistake understand about the ? and !, or confusion with them.
? is when you think this variable/func maybe nil/can not be called, or when you make a type conversion, but you don't sure that must be success.
! is that you are sure it's exist or you will create it immediatelly. also you could comprehend it as unwrap the optional value, just cause of you make it absolute exist.
when we create our own model just like your User, you could make the columns impossible nil, you can do:
var fullName: String? = nil;
fullName = SomeJsonTypeData["fullName"] ?? "default value";
then when you use it, you dispense with any worry about it will be nil.
If just focusing on the photoURL issue, I think this may help you:
if let imageURL = user.photoURL {
self.storageRef.reference(forURL: imageURL).data(withMaxSize: 1 * 1024 * 1024, completion: { (imgData, error) in
if error == nil {
DispatchQueue.main.async {
if let data = imgData {
self.userImageView?.image = UIImage(data: data)
}
}
} else {
print(error!.localizedDescription)
}
})
}