How to add data to a specific uid in Firestore Database? - ios

I would like some help with the coding on how to store data into a specific user after the user have successfully logged in. Below are the codes for the page where user can input the details of their new readings.
import UIKit
import Firebase
import FirebaseAuth
import FirebaseFirestore
class NewBookViewController: UIViewController {
#IBOutlet weak var bookTitleTextField: UITextField!
#IBOutlet weak var bookAuthorTextField: UITextField!
#IBOutlet weak var bookSummaryTextField: UITextField!
#IBOutlet weak var ratingController: UIView!
#IBOutlet weak var newBookCancelButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
func validateFields() -> String? {
if
bookTitleTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
bookAuthorTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
bookSummaryTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" {
return "Please fill in all the fields."
}
return nil
}
#IBOutlet weak var newBookSaveButton: UIButton!
var ref = Firestore.firestore()
#IBAction func newBookSaveButtonTapped(_ sender: Any) {
let uid = Auth.auth().currentUser?.uid
self.ref?.child("new reading").child(uid).setValue(post)
func post() {
let bookTitleTextField = "bookTitle"
let bookAuthorTextField = "bookAuthor"
let bookSummaryTextField = "bookSummary"
let post : [String : AnyObject] = [ "bookTitle" : bookTitleTextField as AnyObject, "bookAuthor" : bookAuthorTextField as AnyObject, "bookSummary" : bookSummaryTextField as AnyObject]
}
this is the successful user sign up on cloud firestore. after the user have logged in, I wanted to add those 3 data (title, author, summary) FOR the specific user.

It looks like you're close. Right now, you aren't returning anything from post, though. I think you also mean to be getting the text values from each UITextField instead of just declaring Strings with the names of the fields.
#IBAction func newBookSaveButtonTapped(_ sender: Any) {
guard let uid = Auth.auth().currentUser?.uid else {
//handle your error here
return
}
self.ref?.child("new reading").child(uid).setValue(post())
}
func post() -> [String:String] {
return ["bookTitle" : bookTitleTextField.text ?? "",
"bookAuthor" : bookAuthorTextField.text ?? "",
"bookSummary" : bookSummaryTextField.text ?? ""]
}

You should take a much safer approach to handling the user's ID and the values of the text fields. Here, the data is only written to the database if the user is logged in and all 3 of the text fields have strings in them. I don't know what collection you intended to place this document in so I went with what you wrote but I suspect it isn't right.
class NewBookViewController: UIViewController {
private let db = Firestore.firestore()
#IBAction func newBookSaveButtonTapped(_ sender: Any) {
guard let uid = Auth.auth().currentUser?.uid,
let data = bookData() else {
return
}
db.collection("new reading").document(uid).setData(data)
}
// This returns an optional dictionary (nil when the data is incomplete).
// This is entirely optional (pun) but I suspect you don't want
// empty fields in these database documents.
func bookData() -> [String: Any]? {
guard let title = bookTitleTextField.text,
let author = bookAuthorTextField.text,
let summary = bookSummaryTextField.text else {
return nil
}
let data: [String: Any] = [
"bookTitle": title,
"bookAuthor": author,
"bookSummary": summary
]
return data
}
}

Related

How to save user's input in UITextFieldi with firebase?

If I just use the text field and run the app I can enter data in the text field and it stores in the Firebase database but when I close the application the data is gone from the text box , better yet it does not show in the UItextbox, i can type it and click submit to send the information to the server. So how can I show it again after the application is closed and reopened. I am using the cloud store in Firebase btw and using swift to code it in xcode
class viewcontroller5: UIViewController{
#IBOutlet weak var HowManyTextfield: UITextField!
#IBOutlet weak var WhatBrandTextField: UITextField!
#IBOutlet weak var HowOftenTextField: UITextField!
#IBOutlet weak var SubmitButton: UIButton!
// set document refenrence
let db = Firestore.firestore()
override func viewDidLoad() {
super.viewDidLoad()
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(self.swipeAction(swipe:)))
swipeRight.direction = UISwipeGestureRecognizer.Direction.right
self.view.addGestureRecognizer(swipeRight)
}
// Function to get the auto generated document ID
func getDocument(){
let docData : [String:Any] = [
"LastUpdated":FieldValue.serverTimestamp(),
"HoursOfSleep": HowManyTextfield.text! as String,
"BrandOfProducts": WhatBrandTextField.text! as String,
"HowManyTrims":HowOftenTextField.text! as String
]
guard let userID = Auth.auth().currentUser?.uid else {return}
// print(userID)
db.collection("Users").whereField("UID", isEqualTo: userID).getDocuments(){ (querySnapshot, err) in
if let err = err {
print(err.localizedDescription)
return
} else{
for document in querySnapshot!.documents{
if document == document{
print(document.documentID)
//create a profile collection and add the new information
let Profile = self.db.collection("Users").document(document.documentID)
Profile.updateData(docData){
err in
if let err = err{
print("error updating document: \(err)")} else { print("Document sucessfully updated")}
}
}
}
}
}
}
#IBAction func SubmitButton(_ sender: UIButton) {
globalDashboardVC?.FirstPage()
getDocument()
}
}

