Passing documentID of Firestore using segue in Swift - ios

I'm learning both Swift and firebase by making an app. In my current firestore, I have a database with collection name Players, and inside of the collection, there is a document with auto-documentID and two fields, such as name and email. Like this.
firestore image
I also use Firebase Auth, so whenever a user makes an account I put their email into firestore with field name email.
Here is the code for RegisterViewController where a user register their account with email and password.
import UIKit
import Firebase
class RegisterViewController: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
let db = Firestore.firestore()
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: - register a new account
#IBAction func registerPressed(_ sender: UIButton) {
if let email = emailTextField.text, let password = passwordTextField.text {
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
if error != nil {
// some codes here
} else {
// Add a new document with a generated ID
var ref: DocumentReference? = nil
ref = self.db.collection(K.FStore.playersCollection).addDocument(data: [K.FStore.emailField: email, K.FStore.nameField: ""]) { (error) in
if let err = error {
print("There was an issue storing data to firestore. \(err)")
} else {
print("Document added with ID: \(ref!.documentID)")
}
}
// pass this data
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == K.registerToHome {
let homeVC = segue.destination as! HomeViewController
homeVC.documentID = ref?.documentID
}
}
self.performSegue(withIdentifier: K.registerToHome, sender: self)
}
}
}
}
}
I'm planning to check if the user has a name in their document name field in the next HomeViewController, so if the value of the name field in the document is "", I want to display an alert with a textfeild, so the user can add their name. However, in the HomeViewController, I need the documentID because I will use this code, db.collection(K.FStore.playersCollection).document(documentID).getDocument in HomeVC, so I want to pass the user's documentID (in the RegisterVC, I was able to get the value as ref?.documentID, so I just want to pass the value to HomeVC) to HomeVC. I tried using func prepare(for segue: UIStoryboardSegue, sender: Any?), but I was not able to pass the value...

Change your code to :
//Top of the your code define id :
var id = ""
else {
// Add a new document with a generated ID
var ref: DocumentReference? = nil
ref = self.db.collection(K.FStore.playersCollection).addDocument(data: [K.FStore.emailField: email, K.FStore.nameField: ""]) { (error) in
if let err = error {
print("There was an issue storing data to firestore. \(err)")
} else {
print("Document added with ID: \(ref!.documentID)")
self.id = ref!.documentID
self.performSegue(withIdentifier: K.registerToHome, sender: self)
}
}
}
//Out of registerPressed func :
// pass this data
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == K.registerToHome {
let homeVC = segue.destination as! HomeViewController
homeVC.documentID = self.id
}
}

Related

Swift - Pass data from Base ViewController to two Container ViewControllers after loading from Firestore

