I am trying to get the inputted text from recipeName, servingsNumber, prepTime and cookTime to save to fb.
I know this is close to where I need to be. I am need some help with my save function. I need to assign the textField.text in sendToFirebase() I am able to print out the inputted text into the console and I was able to get it to save a document to FB but the entry was null. What am I missing? I feel like it is here in my code:
import UIKit
import Firebase
class AddRecipeViewController: UIViewController {
#IBOutlet weak var table: UITableView!
#IBOutlet weak var saveButton: UIBarButtonItem!
let db = Firestore.firestore()
var id = UUID().uuidString
var data = [RecipeData]()
let user = Auth.auth().currentUser?.email
var objRecipe = RecipeData.init()
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
navigationItem.hidesBackButton = true
let newBackButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancelPressed))
self.navigationItem.leftBarButtonItem = newBackButton
table.register(UINib(nibName: "RecipeNameCell", bundle: nil), forCellReuseIdentifier: "recipeName")
table.register(UINib(nibName: "ServingSizeTableViewCell", bundle: nil), forCellReuseIdentifier: "servings")
table.register(UINib(nibName: "PrepTimeTableViewCell", bundle: nil), forCellReuseIdentifier: "prep")
table.register(UINib(nibName: "CookTimeTableViewCell", bundle: nil), forCellReuseIdentifier: "cook")
}
func sendToFirebase() {
do {
let recipeName = objRecipe.recipeName
let servings = objRecipe.servingsNumber
let prep = objRecipe.prepTime
let cook = objRecipe.cookTime
let user = Auth.auth().currentUser?.email
let id = objRecipe.id
let data = try JSONEncoder().encode(objRecipe)
let dictionary = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any] ?? [:]
db.collection("Recipe").document(objRecipe.id).setData(dictionary)
}
catch {
print(error)
}
}
#objc func cancelPressed(sender: UIBarButtonItem) {
navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)
}
#IBAction func savePressed(_ sender: UIBarButtonItem) {
sendToFirebase()
}
}
extension AddRecipeViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = table.dequeueReusableCell(withIdentifier: "recipeName", for: indexPath) as! RecipeNameCell
cell.textField.delegate = self
cell.textField.tag = indexPath.row
return cell
}
else if indexPath.row == 1 {
let cell = table.dequeueReusableCell(withIdentifier: "servings", for: indexPath) as! ServingSizeTableViewCell
cell.textField.delegate = self
cell.textField.tag = indexPath.row
return cell
}
else if indexPath.row == 2 {
let cell = table.dequeueReusableCell(withIdentifier: "prep", for: indexPath) as! PrepTimeTableViewCell
cell.textField.delegate = self
cell.textField.tag = indexPath.row
return cell
}
else if indexPath.row == 3{
let cell = table.dequeueReusableCell(withIdentifier: "cook", for: indexPath) as! CookTimeTableViewCell
cell.textField.delegate = self
cell.textField.tag = indexPath.row
return cell
}
else {
let cell = table.dequeueReusableCell(withIdentifier: "recipeName", for: indexPath) as! RecipeNameCell
return cell
}
}
}
extension AddRecipeViewController: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
switch textField.tag {
case 0:
let recipeName = textField.text!
print(recipeName)
objRecipe.recipeName = textField.text!
// sendToFirebase() // Send to firebase
case 1:
let servingSize = textField.text!
print(servingSize)
objRecipe.servingsNumber = textField.text!
// sendToFirebase() // Send to firebase
case 2:
let prepTime = textField.text!
print(prepTime)
objRecipe.prepTime = textField.text!
// sendToFirebase() // Send to firebase
case 3:
let cookTime = textField.text!
print(cookTime)
objRecipe.cookTime = textField.text!
// sendToFirebase() // Send to firebase
default: break
}
}
}
struct RecipeData: Codable {
var user: String?
var recipeName: String?
var ingredientsText: String?
var directionsText: String?
var servingsNumber: String?
var prepTime: String?
var cookTime: String?
var image: String?
let id = UUID().uuidString
}
class RecipeNameCell: UITableViewCell {
#IBOutlet weak var textField: UITextField!
var id = UUID().uuidString
let db = Firestore.firestore()
var name: String?
override func awakeFromNib() {
super.awakeFromNib()
textField.placeholder = "Recipe Name..."
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
func saveData(text: String) {
if let recipeName = recipeNameTextField.text,
let addedIngredients = ingredientsTextField.text,
let directionsText = directionsTextField.text,
let servingsNum = numServingLabel.text,
let image = imageView.image,
let user = Auth.auth().currentUser?.email {
let newRecipeRef = db.collection(D.FStore.collectionName).document(id)
newRecipeRef.setData([
D.FStore.recipeTextField: recipeName,
D.FStore.ingredientsText: addedIngredients,
D.FStore.directionsText: directionsText,
D.FStore.numberServings: servingsNum,
D.FStore.userField: user,
D.FStore.id: id,
D.FStore.image: image,
]) { err in
if let err = err {
print("Error adding document: \(err)")
} else {
print("Document added with ID:\(newRecipeRef)")
}
}
}
}
import UIKit
import Firebase
class HomeScreenViewController: UIViewController {
#IBOutlet weak var table: UITableView!
#IBOutlet weak var logout: UIBarButtonItem!
#IBOutlet weak var add: UIBarButtonItem!
let db = Firestore.firestore()
var data = [RecipeData]()
var recipeNamed: String?
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
navigationItem.hidesBackButton = true
table.register(UINib(nibName: "RecipeCell", bundle: nil), forCellReuseIdentifier: "cell")
}
func loadRecipeNames() {
db.collection("Recipe")
.addSnapshotListener { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let data = document.data()
if let user = data["User"] as? String,
let recipeNameLabels = data["Recipe Name"] as? String,
let prep = data["Prep Time"] as? String,
let cook = data["Cook Time"] as? String,
let servings = data["Servings"] as? String
{
let newRecipe = RecipeData(user: user, recipeName: recipeNameLabels, servingsNumber: servings, prepTime: prep, cookTime: cook)
self.data.append(newRecipe)
}
DispatchQueue.main.async {
self.table.reloadData()
}
}
}
}
}
#IBAction func logoutPress(_ sender: UIBarButtonItem) {
do {
try Auth.auth().signOut()
navigationController?.popToRootViewController(animated: true)
} catch let signOutError as NSError {
print("Error signing out: %#", signOutError)
}
}
#IBAction func addRecipePressed(_ sender: UIBarButtonItem) {
performSegue(withIdentifier: "add", sender: self)
}
}
extension HomeScreenViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = data[indexPath.row]
let cell = table.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! RecipeCell
cell.recipeNameLabel.text = data.recipeName
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "DetailSegue", sender: self)
}
}
You need to fire the sendToFirebase() function:
extension AddRecipeViewController: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
switch textField.tag {
case 0:
let recipeName = textField.text!
print(recipeName)
objRecipe.recipeName = textField.text!
sendToFirebase() // Send to firebase
case 1:
let servingSize = textField.text!
print(servingSize)
objRecipe.servingsNumber = textField.text!
sendToFirebase()
case 2:
let prepTime = textField.text!
print(prepTime)
objRecipe.prepTime = textField.text!
sendToFirebase()
case 3:
let cookTime = textField.text!
print(cookTime)
objRecipe.cookTime = textField.text!
sendToFirebase()
default: break
}
}
}
sendToFirebase() function:
func sendToFirebase() {
let recipeName = objRecipe.recipeName
let servings = objRecipe.servingsNumber
let prep = objRecipe.prepTime
let cook = objRecipe.cookTime
let user = Auth.auth().currentUser?.email
let newRecipeRef = db.collection("Recipe").document(objRecipe.id)
newRecipeRef.setData([
"Recipe Name" : recipeName ?? "Empty",
"Serving #" : servings ?? "Empty",
"Prep Time" : prep ?? "Empty",
"Cook Time" : cook ?? "Empty",
"User" : user ?? "No email provided",
])
{ err in
if let err = err {
print("Error adding document: \(err)")
} else {
print("Document added with ID:\(newRecipeRef)")
}
}
}
Related
I have tableview listed pdfs by retrieving from Firebase. No problem with retrieving datas and sending default SMTP. What I am trying to do that selected urls(pdf) in tableview, will be sent by using SMTP to user email so that user can easily reach pdf urls. I could not figure out how to handle it. This is the only critical point in my project. Any help is appreciated.
import UIKit
import Firebase
import PDFKit
import skpsmtpmessage
class IcsViewcontroller: UIViewController , UISearchBarDelegate,SKPSMTPMessageDelegate{
var preImage : UIImage?
let cellSpacingHeight: CGFloat = 20
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var pdfListView: UITableView!
#IBOutlet weak var spinner: UIActivityIndicatorView!
var pdfList = [pdfClass]()
var searchall = [pdfClass]()
var searching = false
var selectedPdf = [pdfClass]()
override func viewDidLoad() {
super.viewDidLoad()
let editButton = UIBarButtonItem(title: "Edit", style: .plain, target: self, action: #selector(showEditing(_:)))
navigationItem.rightBarButtonItem = editButton
pdfListView.delegate = self
pdfListView.dataSource = self
searchBar.delegate = self
self.pdfListView.isHidden = true
getPdf()
sendEmail()
searchBar.searchTextField.backgroundColor = .white
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let selectionIndexPath = self.pdfListView.indexPathForSelectedRow {
self.pdfListView.deselectRow(at: selectionIndexPath, animated: animated)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if searching {
let destination = segue.destination as! PdfKitViewController
let selectedIndexPath = pdfListView.indexPathForSelectedRow
destination.pdf = searchall[selectedIndexPath!.row]
} else {
let destination = segue.destination as! PdfKitViewController
let selectedIndexPath = pdfListView.indexPathForSelectedRow
destination.pdf = pdfList [selectedIndexPath!.row]
}
}
#objc func showEditing(_ sender: UIBarButtonItem)
{
if(self.pdfListView.isEditing == true)
{
self.pdfListView.isEditing = false
self.navigationItem.rightBarButtonItem?.title = "Edit"
}
else
{
self.pdfListView.allowsMultipleSelectionDuringEditing = true
self.pdfListView.isEditing = true
self.navigationItem.rightBarButtonItem?.title = "Done"
}
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
if searchBar.text == nil || searchBar.text == "" {
searching = false
} else {
searching = true
}
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searching = false
searchBar.text = ""
self.pdfListView.reloadData()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searching = false
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
searchall = pdfList
searching = false
pdfListView.reloadData()
} else {
searching = true
searchall = pdfList.filter({($0.pdf_name?.lowercased().prefix(searchText.count))! == searchText.lowercased() })
pdfListView.reloadData()
}
}
func getPdf () {
spinner.startAnimating()
let docRef = Storage.storage().reference().child("ICS_Documents")
docRef.listAll{ (result , error ) in
if error != nil {
let alert = UIAlertController(title: "Error", message: "No Database Connection", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
self.spinner.stopAnimating()
}
for item in result.items {
let storeageLocation = String( describing : item)
let gsReference = Storage.storage().reference(forURL: storeageLocation)
gsReference.downloadURL{ url , error in
if let error = error{
print(error)
} else {
let pdf_name = String( item.name)
let pdf_url = url?.absoluteString
let thumbnailSize = CGSize(width: 100, height: 100)
let thmbnail = self.generatePdfThumbnail(of: thumbnailSize, for: url!, atPage: 0)
let pdfall = pdfClass(pdf_name: pdf_name, pdf_url: pdf_url!, pdf_preview: thmbnail!)
self.pdfList.append(pdfall)
}
DispatchQueue.main.async {
self.pdfList.sort{ $0.pdf_name ?? "" < $1.pdf_name ?? ""}
self.pdfListView.reloadData()
self.spinner.stopAnimating()
self.pdfListView.isHidden = false
}
}
}
}
}
func generatePdfThumbnail(of thumbnailSize: CGSize , for documentUrl: URL, atPage
pageIndex: Int) -> UIImage? {
let pdfDocument = PDFDocument(url: documentUrl)
let pdfDocumentPage = pdfDocument?.page(at: pageIndex)
return pdfDocumentPage?.thumbnail(of: thumbnailSize, for:
PDFDisplayBox.trimBox)
}
func sendEmail() {
let message = SKPSMTPMessage()
message.relayHost = "smtp.gmail.com"
message.login = "xxx#gmail.com"
message.pass = "******"
message.requiresAuth = true
message.wantsSecure = true
message.relayPorts = [587]
message.fromEmail = "xxxx#gmail.com"
message.toEmail = "xxxx#gmail.com"
message.subject = "subject"
let messagePart = [kSKPSMTPPartContentTypeKey: "text/plain; charset=UTF-8",
kSKPSMTPPartMessageKey: "Hi alll"]
message.parts = [messagePart]
message.delegate = self
message.send()
}
func messageSent(_ message: SKPSMTPMessage!) {
print("Successfully sent email!")
}
func messageFailed(_ message: SKPSMTPMessage!, error: Error!) {
print("Sending email failed!")
}
}
extension IcsViewcontroller : UITableViewDelegate,UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if searching{
return searchall.count
}else {
return pdfList.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "pdfCell", for: indexPath) as! pdfCellTableViewCell
let varcell : pdfClass
if searching {
varcell = searchall [indexPath.row]
} else {
varcell = pdfList [indexPath.row]
}
cell.configure(name: varcell.pdf_name! , pdfthumbnail: varcell.pdf_preview!)
cell.accessoryType = .disclosureIndicator
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var indx : pdfClass
if searching{
indx = searchall[indexPath.row ]
}else {
indx = pdfList[indexPath.row]
}
self.selectdeselectcell(tableview: tableView, indexpath: indexPath)
print("selected")
if pdfListView.isEditing {
func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return !tableView.isEditing
}
}else{
performSegue(withIdentifier: "toPdfKit", sender: indx)
print(indexPath.row)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
self.selectdeselectcell(tableview: tableView, indexpath: indexPath)
print("deselected")
}
func selectdeselectcell(tableview : UITableView ,indexpath : IndexPath){
if pdfListView.isEditing{
self.selectedPdf.removeAll()
if let arr = pdfListView.indexPathForSelectedRow{
print(arr)
}
}else {
return
}
}
/* func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let deleteAction = UITableViewRowAction(style: .default, title: "Delete", handler: { (action:UITableViewRowAction,indexPath:IndexPath)-> Void in
let storage = Storage.storage()
var childsURL : String
if self.searching {
childsURL = self.searchall[indexPath.row ].pdf_url!
}else{
childsURL = self.pdfList[indexPath.row].pdf_url!
}
let storageref = storage.reference(forURL: childsURL)
storageref.delete{ error in
if let error = error {
print(error.localizedDescription)
} else{
print("File deleted")
}
}
self.pdfList.removeAll()
self.getPdf()
})
return [deleteAction]
}*/
}
I am using below code for the UITableView, in every cell there are two buttons configured to perform certain actions as shown in the code. Earlier it was working fine, but now As I update this code to new version of Xcode it is not working, whenever I tap on the cell or the button in the cell it doesn't perform any action, neither it shows any error, but it just darkens the half of the cell with grey colour?
Xcode earlier was 10 and now 11, swift 5 version same in both cases
There is one fixed cell at the top and then there is list of cells as per the number of documents in the database
What could be the issue?
for information I am using Swift IOS and cloud firestore database
class HomeViewController: UITableViewController {
var posts = [Post]()
var db: Firestore!
var scores1 = [Scores]()
var postAuthorId:String = ""
var postAuthorname:String = ""
var CommentAuthorName:String = ""
var PostTitle:String = ""
var postAuthorGender:String = ""
var postAuthorEmail:String = ""
var postAuthorfullname:String = ""
var postAuthorSpinnerC:String = ""
var postContent:String = ""
var postCategory:String = ""
var postAuthorPicUrl:String = ""
var postTimeStamp:String = ""
var l11:Int = 0
var postKey:String = ""
private var documents: [DocumentSnapshot] = []
override func viewDidLoad() {
super.viewDidLoad()
db = Firestore.firestore()
tableView.dataSource = self
tableView.delegate = self
retrieveAllPosts()
getuserscores()
var AViewController: UIViewController = UIViewController()
var MyTabBarItem: UITabBarItem = UITabBarItem(title: nil, image: UIImage(named: "pencil")?.withRenderingMode(UIImage.RenderingMode.alwaysOriginal), selectedImage: UIImage(named: "pencil"))
AViewController.tabBarItem = MyTabBarItem
// Do any additional setup after loading the view.
// navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Sign Out", style: .plain, target: self, action: #selector(handleLogout))
}
func getuserscores(){
let userRef = Firestore.firestore().collection("users").document(Auth.auth().currentUser!.uid)
userRef.getDocument{(document, error) in
if let document = document, document.exists{
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
let l1 = document.get("l1") as! Int
let l2 = document.get("l2") as! Int
let l3 = document.get("l3") as! Int
let l4 = document.get("l4") as! Int
let newScores = Scores(_l1: l1, _l2: l2, _l3: l3, _l4: l4)
self.scores1.append(newScores)
}
self.tableView.reloadData()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func retrieveAllPosts(){
let postsRef = Firestore.firestore().collection("posts").order(by: "timestamp", descending: true).limit(to: 50)
postsRef.getDocuments { (snapshot, error) in
if let error = error {
print(error.localizedDescription)
} else {
if let snapshot = snapshot {
for document in snapshot.documents {
let data = document.data()
let username = data["post_author_username"] as? String ?? ""
let postTitle = data["postTitle"] as? String ?? ""
let postcategory = data["postcategory"] as? String ?? ""
let postContent = data["postContent"] as? String ?? ""
let postAuthorProfilePicUrl = data["post_user_profile_pic_url"] as? String ?? ""
let postAuthorSpinnerC = data["post_author_spinnerC"] as? String
let newSourse = Post(_documentId: document.documentID, _username: username, _postTitle: postTitle, _postcategory: postcategory, _postContent: postContent, _postuserprofileImagUrl: postAuthorProfilePicUrl, _postAuthorSpinncerC: postAuthorSpinnerC)
self.posts.append(newSourse)
}
self.tableView.reloadData()
}
}
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.estimatedRowHeight = 200
tableView.rowHeight = UITableView.automaticDimension
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return scores1.count + posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 && scores1.count == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "SmileMiles") as! ScoresCellInHomeScreen
cell.set(scores: scores1[indexPath.row])
return cell
} else if posts.count > (indexPath.row - 1 ) {
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
cell.btnComment.tag = indexPath.row - 1
cell.btnComment.addTarget(self, action: #selector(toComments(_:)), for: .touchUpInside)
cell.favoritebutton.tag = indexPath.row - 1
cell.favoritebutton.addTarget(self, action: #selector(favupdate(_:)), for: .touchUpInside)
cell.set(post: posts[indexPath.row - 1 ])
return cell
} else {
return UITableViewCell()
}
}
#objc func favupdate(_ sender: AnyObject) {
let commentbutton = sender as! UIButton
let post = posts[commentbutton.tag]
postKey = post._documentId // or what key value it is
let userMarkRef = Firestore.firestore().collection("users").document(Auth.auth().currentUser!.uid).collection("marked_posts").document(postKey)
let postRef = Firestore.firestore().collection("posts").document(postKey)
postRef.getDocument{(document, error) in
if let document = document, document.exists{
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
self.postAuthorId = document.get("post_author_id") as! String
self.postAuthorname = document.get("post_author_username") as! String
self.PostTitle = document.get("postTitle") as! String
self.postContent = document.get("postContent") as! String
self.postAuthorEmail = document.get("post_author_email") as! String
self.postCategory = document.get("postcategory") as! String
self.postAuthorfullname = document.get("post_author_fullname") as! String
self.postAuthorGender = document.get("post_author_gender") as! String
self.postAuthorPicUrl = document.get("post_user_profile_pic_url") as! String
// let l11:Bool = document.get("l1") as! Bool
// self.postTimeStamp = document.get("post_timeStamp") as! String
self.postAuthorSpinnerC = document.get("post_author_spinnerC") as! String
}
let postObject = [
"post_author_gender": self.postAuthorGender,
// "post_author_dateOfBirth": self.dateOfBirth,
"post_author_spinnerC": self.postAuthorSpinnerC,
"post_author_fullname": self.postAuthorfullname,
"post_author_id": self.postAuthorId,
"post_author_username": self.postAuthorname,
"post_author_email": self.postAuthorEmail,
"postTitle": self.PostTitle,
"postcategory": self.postCategory,
"postContent": self.postContent,
// "post_timestamp": FieldValue.serverTimestamp(),
"post_user_profile_pic_url":self.postAuthorPicUrl,
"post_id": self.postKey
] as [String : Any]
userMarkRef.setData(postObject, merge: true) { (err) in
if let err = err {
print(err.localizedDescription)
}
print("Successfully set new user data")
}
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 {
return 150
}
else{
return UITableView.automaticDimension
}
}
#objc func toComments(_ sender: AnyObject) {
let commentbutton = sender as! UIButton
let post = posts[commentbutton.tag]
postKey = post._documentId // or what key value it is
print(postKey + "hello")
performSegue(withIdentifier: "toCommentsList", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "toCommentsList"){
var vc = segue.destination as! CommentListViewController
vc.postId = postKey
}
}
}
Do this in tableview cellForRowAt function before return cell:
cell.selectionStyle = .none
return cell
this will fix the issue
How should I get the document id upon tapping the UItableViewCell?
I know the below code do give me the row index, but for a feed post I would like to get the document id for the the particular post everytime
I have tried to retrieve the document id through key setup in model file but avail to no good
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("row selected: \(indexPath.row)")
performSegue(withIdentifier: "toDetailView", sender: indexPath)
}
Posts Model
import Foundation
import Firebase
import FirebaseFirestore
protocol DocumentSerializable {
init?(dictionary:[String:Any])
}
struct Post {
var _username: String!
var _postTitle: String!
var _postcategory: String!
var _postContent: String!
var dictionary:[String:Any] {
return [
"username": _username,
//"profile_pic":profile_pic,
"postTitle":_postTitle,
"postcategory":_postcategory,
"postContent":_postContent
]
}
}
extension Post : DocumentSerializable {
init?(dictionary: [String : Any]) {
guard let username = dictionary["username"] as? String,
// let profile_pic = dictionary["profile_pic"] as? String,
let postTitle = dictionary["postTitle"] as? String,
let postcategory = dictionary["postcategory"] as? String,
let postContent = dictionary["postContent"] as? String else { return nil }
self.init( _username: username ,_postTitle: postTitle, _postcategory: postcategory, _postContent: postContent)
}
}
Code to retrieve the collection as whole into. tableview
import Foundation
import UIKit
import Firebase
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView:UITableView!
var posts = [Post]()
var db: Firestore!
var postKey:String = ""
private var documents: [DocumentSnapshot] = []
//public var posts: [Post] = []
private var listener : ListenerRegistration!
override func viewDidLoad() {
super.viewDidLoad()
db = Firestore.firestore()
self.navigationController?.navigationBar.isTranslucent = false
tableView = UITableView(frame: view.bounds, style: .plain)
let cellNib = UINib(nibName: "PostTableViewCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "postCell")
tableView.backgroundColor = UIColor(white: 0.90,alpha:1.0)
view.addSubview(tableView)
var layoutGuide:UILayoutGuide!
if #available(iOS 11.0, *) {
layoutGuide = view.safeAreaLayoutGuide
} else {
// Fallback on earlier versions
layoutGuide = view.layoutMarginsGuide
}
tableView.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: layoutGuide.topAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor).isActive = true
tableView.delegate = self
tableView.dataSource = self
tableView.reloadData()
retrieveAllPosts()
//checkForUpdates()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/* override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("toDetailPage", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath = self.tableView.indexPathForSelectedRow
let person = personList[indexPath!.row]
if segue.identifier == "toDetailPage"{
let DetailBookViewController = (segue.destinationViewController as! DetailPage)
DetailBookViewController.user_name = user_name
DetailBookViewController.user_age = user_age
DetailBookViewController.user_urlPicture = user_urlPicture
}*/
#IBAction func handleLogout(_ sender:Any) {
try! Auth.auth().signOut()
self.dismiss(animated: false, completion: nil)
}
/*func checkForUpdates() {
db.collection("posts").whereField("timeStamp", isGreaterThan: Date())
.addSnapshotListener {
querySnapshot, error in
guard let snapshot = querySnapshot else {return}
snapshot.documentChanges.forEach {
diff in
if diff.type == .added {
self.posts.append(Post(dictionary: diff.document.data())!)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}*/
func retrieveAllPosts(){
let postsRef = Firestore.firestore().collection("posts").limit(to: 50)
postsRef.getDocuments { (snapshot, error) in
if let error = error {
print(error.localizedDescription)
} else {
if let snapshot = snapshot {
for document in snapshot.documents {
let data = document.data()
self.postKey = document.documentID
let username = data["username"] as? String ?? ""
let postTitle = data["postTitle"] as? String ?? ""
let postcategory = data["postcategory"] as? String ?? ""
let postContent = data["postContent"] as? String ?? ""
let newSourse = Post(_postKey:self.postKey, _username: username, _postTitle: postTitle, _postcategory: postcategory, _postContent: postContent)
self.posts.append(newSourse)
print(self.postKey)
}
self.tableView.reloadData()
}
}
}
}
/* postsRef.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
self.posts = querySnapshot!.documents.flatMap({Post(dictionary: $0.data())})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}*/
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
cell.set(post: posts[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("row selected: \(indexPath.row)")
//performSegue(withIdentifier: "toDetailView", sender: indexPath)
}
}
Add a new documentId property to your Post struct:
struct Post {
var _username: String!
var _postTitle: String!
var _postcategory: String!
var _postContent: String!
var _documentId: String! // This is new.
var dictionary:[String : Any] {
return [
"documentId" : _documentId, // This is new.
"username": _username,
//"profile_pic":profile_pic,
"postTitle":_postTitle,
"postcategory":_postcategory,
"postContent":_postContent
]
}
}
extension Post : DocumentSerializable
{
init?(dictionary: [String : Any])
{
guard let username = dictionary["username"] as? String,
// let profile_pic = dictionary["profile_pic"] as? String,
let postTitle = dictionary["postTitle"] as? String,
let postcategory = dictionary["postcategory"] as? String,
let documentId = dictionary["documentId"] as? String // This is new.
let postContent = dictionary["postContent"] as? String else { return nil }
self.init( _username: username ,_postTitle: postTitle, _postcategory: postcategory, _postContent: postContent, _documentId: documentId)
}
}
Change your retrieveAllPosts function and set the documentId of the Post instance, do not use the global variable for this:
if let snapshot = snapshot
{
for document in snapshot.documents
{
let data = document.data()
let username = data["username"] as? String ?? ""
let postTitle = data["postTitle"] as? String ?? ""
let postcategory = data["postcategory"] as? String ?? ""
let postContent = data["postContent"] as? String ?? ""
let newSourse = Post(_postKey:self.postKey, _username: username, _postTitle: postTitle, _postcategory: postcategory, _postContent: postContent, _documentId: document.documentId)
self.posts.append(newSourse)
}
}
Now you can access the documentId of the selected Post in didSelectRowAt:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let post = self.posts[indexPath.row]
Swift.print(post.documentId)
// print("row selected: \(indexPath.row)")
// performSegue(withIdentifier: "toDetailView", sender: indexPath)
}
Hopefully this will guide you into the right direction.
I'm using firebase to make an iOS app. I want to retrieve all the users on my database and display their name and profile picture in a table view. Here is my code for my TableViewCell:
import UIKit
import FirebaseDatabase
import FirebaseAuth
import SDWebImage
class HomeTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var likeImageView: UIImageView!
#IBOutlet weak var messageImageView: UIImageView!
#IBOutlet weak var likeCountButton: UIButton!
var homeVC: HomeViewController?
var postReference: DatabaseReference!
var post: UserFile?{
didSet {
updateView()
}
}
var user: UserFile? {
didSet {
updateUserInfo()
}
}
override func awakeFromNib() {
super.awakeFromNib()
nameLabel.text = ""
let berryTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleLikeTap))
likeImageView.addGestureRecognizer(berryTapGesture)
likeImageView.isUserInteractionEnabled = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func updateView() {
if let photoURL = post?.picURL {
profileImageView.sd_setImage(with: URL(string: photoURL))
}
API.Post.REF_POSTS.child(post!.id!).observeSingleEvent(of: .value, with: { postSnapshot in
if let postDictionary = postSnapshot.value as? [String:Any] {
let post = UserFile.transformPost(postDictionary: postDictionary, key: postSnapshot.key)
self.updateLike(post: post)
}
})
API.Post.REF_POSTS.child(post!.id!).observe(.childChanged, with: { snapshot in
if let value = snapshot.value as? Int {
self.likeCountButton.setTitle("\(value) berries", for: .normal)
}
})
}
func updateLike(post: UserFile) {
let imageName = post.berries == nil || !post.isBerried! ? "berry" : "berrySelected"
likeImageView.image = UIImage(named: imageName)
// display a message for berries
guard let count = post.berryCount else {
return
}
if count != 0 {
likeCountButton.setTitle("\(count) berries", for: .normal)
} else if post.berryCount == 0 {
likeCountButton.setTitle("Be the first to Like this", for: .normal)
}
}
func incrementberries(forReference ref: DatabaseReference) {
ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
if var post = currentData.value as? [String : AnyObject], let uid = Auth.auth().currentUser?.uid {
var berries: Dictionary<String, Bool>
berries = post["berries"] as? [String : Bool] ?? [:]
var likeCount = post["berryCount"] as? Int ?? 0
if let _ = berries[uid] {
// Unlike the post and remove self from stars
likeCount -= 1
berries.removeValue(forKey: uid)
} else {
// Like the post and add self to stars
likeCount += 1
berries[uid] = true
}
post["berryCount"] = likeCount as AnyObject?
post["berries"] = berries as AnyObject?
currentData.value = post
return TransactionResult.success(withValue: currentData)
}
return TransactionResult.success(withValue: currentData)
}) { (error, committed, snapshot) in
if let error = error {
print(error.localizedDescription)
}
if let postDictionary = snapshot?.value as? [String:Any] {
let post = UserFile.transformPost(postDictionary: postDictionary, key: snapshot!.key)
self.updateLike(post: post)
}
}
}
func handleLikeTap() {
postReference = API.Post.REF_POSTS.child(post!.id!)
incrementberries(forReference: postReference)
}
override func prepareForReuse() {
super.prepareForReuse()
profileImageView.image = UIImage(named: "industribune-default-no-profile-pic")
}
func updateUserInfo() {
nameLabel.text = user?.username
if let photoURL = user?.profileImageURL {
profileImageView.sd_setImage(with: URL(string: photoURL), placeholderImage: UIImage(named: "industribune-default-no-profile-pic"))
}
}
}
I am displaying this cell on my HomeViewController:
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import Firebase
class HomeViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
var posts = [UserFile]()
var users = [UserFile]()
override func viewDidLoad() {
super.viewDidLoad()
// for performance set an estimated row height
tableView.estimatedRowHeight = 1
// but also request to dynamically adjust to content using AutoLayout
tableView.rowHeight = UITableViewAutomaticDimension
//tableView.delegate = self
tableView.dataSource = self
loadPosts()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadPosts() {
activityIndicatorView.startAnimating()
API.User.observePosts { (newPost) in
guard let userID = newPost.uid else { return }
self.fetchUser(uid: userID, completed: {
// append the new Post and Reload after the user
// has been cached
self.posts.append(newPost)
self.activityIndicatorView.stopAnimating()
self.tableView.reloadData()
})
}
}
func fetchUser(uid: String, completed: #escaping () -> Void) {
API.User.observeUser(withID: uid) { user in
self.users.append(user)
completed()
}
}
}
extension HomeViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
cell.post = posts[indexPath.row]
cell.user = users[indexPath.row]
cell.homeVC = self
return cell
}
}
I have a lot of craziness going on in my project so let me know if you have any questions and what I'm doing wrong. If it's too complicated to understand I'm ready to erase everything and start over too.
And I do honestly think that I followed all the guidelines to ask a question so don't like shut this question down or something.
That's a lot of code. Try this super reduced example. For this, the users node only stores the name as a child node but it could also have an image, email, address, etc.
Example users node
users
uid_0:
name: "Bert"
uid_1:
name: "Ernie"
and some code
var usersArray = [ [String: Any] ]() //an array of dictionaries.
class ViewController: UIViewController {
//set up firebase references here
override func viewDidLoad() {
super.viewDidLoad()
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let userDict = snap.value as! [String: Any]
self.usersArray.append(userDict)
}
self.tableView.reloadData()
})
and the tableView delegate methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.usersArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
let userDict = self.usersArray[indexPath.row]
cell.text = userDict["name"] as! String
//cell.imge = userDict["image"] etc etc
return cell
}
Now... that all being said. This is the perfect use for an array of UserClass objects instead of the dictionaries.
Here's a starting point....
class UserClass {
var name = ""
var image = ""
func init(snap: DataSnapshot) {
//populate the vars from the snapshot
}
}
var userClassArray = [UserClass]()
Don't copy and paste this as there are probably typos but it should point you in the right direction.
I've googled and read a lot of questions and answers about this topic, however I still can't solve my problem. I have a project connected to firebase, with a tableView connected to that information. The following error pops up:
unexpectedly found nil while unwrapping an Optional value.
I've checked all my connections between UIButtons, UILabels and such, and most of all i have tried using breakpoints to locate the error. However I can't seem to get my head around a solution. Here comes code and messages from the project and the error:
#IBOutlet weak var addButton: UIBarButtonItem!
let listToUsers = "ListToUsers"
var backgroundNr = 0
var items: [GroceryItem] = []
let ref = FIRDatabase.database().reference(withPath: "grocery-items")
let usersRef = FIRDatabase.database().reference(withPath: "online")
var user: User!
var userCountBarButtonItem: UIBarButtonItem!
var counter = 1
override func viewDidLoad() {
super.viewDidLoad()
if user?.email == "tor#gmail.com" {
addButton.isEnabled = false
}
tableView.allowsMultipleSelectionDuringEditing = false
userCountBarButtonItem = UIBarButtonItem(title: "1",
style: .plain,
target: self,
action: #selector(userCountButtonDidTouch))
userCountBarButtonItem.tintColor = UIColor.white
navigationItem.leftBarButtonItem = userCountBarButtonItem
usersRef.observe(.value, with: { snapshot in
if snapshot.exists() {
self.userCountBarButtonItem?.title = snapshot.childrenCount.description
} else {
self.userCountBarButtonItem?.title = "0"
}
})
ref.queryOrdered(byChild: "completed").observe(.value, with: { snapshot in
var newItems: [GroceryItem] = []
for item in snapshot.children {
let groceryItem = GroceryItem(snapshot: item as! FIRDataSnapshot)
newItems.append(groceryItem)
}
self.items = newItems
self.tableView.reloadData()
})
FIRAuth.auth()!.addStateDidChangeListener { auth, user in
guard let user = user else { return }
self.user = User(authData: user)
let currentUserRef = self.usersRef.child(self.user!.uid)
currentUserRef.setValue(self.user!.email)
currentUserRef.onDisconnectRemoveValue()
}
}
// MARK: UITableView Delegate methods
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as! SubjectTableViewCell
let groceryItem = items[indexPath.row]
if backgroundNr == 0 {
cell.backgroundColor = UIColor(red: 222/255, green: 164/255, blue: 50/255, alpha: 0.6)
backgroundNr += 1
} else {
cell.backgroundColor = UIColor.white
backgroundNr -= 1
}
cell.textLabel?.text = groceryItem.name
cell.detailTextLabel?.text = groceryItem.addedByUser
toggleCellCheckbox(cell, isCompleted: groceryItem.completed)
return cell
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let groceryItem = items[indexPath.row]
groceryItem.ref?.removeValue()
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) else { return }
let groceryItem = items[indexPath.row]
let toggledCompletion = !groceryItem.completed
toggleCellCheckbox(cell, isCompleted: toggledCompletion)
groceryItem.ref?.updateChildValues([
"completed": toggledCompletion
])
}
func toggleCellCheckbox(_ cell: UITableViewCell, isCompleted: Bool) {
if !isCompleted {
cell.accessoryType = .none
cell.textLabel?.textColor = UIColor.black
cell.detailTextLabel?.textColor = UIColor.black
} else {
cell.accessoryType = .checkmark
cell.textLabel?.textColor = UIColor.gray
cell.detailTextLabel?.textColor = UIColor.gray
}
}
// MARK: Add Item
#IBAction func addButtonTapped(_ sender: Any) {
let alert = UIAlertController(title: "Kunskaps Område",
message: "Lägg till objekt",
preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Spara",
style: .default) { _ in
// 1
guard let textField = alert.textFields?.first,
let text = textField.text else { return }
// 2
let groceryItem = GroceryItem(name: text,
addedByUser: self.user!.email,
completed: false)
// 3
let groceryItemRef = self.ref.child(text.lowercased())
// 4
groceryItemRef.setValue(groceryItem.toAnyObject())
}
let cancelAction = UIAlertAction(title: "Cancel",
style: .default)
alert.addTextField()
alert.addAction(saveAction)
alert.addAction(cancelAction)
present(alert, animated: true, completion: nil)
}
func userCountButtonDidTouch() {
performSegue(withIdentifier: listToUsers, sender: nil)
}
#IBAction func startClockTapp(_ sender: Any) {
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(GroceryListTableViewController.updateCounter), userInfo: nil, repeats: true)
}
func updateCounter() {
counter += 1
let clock = String(counter)
print(clock)
let clockCurrent = Clock(name: clock, addedByUser: self.user!.email)
let clockCurrentRef = self.ref.child(clock.lowercased())
clockCurrentRef.setValue(clockCurrent.toAnyObject())
}
When using breakpoints to locate where the error occurs, it seems that it is located in the following function: (the first line of code in "UITable view delegate methods)
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
I don't now if I'm using the breakpoints wrong, however that's the result I get, if it helps.
The "grocery" objects struct file´s code is the following:
import Foundation
import Firebase
struct GroceryItem {
let key: String
let name: String
let addedByUser: String
let ref: FIRDatabaseReference?
var completed: Bool
init(name: String, addedByUser: String, completed: Bool, key: String = "") {
self.key = key
self.name = name
self.addedByUser = addedByUser
self.completed = completed
self.ref = nil
}
init(snapshot: FIRDataSnapshot) {
key = snapshot.key
let snapshotValue = snapshot.value as! [String: AnyObject]
name = snapshotValue["name"] as! String
addedByUser = snapshotValue["addedByUser"] as! String
completed = snapshotValue["completed"] as! Bool
ref = snapshot.ref
}
func toAnyObject() -> Any {
return [
"name": name,
"addedByUser": addedByUser,
"completed": completed
]
}
}
The database content on Firebase gets structured as following:
educationlevel-e230e
grocery-items
Fysik:, addedByUser:, completed:
The full string of the basic error message:
fatal error: unexpectedly found nil while unwrapping an Optional value
2016-12-24 01:22:54.799117 EducationLevel[1077:263810]
fatal error:
unexpectedly found nil while unwrapping an Optional value
I really can't find the answer to this problem by my self or anywhere on the web, REALLY appreciates it if I could get some help (sorry for the probably bad english, I'm from Sweden;) )
(If you need more strings from the error message or anything like that I'll immediately update this question)
Thanks!! Tor from Sweden.