Query Data from Firebase and write in UITextfield

I'm trying to pull data from my firebase project and write the field value in a UITextfield. I can't seem to figure out how to query the data needed for the UITextfields from the firebase field values correctly.
Is there anyway to do this? I have only seen videos of people adding firebase documents to a tableview, not any for direct pull of a firebase value to a UITextfield.
I've tried:
Switching the constants to instance the UITextfield Outlets and optionally unwrap as text fields let username = data[USERNAME] as? UITextField ?? "anonymous"
Creating variables of the UITextfields and equal the collections array data. So something kind like... let self.username = Userdata[0] but I keep getting "expected pattern error"
I know these may not be appropriate syntax but this is the first time I've ever coded and attempted to make an app, but I can't find any tutorial to follow for this scenario. So any information greatly appreciated, Thanks.
Heres what I've got so far...
import UIKit
import FirebaseDatabase
import Firebase
class ProfileVC: UIViewController {
#IBOutlet weak var userImage: UIImageView!
#IBOutlet weak var username: UITextField!
#IBOutlet weak var firstname: UITextField!
#IBOutlet weak var userEmail: UITextField!
#IBOutlet weak var lastname: UITextField!
#IBOutlet private weak var bgView: UIView!
//variables
private var Userdatas = [Userdata]()
private var usersCollectionRef: CollectionReference!
override func viewDidLoad() {
super.viewDidLoad()
usersCollectionRef = Firestore.firestore().collection(USERS_REF)
}
override func viewWillAppear(_ animated: Bool){
usersCollectionRef.getDocuments { (snapshot, error) in
if let err = error {
debugPrint("Error fetching docs: \(err) ")
} else {
guard let snap = snapshot else{return}
for document in snap.documents {
let data = document.data()
let username = data[USERNAME] as? String ?? "anonymous"
let firstname = data[FIRST_NAME] as? String ?? "Anonymous"
let lastname = data[LAST_NAME] as? String ?? "Anonymous"
let email = data[EMAIL] as? String ?? "Anonymous"
let documentID = document.documentID
let newuserData = Userdata(username: username, email: email, firstname: firstname, lastname: lastname, documentID: documentID)
}
}
}
}

App crashes at click with message: 'this class is not key value coding-compliant for the key postReplyButton.'

My app crashes when I click a cell in my tableView of recent posts. The click is supposed to segue me to the MainTextView which has the postReplyButton. The segue worked until I started experimenting with creating comments for the posts.
Here is the MainTextView code:
import Foundation
import UIKit
import Firebase
class MainTextView: UIViewController {
#IBOutlet weak var titleText: UILabel!
#IBOutlet weak var mainText: UILabel!
#IBOutlet weak var commentPlaceHolder: UILabel!
#IBOutlet weak var newCommentLabel: UITextView!
var delegate:NewPostVCDelegate?
#IBAction func postReplyButton() {
// Firebase code here
let postRef = Database.database().reference().child("posts").childByAutoId()
let postObject = [
"comment": newCommentLabel.text,
"timestamp": [".sv": "timestamp"]
] as [String : Any]
postRef.setValue(postObject, withCompletionBlock: { error, ref in
if error == nil {
self.delegate!.didUploadPost(withID: ref.key!)
self.dismiss(animated: true, completion: nil)
} else {
// Handle error
}
})
newCommentLabel.text = String()
commentPlaceHolder.isHidden = false
}
var post: Post?
// MARK: - View Controller LifeCycle
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.setMain()
}
override func viewDidLoad() {
super.viewDidLoad()
newCommentLabel.delegate = self as! UITextViewDelegate
}
private func setMain() {
guard let post = self.post else {
return
}
titleText.text = post.text
mainText.text = post.title
}
func textViewDidChange(_commentView: UITextView) {
commentPlaceHolder.isHidden = !newCommentLabel.text.isEmpty
}
}
For reference, here is my Post class code:
import Foundation
class Post {
var id:String
var title: String
var text:String
var createdAt:Date
var comment: [String] = []
init(id: String, title: String,text:String, timestamp:Double, comment: [String] = []) {
self.id = id
self.title = title
self.text = text
self.createdAt = Date(timeIntervalSince1970: timestamp / 1000)
}
static func parse(_ key:String, data:[String:Any]) -> Post? {
if let title = data["text"] as? String,
let text = data["title"] as? String,
let timestamp = data["timestamp"] as? Double {
return Post(id: key, title: title, text: text, timestamp:timestamp, comment: [])
}
return nil
}
}
I suspect the issue may be with the delegate, which was declared as such in my NewPostViewController:
protocol NewPostVCDelegate {
func didUploadPost(withID id:String)
}
I have tried troubleshooting the storyboard, but everything seems to be in place. Is there an issue of the reuse of the protocol or perhaps the change of adding comments to the Post class itself? Maybe the issue is that I do not in fact want to upload a new post, but really I just want to add a comment to an existing post. If this is the case, how would I change the delegate or create a new one? I can provide more detail if needed. Thank you for your help.
This usually happens if you have an IBOutlet that was created previously with the same postReplyButton name. To check if your app has any other Outlet with the same name go to the Search section in your project and search for postReplyButton and see if you get multiple results for that name. If you do then click on the one which you don't need and delete it from the properties section.
If you have any Outlet which has a bad connection you will see something like this in the properties sections when you click on any one of the search result for postReplyButton
If that does not work then try renaming the Outlet entirely and see if that fixes the problem.
EDITED:
For your issue that you mentioned in the comments try this.
Instead of casting your newCommentLabel as an optional type of UITextViewDelegate just extend your viewController to conform to UITextViewDelegate. This should solve the issue.
class MainTextView: UIViewController, UITextViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
newCommentLabel.delegate = self
}
}
Once you add UITextViewDelegate to your viewController you will no longer get the warning in viewDidLoad to cast newCommentLabel as an optional of type UITextViewDelegate.