I know this may be a simple solution but I can't figure it out. I'm trying to load data from Firestore in my Base ViewController to then populate two Container Viewcontrollers at the same time.(I want to do it this way to save on Read Cost) I've been trying to go the segue route but the segue is called before my data is finished loading from Firestore. I need the data to be present to popular the two different Container Viewcontrollers(One container is a chart. The other container is a line graph). Any suggestions would be greatly appreciated.
import UIKit
import Firebase
class BaseViewController: UIViewController {
var db: Firestore!
override func viewDidLoad() {
super.viewDidLoad()
let settings = FirestoreSettings()
Firestore.firestore().settings = settings
db = Firestore.firestore()
if Reachability.isConnectedToNetwork(){
print("Internet Connection Available!")
loadFirestoreData()
} else {
print("Internet Connection not Available!")
}
}
// Load Firestore Data
func loadFirestoreData() {
db.collection("chartGraph").document("companyX")
.getDocument{ (document, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
self.performSegue(withIdentifier: "ChartSegue", sender: document!.data()!)
self.performSegue(withIdentifier: "LineGraphSegue", sender: document!.data()!)
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if identifier == "ChartSegue" {
let vc1 = segue.destinationViewController as? ChartViewController
vc1.dataLoaded(data: (sender as? [String: Any])!)
}
if identifier == "LineGraphSegue" {
let vc2 = segue.destinationViewController as? LineGraphViewController
vc2.dataLoaded(data: (sender as? [String: Any])!)
}
}
}
You need to keep a reference to Container in your MainViewController.
For that you should add instance variables to MainViewController that will hold a reference to the container controllers, not just the view. You'll need to set it in prepareForSegue.
So the beginning of MainViewController look something like this:
class MainViewController: UIViewController {
var containerViewChartController: ChartViewController?
var containerViewLineGraphController: LineGraphViewController?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let controller = segue.destination as? ChartViewController {
containerViewChartController = controller
} else if let controller = segue.destination as? LineGraphViewController {
containerViewLineGraphController = controller
}
}
then you can call container methods like this
func button_Container() {
containerViewChartController?.changeData(yourData)
}

How to pass data in a closure to another scene

It is my first app in swift. I am using Alamofire for my HTTP request. Coming from Android, I know it is possible to attach serialized object to navcontroller action while navigating from one screen to another.
I want to be able to perform segue after from the viewmodel subscription and attach the resultant token to the segue as I will be using it for verification at the next screen.
I have tried didSet but to no avail.
How can I do this in swift.
//MARK: Register user
#IBAction func registerUser(_ sender: Any) {
let fullName = firstNameTF.text! + " " + lastNameTF.text!
let email = emailTF.text
let password = passwordTF.text
let phone = phoneNumberTF.text
let country = countryDropDown.text
let user = User(name: fullName, email: email, password: password, country: country, phone: phone, token: nil)
var tk = ""{
didSet{
token = tk
}
}
authViewModel.registerUser(user: user).subscribe(onNext: { (AuthResponse) in
print("messaage \(String(describing: AuthResponse.message))")
self.tokens = AuthResponse.token
self.performSegue(withIdentifier: "gotoVerification", sender: self)
}, onError: { (Error) in
print("Error: \(Error.localizedDescription)")
}, onCompleted: nil) {
}.disposed(by: disposeBag)
print("token \(token)")
// AF.request(url, method: .post, parameters: user, encoder: JSONParameterEncoder.default).responseDecodable(of:AuthResponse.self){response in
//
// response.map { (AuthResponse) in
// print("messaage \(String(describing: AuthResponse.message))")
// }
//
// print("user: \(user)")
// print("response \(String(describing: response))")
// }
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? UserVerification{
//
vc.tokens = token
print("token \(token)")
}
}
You can pass the token as the sender:
self.performSegue(withIdentifier: "gotoVerification", sender: AuthResponse.token)
Then:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? UserVerification, let token = sender as? String {
vc.tokens = token
print("token \(token)")
}
}

Pass value of email between view controllers [duplicate]

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 4 years ago.
I want to pass the value "Email" from one VC to another when I press the button. So i want create a sign up with firebase and swift 4, first a view controller asks me the email, after entering the email, I click on next, and another view controller will ask me for the password, then click on register. How do I save the email of first ViewController and pass it to the second ViewController? And then send email of first vc and password of second vc to firebase? sorry for my english. In practice it must work like the instagram sign up.
import Foundation
import Firebase
import FirebaseAuth
import UITextField_Shake
internal class SignUpSegue0 : UIViewController {
#IBOutlet weak var labelwrongpass: UILabel!
#IBOutlet weak var Emailsign: UITextField!
func isValidEmail(testStr:String) -> Bool {
print("validate emilId: \(testStr)")
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
let emailTest = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
let result = emailTest.evaluate(with: testStr)
return result
}
#IBAction func Nextsign1(_ sender: UIButton) {
guard let email = Emailsign.text else { return }
if Emailsign.text == "" {
self.labelwrongpass.text = "Please enter your email"
self.Emailsign.shake(10,withDelta: 5.0)
} else {
Auth.auth().fetchProviders(forEmail: email, completion: {
(providers, error) in
if error != nil {
print("Email disponibile")
self.labelwrongpass.text = "Email inserita correttamente"
self.labelwrongpass.textColor = UIColor.green
func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.destination is SignUpSegue1
{
let vc = segue.destination as? SignUpSegue1
vc?.email = ""
}
}
} else if providers != nil {
print("L'email inserita è già esistente")
self.labelwrongpass.text = nil
self.labelwrongpass.text = "L'email inserita è già esistente"
self.Emailsign.shake(10,withDelta: 5.0)
}
})
}
}
}
You can create a var in your destination controller:
class DestinationViewController: UIViewController {
var variableToShare : String?
//the rest of the class methods....
And in your controller override PrepareForSegue and something like:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Create a variable that you want to send
var toShare = "Email"
// Create a new variable to store the instance of DetinationViewController
let destinationVC = segue.destinationViewController as DetinationViewController
destinationVC.variableToSharer = toShare
}
}

How to add data to specific use reference in firebase database?

My current iOS requires the user to sign up and log in and once the user logs in there data is presented in the database by their user.uid and their email address they signed up with. Once the user has logged in, I want to the user to be able to store data at their specific user.uid node. For example, if "User A" is logged in and they enter some type of data, I want the data to be saved just under that user (so each user should contain their own set of data and should not be to access or modify any other users data besides there own). So my question is, what would be the best way to keep track of the logged in user or access the logged in user to store future references in later view controllers outside of just the log in controller? It's a food delivery app, so once a user is logged in, I wanted all future entered data to be saved under that user in the database.
Code for login controller:
import UIKit
import FirebaseDatabase
import FirebaseAuth
class LogInController: UIViewController {
var ref: DatabaseReference!
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
func placeholders() {
emailField.placeholder = "Enter Email"
passwordField.placeholder = "Enter Password"
}
#IBAction func loginButton(_ sender: AnyObject) {
Auth.auth().signIn(withEmail: emailField.text!, password: passwordField.text!, completion: { (user, error) in
let userID: String = (user?.uid)!
let userEmail: String = self.emailField.text!
self.ref.child("Users/\(userID)").setValue(userEmail)
if error != nil {
print(error?.localizedDescription as Any)
}
else {
print("User logged in with UserID of: " + (user?.uid)!)
}
})
performSegue(withIdentifier: "signedIn", sender: self)
}
#IBAction func signoutButton(_ sender: Any) {
print("User has logged out...")
try! Auth.auth().signOut()
}
#IBAction func registerButton(_ sender: AnyObject) {
Auth.auth().createUser(withEmail: emailField.text!, password: passwordField.text!, completion: { (user, error) in
if error != nil {
print(error?.localizedDescription as Any)
return
}
print("User created with UserId of: " + (user?.uid)!)
})
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let flavorsVC = segue.destination as? FlavorsController {
flavorsVC.ref = ref
let userEmail = emailField.text
flavorsVC.email = userEmail!
}
}
override func viewDidLoad() {
super.viewDidLoad()
placeholders()
ref = Database.database().reference()
}
So again, once the user has logged in, I want all data entered to be saved just under that logged in user on following view controllers. For example in the next view controller I have a variable bookieAmount, how could I modify my code to where each logged in user has a separate node where their individual bookieAmount can be saved to? So far when I try to implement such functionality it just replaces the previous reference and doesn't create a new for that specific user like I want it to.
Code for next view controller:
class FlavorsController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var bookieFlavors = ["Chocolate Chip", "Sugar w/o icing", "Sugar w/ icing", "Peanut Butter", "Honey", "Shortbread", "Ginger", "Double Chocolate", "Macadamie Nut", "Oatmeal Raisin", "Snickerdoodle"]
var amount = [Int]()
var bookieTotal = Int()
var ref: DatabaseReference!
var flavorRef: DatabaseReference!
var email = String()
override func viewDidLoad() {
super.viewDidLoad()
for _ in self.bookieFlavors {
self.amount.append(0)
}
flavorTable.delegate = self
flavorTable.dataSource = self
//database references
ref = Database.database().reference()
flavorRef = Database.database().reference()
}
func emptyAmount(_ sender: UIButton) {
print("Button Held, Amount Emptied")
self.amount[sender.tag] = self.amount[sender.tag] - (self.amount[sender.tag] + 1)
let cell = self.flavorTable.cellForRow(at: IndexPath(row: sender.tag, section: 0)) as? FlavorTableCell
cell?.bookieAmount.text = "= \(self.amount[sender.tag])"
}
#IBAction func bookieButton(_ sender: UIButton) {
self.amount[sender.tag] = self.amount[sender.tag] + 1
let cell = self.flavorTable.cellForRow(at: IndexPath(row: sender.tag, section: 0)) as? FlavorTableCell
cell?.bookieAmount.text = "= \(self.amount[sender.tag])"
// print(amount[sender.tag])
self.bookieTotal = amount.reduce(0, +)
print(bookieTotal)
}
#IBOutlet weak var flavorTable: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return bookieFlavors.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! FlavorTableCell
//flavor label configuration
cell.flavorLabel.text = bookieFlavors[indexPath.row]
//amount configuration
cell.bookieAmount.text = "= \(self.amount[indexPath.row])"
cell.bookieButton.tag = indexPath.row
cell.bookieButton.addTarget(self, action: #selector(bookieButton(_:)), for: .touchUpInside)
cell.bookieButton.addTarget(self, action: #selector(emptyAmount(_:)), for: .touchDownRepeat)
return cell
}
#IBAction func registerBookieAmount(_ sender: Any) {
print(bookieTotal)
let amount: Int = bookieTotal
let user = Auth.auth().currentUser
if ((user) != nil) {
}
}
What you can do is to make a request to Firebase server to see if the user is still logged in by this code
if Auth.auth().currentUser != nil {
// User is signed in.
let ref = Database.database().reference()
ref.child("users").child(user.uid).child("orders").setValue(["order": orderNumber])
} else {
// No user is signed in.
}