Adding Comment Replies to Posts as an Array of Strings in Class "Post"

I have created an app where users can generate posts that are added to a postTableView. Users can then click on any of the cells of postTableView to go to a unique view with the title and text of the post along with a commentTableView filled with user generated comments. Below the commentTableView is a textView that you can write your comment in and a button allowing you to submit your comment. I am trying to code my app so that when you press the button, the text that you wrote in the textView is appended to an array of unique comments for that post. Those comments populate the commentTableView. The following is my current flawed attempt:
Here is the Post Class:
import Foundation
class Post {
var id:String
var title: String
var text:String
var createdAt:Date
var comment: [String] = []
init(id: String, title: String,text:String, timestamp:Double, comment: [String] = []) {
self.id = id
self.title = title
self.text = text
self.createdAt = Date(timeIntervalSince1970: timestamp / 1000)
}
static func parse(_ key:String, data:[String:Any]) -> Post? {
if let title = data["text"] as? String,
let text = data["title"] as? String,
let timestamp = data["timestamp"] as? Double {
return Post(id: key, title: title, text: text, timestamp:timestamp, comment: [])
}
return nil
}
}
Here is my current view controller that you get when you click on any of the cells from the postTableView:
import Foundation
import UIKit
import Firebase
class MainTextView: UIViewController {
#IBOutlet weak var titleText: UILabel!
#IBOutlet weak var mainText: UILabel!
#IBOutlet weak var commentPlaceHolder: UILabel!
#IBOutlet weak var newCommentLabel: UITextView!
var delegate:NewPostVCDelegate?
#IBAction func postReplyButton() {
// Firebase code here
let postRef = Database.database().reference().child("posts").childByAutoId()
let postObject = [
"comment": newCommentLabel.text,
"timestamp": [".sv": "timestamp"]
] as [String : Any]
postRef.setValue(postObject, withCompletionBlock: { error, ref in
if error == nil {
self.delegate!.didUploadPost(withID: ref.key!)
self.dismiss(animated: true, completion: nil)
} else {
// Handle error
}
})
newCommentLabel.text = String()
commentPlaceHolder.isHidden = false
}
var post: Post?
// MARK: - View Controller LifeCycle
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.setMain()
}
override func viewDidLoad() {
super.viewDidLoad()
newCommentLabel.delegate = self as! UITextViewDelegate
}
private func setMain() {
guard let post = self.post else {
return
}
titleText.text = post.text
mainText.text = post.title
}
func textViewDidChange(_commentView: UITextView) {
commentPlaceHolder.isHidden = !newCommentLabel.text.isEmpty
}
}
How can I fix my errors and programmatically execute my vision of populating my comment section with user for each post?
For
Class 'MainTextView' has no initializers
Replace
var delegate:NewPostVCDelegate
with
var delegate:NewPostVCDelegate?

Firebase Database not Uploading

I have my upload code here
import UIKit
import Firebase
class ChatViewController: UIViewController {
let chatRef = FIRDatabase.database().reference().child("chat")
let userUid = FIRAuth.auth()?.currentUser?.uid
var userName = ""
#IBOutlet weak var topBar: UINavigationItem!
#IBOutlet weak var containerView: UIView!
#IBOutlet var inputTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
topBar.title = "Chat Log Controller"
FIRDatabase.database().reference().child("users/\(userUid!)/name").observe(.value) { (snap: FIRDataSnapshot) in
self.userName = (snap.value! as! String).description
}
}
#IBAction func handleSend(_ sender: AnyObject) {
let childChatRef = chatRef.childByAutoId()
let message = inputTextField.text!
childChatRef.child("text").setValue(message)
print(inputTextField.text)
}
#IBAction func handleSendByEnter(_ sender: AnyObject) {
let childChatRef = chatRef.childByAutoId()
let message = inputTextField.text!
print(userName)
childChatRef.child("name").setValue(userName)
childChatRef.child("text").setValue(message)
print(inputTextField.text)
}
}
text is successfully uploaded But
It doesn't print userName and doesn't upload it to Firebase Database
But username is nut nil!
Try to use your observer code as,
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
}
Just take self.username = snap.value! as! String
It will solve your problem.

Resources