Swift sending Multiple Objects to View Controller

I am trying to send multiple objects from my initial view controller to my Username VC. Here is the segue code from my controllers: The issue comes when I add in the code to send the second object, termreport. If I delete the termsM and the assignment, it send the students as usually, but I also need to send the termReport object. How would I fix this?
ViewControler:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let students = sender as AnyObject as? [Student]
else { return }
guard let termsM = sender as AnyObject as? [TermReport] //How would I send both objects?
else { return }
if let secondVC = segue.destination as? UsernameVC {
secondVC.students = students
secondVC.userWebView = webView
secondVC.terms = termsM // not sending
}
let gradeResponse = try Parser(innerHTML)
self.performSegue(withIdentifier: "ShowStudents", sender: gradeResponse.students)
self.performSegue(withIdentifier: "ShowStudents", sender: gradeResponse.termReports) //how would I send both variables?
UsernameVC:
var terms: [TermReport]!
override func viewDidLoad() {
print("TERM \(terms[0].grades[3])")//returns found nil optional ERROR
}
You have to include all of the variables you want to send to another ViewController using a segue into a single object (which can be a collection as well). You either create a custom class/struct that has properties with type [Student] and [TermReport] or put these into a native collection (Tuple or Dictionary).
Create custom struct:
struct TermData {
var students = [Student]()
var termReports = [TermReport]()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let segueData = sender as? TermData
else { return }
if let secondVC = segue.destination as? UsernameVC {
secondVC.students = segueData.students
secondVC.userWebView = webView
secondVC.terms = segueData.termReports
}
}
let gradeResponse = try Parser(innerHTML)
let termData = TermData(students: gradeResponse.students, termReports: gradeResponse.termReports)
self.performSegue(withIdentifier: "ShowStudents", sender: termData)

Resources