Show posts from TableView only from current user login - ios

I have a viewcontroller displaying some post using a tableview and cells. All the datas are being fetched from Firebase ( Database and Users).
I have a login screen before to arrive to the feedview.
I'd like to show only the pots from the current user login in that tableview controller. How is that possible ?
My database is build like this:
For my Signin Page, I am using the following:
//
// ViewController.swift
// MobileAppDemo
//
// Created by Mikko Hilpinen on 27.10.2016.
// Copyright © 2016 Mikkomario. All rights reserved.
//
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
import Firebase
import FirebaseAuth
import SwiftKeychainWrapper
fileprivate struct RegisterInfo
{
let email: String
let password: String
}
class SignInVC: UIViewController
{
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
override func viewDidLoad()
{
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool)
{
if User.currentUserId != nil
{
print("AUTH: USING EXISTING KEYCHAIN")
User.startTrackingCurrentUser()
//performSegue(withIdentifier: "ToFeed", sender: nil)
}
else
{
print("AUTH: NO EXSTING KEYCHAIN")
}
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
print("AUTH: Preparing for segue \(segue.identifier)")
if let registrationVC = segue.destination as? RegisterVC
{
print("AUTH: Found registration VC")
if let info = sender as? RegisterInfo
{
print("AUTH: Sending email (\(info.email)) and password (\(info.password.characters.count) chars) information: ")
registrationVC.setBaseInfo(email: info.email, password: info.password)
}
}
}
#IBAction func signInButtonPressed(_ sender: UIButton)
{
if let email = emailField.text, let password = passwordField.text
{
FIRAuth.auth()?.signIn(withEmail: email, password: password)
{
(user, error) in
if let error = error
{
// TODO: Handle other errors here as well
switch FIRAuthErrorCode(rawValue: error._code)!
{
case .errorCodeUserNotFound:
print("AUTH: USER NOT FOUND -> CREATING NEW USER")
print("AUTH: Sending email \(email) and password \(password.characters.count) characters")
self.performSegue(withIdentifier: "RegisterUser", sender: RegisterInfo(email: email, password: password))
default: print("AUTH: ERROR IN EMAIL LOGIN \(error)") // TODO: Inform user
}
}
else
{
print("AUTH: EMAIL AUTH SUCCESSFUL")
User.currentUserId = user?.uid
User.startTrackingCurrentUser()
self.performSegue(withIdentifier: "ToFeed", sender: nil)
}
}
}
// TODO: Inform user that the field contents are missing
}
fileprivate func firebaseAuth(with credential: FIRAuthCredential)
{
if FIRAuth.auth() == nil
{
print("AUTH: NO AUTH SERVICE AVAILABLE")
}
FIRAuth.auth()?.signIn(with: credential)
{
(user, error) in
if let error = error
{
print("AUTH: UNABLE TO AUTHENTICATE TO FIREBASE")
print("AUTH: \(error)")
}
else
{
if let user = user
{
print("AUTH: SUCCESSFULLY AUTHENTICATED WITH FIREBASE")
// Updates current user data
var userName = "User"
var image: UIImage?
if let retrievedName = user.displayName
{
userName = retrievedName
}
if let retrievedImageUrl = user.photoURL
{
if let data = try? Data(contentsOf: retrievedImageUrl)
{
image = UIImage(data: data)
}
}
User.post(uid: user.uid, provider: user.providerID, userName: userName, image: image)
{
user in
User.currentUser = user
User.startTrackingCurrentUser()
self.performSegue(withIdentifier: "ToFeed", sender: nil)
}
}
}
}
}
}
To display my data in the table view, I'm using this view controller:
//
// Feed.swift
// MobileAppDemo
//
// Created by Mikko Hilpinen on 31.10.2016.
// Copyright © 2016 Mikkomario. All rights reserved.
//
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import SwiftKeychainWrapper
import SwiftyJSON
var posts = [Post]()
var selectedIndexPath: Int = 0
class FeedVC: UIViewController, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITableViewDelegate {
#IBOutlet weak var feedTableView: UITableView!
private var readPosts: ObserveTask?
override func viewDidLoad() {
super.viewDidLoad()
feedTableView.dataSource = self
feedTableView.delegate = self
readPosts = Post.observeList(from: Post.parentReference.queryOrdered(byChild: Post.PROPERTY_CREATED)) {
observedPosts in
posts = observedPosts.reversed()
self.feedTableView.reloadData()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.feedTableView.dequeueReusableCell(withIdentifier: "MessageCell")! as UITableViewCell
let imageView = cell.viewWithTag(1) as! UIImageView
let titleLabel = cell.viewWithTag(2) as! UILabel
titleLabel.text = posts[indexPath.row].title
titleLabel.numberOfLines = 0
Storage.getImage(with: posts[indexPath.row].imageUrl){
postPic in
imageView.image = postPic
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedIndexPath = indexPath.row
self.performSegue(withIdentifier: "push", sender: self)
self.feedTableView.reloadData()
}
}
and finally for my Cells:
//
// MessageCell.swift
// MobileAppDemo
//
// Created by Mikko Hilpinen on 31.10.2016.
// Copyright © 2016 Mikkomario. All rights reserved.
//
import UIKit
import FirebaseStorage
class MessageCell: UITableViewCell
{
#IBOutlet weak var messageImageView: UIImageView!
#IBOutlet weak var messageTextView: UITextView!
#IBOutlet weak var titleTextView: UITextView!
#IBOutlet weak var linkbutton: UIButton!
private var post: Post!
func configureCell(tableView: UITableView, post: Post)
{
self.post = post
// Basic info
titleTextView.text = post.title
messageTextView.text = post.caption
// Post user
User.get(id: post.creatorId)
{
postCreator in
}
// Image
Storage.getImage(with: post.imageUrl)
{
postPic in
self.messageImageView.image = postPic
// Row height changes so table needs to be reset
tableView.beginUpdates()
tableView.endUpdates()
}
}
}
If anybody have any clue / suggestion how I can achieve it ?? It will be incredible :) :)
Thanks a lot !!!

With your Posts database reference with queryOrdered(byChild:) add queryEqual(toValue:) and compare it with User.currentUserId.
readPosts = Post.observeList(from: Post.parentReference
.queryOrdered(byChild: Post.PROPERTY_CREATED)
.queryEqual(toValue: User.currentUserId)) {
observedPosts in
posts = observedPosts.reversed()
self.feedTableView.reloadData()
}

Related

Pass Firebase data from UITableViewCell to ViewController

First post so apologies for anything I do wrong here.
I've been stuck on this for awhile now. I think the problem is pretty straight forward, but I seem to be missing something. The code below is from my homeVC which has a tableview. I created a tableViewCell as well. I have firebase hooked up and the data saves properly. Overall goal is to create a recipe manager. Home screen has a list of recipes, you can add and edit. When you click on the recipe name in the homeVC table you are taken to ShowDataVC. I am able to load the recipe name into the tableview but cant seem to get it to load when selected to the ShowDataVC. Ultimately I think I want to be able to use the UUID I created for each recipe to display all info, handling edits, handling error state if no recipe is found for that ID.
Thanks in advance!
struct RecipeData {
let user: String
let recipeName: String
let ingredientsText: String?
let directionsText: String?
let servingsNumber: Int?
let id = UUID().uuidString
}
import UIKit
import Firebase
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var table: UITableView!
#IBOutlet weak var logout: UIBarButtonItem!
#IBOutlet weak var add: UIBarButtonItem!
let db = Firestore.firestore()
var id = UUID().uuidString
var data = [RecipeData]()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
table.delegate = self
table.dataSource = self
navigationItem.hidesBackButton = true
table.register(UINib(nibName: D.cellNibName, bundle: nil), forCellReuseIdentifier: D.cellIdentifier)
loadRecipeNames()
}
func loadRecipeNames() {
db.collection(D.FStore.collectionName)
.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[D.FStore.userField] as? String,
let recipeNameLabels = data[D.FStore.recipeTextField] as? String {
//print("This is = \(document.documentID) => \(document.data())")
let newRecipe = RecipeData(user: user, recipeName: recipeNameLabels, ingredientsText: nil, directionsText: nil, servingsNumber: nil)
self.data.append(newRecipe)
DispatchQueue.main.async {
self.table.reloadData()
let indexPath = IndexPath(row: self.data.count - 1, section: 0)
self.table.scrollToRow(at: indexPath, at: .top, animated: true)
}
}
}
}
}
}
#IBAction func logoutPressed(_ sender: UIBarButtonItem) {
do {
try Auth.auth().signOut()
navigationController?.popToRootViewController(animated: true)
} catch let signOutError as NSError {
print("Error signing out: %#", signOutError)
}
}
#IBAction func addPressed(_ sender: UIBarButtonItem) {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: D.addRecipeSegue) as! AddRecipeViewController
navigationController?.pushViewController(vc, animated: true)
}
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: D.cellIdentifier, for: indexPath) as! RecipeNameCell
cell.label.text = data.recipeName
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "ShowSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowSegue" {
if let indexPath = table.indexPathForSelectedRow {
let vc = segue.destination as! ShowRecipeDataViewController
vc.newData = data[indexPath.row]
}
}
}
}
import UIKit
import Firebase
class AddRecipeViewController: UIViewController, UIImagePickerControllerDelegate & UINavigationControllerDelegate {
#IBOutlet weak var recipeNameTextField: UITextField!
#IBOutlet weak var ingredientsTextField: UITextView!
#IBOutlet weak var directionsTextField: UITextView!
#IBOutlet weak var stepper: UIStepper!
#IBOutlet weak var numServingLabel: UILabel!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var takePicture: UIButton!
#IBOutlet weak var saveButton: UIBarButtonItem!
let db = Firestore.firestore()
var data: [RecipeData] = []
var stepperValue: Int = 0
var id = UUID().uuidString
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .lightGray
}
#IBAction func savePressed(_ sender: UIBarButtonItem) {
if let text = recipeNameTextField.text, !text.isEmpty {
saveData(text: text)
print("Data successfully saved!")
}
}
func saveData(text: String) {
if let recipeName = recipeNameTextField.text,
let addedIngredients = ingredientsTextField.text,
let directionsText = directionsTextField.text,
let servingsNum = numServingLabel.text,
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
]) { err in
if let err = err {
print("Error adding document: \(err)")
} else {
print("Document added with ID:\(newRecipeRef)")
}
}
}
}
#IBAction func takePicturePressed(_ sender: UIButton) {
let picker = UIImagePickerController()
picker.sourceType = .camera
picker.delegate = self
picker.allowsEditing = true
present(picker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion: nil)
guard let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage else {
return
}
guard let imageData = image.pngData() else {
return
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
#IBAction func stepperPressed(_ sender: UIStepper) {
stepperValue = Int(sender.value)
numServingLabel.text = "\(stepperValue)"
}
}
import UIKit
import Firebase
class ShowRecipeDataViewController: UIViewController {
#IBOutlet weak var recipeNameLabel: UILabel!
#IBOutlet weak var ingredientsText: UILabel!
#IBOutlet weak var directionsText: UILabel!
#IBOutlet weak var numServings: UILabel!
#IBOutlet weak var logout: UIBarButtonItem!
let db = Firestore.firestore()
var newData = [RecipeData]()
var data: [RecipeData] = []
var id = UUID().uuidString
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemOrange
}
#IBAction func logoutPressed() {
do {
try Auth.auth().signOut()
navigationController?.popToRootViewController(animated: true)
} catch let signOutError as NSError {
print("Error signing out: %#", signOutError)
}
}
}
Update
You'll need to assign the values to your outlets. You can do that like so.
class ShowRecipeDataViewController: UIViewController {
#IBOutlet weak var recipeNameLabel: UILabel!
#IBOutlet weak var ingredientsText: UILabel!
#IBOutlet weak var directionsText: UILabel!
#IBOutlet weak var numServings: UILabel!
#IBOutlet weak var logout: UIBarButtonItem!
let db = Firestore.firestore()
var newData: RecipeData? = nil
var data: [RecipeData] = []
var id = UUID().uuidString
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemOrange
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let newData = newData else {
return
}
recipeNameLabel.text = newData.recipeName
ingredientsText.text = newData.ingredientsText
directionsText.text = newData.directionsText
numServings.text = "\(newData.servingsNumber)"
}
#IBAction func logoutPressed() {
do {
try Auth.auth().signOut()
navigationController?.popToRootViewController(animated: true)
} catch let signOutError as NSError {
print("Error signing out: %#", signOutError)
}
}
}
Original Answer
You'll need to share your ShowRecipeDataViewController code to get a better answer. But, the problem is probably there. But if I had to guess you'll need to add the code to tell the textField or Label to have the data in it.
Often, people do that in the ViewDidLoad function like:
// inside ShowRecipeDataViewController
override func viewDidLoad() {
super.viewDidLoad()
recipeLabel.text = data.recipeName
ingredientsTextField.text = data.ingredients
}
// etc.,

How to print data in empty array

I'm trying to print the chat array that is declared as a empty global variable in a table. The data that I'm trying to print is received using web sockets. I'm assigning the data in the messageReceived function, and I know that the data is getting to the program because I'm printing in a label, but the moment that I'm trying to print it in the table is simple not working. All of this is in the ViewController.swift:
import UIKit
import Starscream
var messagetext: String = ""
var tabletext: String = ""
var chat = [String] ()
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
var socket = WebSocket(url: URL(string: "ws://localhost:1337/")!, protocols: ["chat"])
#IBOutlet weak var chatMessage: UILabel!
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var tableView: UITableView!
#IBAction func buttonClick(_ sender: Any) {
messagetext = textField.text!
sendMessage(messagetext)
}
override func viewDidLoad() {
super.viewDidLoad()
self.textField.delegate = self
socket.delegate = self
socket.connect()
navigationItem.hidesBackButton = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
func textFieldDidEndEditing(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return (true)
}
deinit{
socket.disconnect(forceTimeout: 0)
socket.delegate = nil
}
}
// MARK: - FilePrivate
fileprivate extension ViewController {
func sendMessage(_ messager: String) {
socket.write(string: messager)
}
func messageReceived(_ message: String) {
chatMessage.text = message
chat.append(message)
}
}
// MARK: - WebSocketDelegate
extension ViewController : WebSocketDelegate {
public func websocketDidConnect(_ socket: Starscream.WebSocket) {
}
public func websocketDidDisconnect(_ socket: Starscream.WebSocket, error: NSError?) {
performSegue(withIdentifier: "websocketDisconnected", sender: self)
}
public func websocketDidReceiveMessage(_ socket: Starscream.WebSocket, text: String) {
// 1
guard let data = text.data(using: .utf16),
let jsonData = try? JSONSerialization.jsonObject(with: data),
let jsonDict = jsonData as? [String: Any],
let messageType = jsonDict["type"] as? String else {
return
}
// 2
if messageType == "message",
let messageData = jsonDict["data"] as? [String: Any],
let messageText = messageData["text"] as? String {
messageReceived(messageText)
}
}
public func websocketDidReceiveData(_ socket: Starscream.WebSocket, data: Data) {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return(chat.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text = chat[indexPath.row] as! String
return(cell)
}
}
Assuming that you are sure about there is data to be received by your view controller, The issue would be: the tableview data source methods are called before receiving any data, which means chat data source array is still empty, thus there is no data to display.
The solution for your case is to make sure to reload the tableview after receiving data (updating the value of chat data source array), which means in your case after appending a message to chat in messageReceived method by calling reloadData() UITableView instance method:
func messageReceived(_ message: String) {
chatMessage.text = message
chat.append(message)
// here we go:
tableView.reloadData()
}
In your message received handler, issue a tableview.reloadData()
Cheers!
You need to tell the tableview that there is new data. You also need to allow for the fact that the network operation probably occurred on a background queue and UI updates must be on the main queue:
func messageReceived(_ message: String) {
DispatchQueue.main.async {
let newRow = IndexPath(row: chat.count, section:0)
chatMessage.text = message
chat.append(message)
tableView.insertRows(at:[newRow],with: .automatic)
}
}

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

ios Firebase - segue data in a "details post" Controller

Here is my situation:
I have a controller "Feed" which list multiple posts via a table ( a title and image) from Firebase.
On touch of a button, it bring to a "Feed Details" controller, where I would like the data ( title image and caption) from the post clicked previously (parent) being display. ( see screenshot 2)
At the moment nothing is being fetch when I arrive to the feed details controllers ...
How is it possible to fetch the details from the item click previously ??
Currently this is my feed controller:
//
// FeedVC.swift
// MobileAppDemo
//
// Created by Mikko Hilpinen on 31.10.2016.
// Copyright © 2016 Mikkomario. All rights reserved.
//
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import SwiftKeychainWrapper
import SwiftyJSON
class FeedVC: UIViewController, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate
{
#IBOutlet weak var addImageView: UIImageView!
#IBOutlet weak var feedTableView: UITableView!
#IBOutlet weak var titleInputView: InputTextView!
#IBOutlet weak var captionInputView: InputTextView!
private var posts = [Post]()
private var imagePicker = UIImagePickerController()
private var imageSelected = false
private var readPosts: ObserveTask?
override func viewDidLoad()
{
super.viewDidLoad()
imagePicker.delegate = self
imagePicker.allowsEditing = true
feedTableView.dataSource = self
feedTableView.rowHeight = UITableViewAutomaticDimension
feedTableView.estimatedRowHeight = 320
readPosts = Post.observeList(from: Post.parentReference.queryOrdered(byChild: Post.PROPERTY_CREATED))
{
posts in
self.posts = posts.reversed()
self.feedTableView.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
{
if let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as? MessageCell
{
let post = posts[indexPath.row]
cell.configureCell(tableView: tableView, post: post)
return cell
}
else
{
fatalError()
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let image = info[UIImagePickerControllerEditedImage] as? UIImage
{
addImageView.image = image
imageSelected = true
}
picker.dismiss(animated: true, completion: nil)
}
#IBAction func selectImagePressed(_ sender: AnyObject)
{
present(imagePicker, animated: true, completion: nil)
}
#IBAction func postButtonPressed(_ sender: AnyObject)
{
guard let caption = captionInputView.text, !caption.isEmpty else
{
// TODO: Inform the user
print("POST: Caption must be entered")
return
}
guard let title = titleInputView.text, !title.isEmpty else
{
// TODO: Inform the user
print("POST: title must be entered")
return
}
guard let image = addImageView.image, imageSelected else
{
print("POST: Image must be selected")
return
}
guard let currentUserId = User.currentUserId else
{
print("POST: Can't post before logging in")
return
}
imageSelected = false
addImageView.image = UIImage(named: "add-image")
captionInputView.text = nil
titleInputView.text = nil
// Uploads the image
if let imageData = UIImageJPEGRepresentation(image, 0.2)
{
let imageUid = NSUUID().uuidString
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"
Storage.REF_POST_IMAGES.child(imageUid).put(imageData, metadata: metadata)
{
(metadata, error) in
if let error = error
{
print("STORAGE: Failed to upload image to storage \(error)")
}
if let downloadURL = metadata?.downloadURL()?.absoluteString
{
// Caches the image for faster display
Storage.imageCache.setObject(image, forKey: downloadURL as NSString)
print("STORAGE: Successfully uploaded image to storage")
_ = Post.post(caption: caption, title: title, imageUrl: downloadURL, creatorId: currentUserId)
}
}
}
}
#IBAction func signOutButtonPressed(_ sender: AnyObject)
{
// Doesn't listen to posts anymore
readPosts?.stop()
try! FIRAuth.auth()?.signOut()
User.currentUserId = nil
dismiss(animated: true, completion: nil)
}
}
I'm using the code for the feeddetails - Which of course doesn't feet the data
My database structure look like below:
How is it possible to fetch the data in that details view ?? If anybody could explain me the process, it will be really fantastic !!
Thank you for all your time and help !!
----- EDIT: --------
Thanks to Retterdesdialogs, I've update the code and the App start, but when i click on the button linkbutton nothin happen and the App crash:
The Console display:
Could not cast value of type 'UIViewController' (0x1087f6758) to 'MobileAppDemo.FeedDetailsController' (0x104bc34a0).
I'm having now for code:
feedVC.swift ( there all the post are listed):
//
// FeedVC.swift
// MobileAppDemo
//
// Created by Mikko Hilpinen on 31.10.2016.
// Copyright © 2016 Mikkomario. All rights reserved.
//
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import SwiftKeychainWrapper
import SwiftyJSON
class FeedVC: UIViewController, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate
{
#IBOutlet weak var addImageView: UIImageView!
#IBOutlet weak var feedTableView: UITableView!
#IBOutlet weak var titleInputView: InputTextView!
#IBOutlet weak var linkbutton: UIButton!
#IBOutlet weak var captionInputView: InputTextView!
private var posts = [Post]()
private var imagePicker = UIImagePickerController()
private var imageSelected = false
private var readPosts: ObserveTask?
override func viewDidLoad()
{
super.viewDidLoad()
imagePicker.delegate = self
imagePicker.allowsEditing = true
feedTableView.dataSource = self
feedTableView.rowHeight = UITableViewAutomaticDimension
feedTableView.estimatedRowHeight = 320
readPosts = Post.observeList(from: Post.parentReference.queryOrdered(byChild: Post.PROPERTY_CREATED))
{
posts in
self.posts = posts.reversed()
self.feedTableView.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
// here you need to add
{
if let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as? MessageCell
{
let post = posts[indexPath.row]
cell.configureCell(tableView: tableView, post: post)
cell.linkbutton.tag = indexPath.row
cell.linkbutton.addTarget(self, action: #selector(FeedVC.toFeedDetailAction(_:)), for: .touchUpInside)
return cell
}
else
{
fatalError()
}
}
func toFeedDetailAction(_ sender: UIButton) {
let FeedDetailsController = self.storyboard?.instantiateViewController(withIdentifier: "FeedDetailsIdentifier") as! FeedDetailsController
FeedDetailsController.post = posts[sender.tag]
self.navigationController?.pushViewController(FeedDetailsController, animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let image = info[UIImagePickerControllerEditedImage] as? UIImage
{
addImageView.image = image
imageSelected = true
}
picker.dismiss(animated: true, completion: nil)
}
#IBAction func selectImagePressed(_ sender: AnyObject)
{
present(imagePicker, animated: true, completion: nil)
}
#IBAction func postButtonPressed(_ sender: AnyObject)
{
guard let caption = captionInputView.text, !caption.isEmpty else
{
// TODO: Inform the user
print("POST: Caption must be entered")
return
}
guard let title = titleInputView.text, !title.isEmpty else
{
// TODO: Inform the user
print("POST: title must be entered")
return
}
guard let image = addImageView.image, imageSelected else
{
print("POST: Image must be selected")
return
}
guard let currentUserId = User.currentUserId else
{
print("POST: Can't post before logging in")
return
}
imageSelected = false
addImageView.image = UIImage(named: "add-image")
captionInputView.text = nil
titleInputView.text = nil
// Uploads the image
if let imageData = UIImageJPEGRepresentation(image, 0.2)
{
let imageUid = NSUUID().uuidString
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"
Storage.REF_POST_IMAGES.child(imageUid).put(imageData, metadata: metadata)
{
(metadata, error) in
if let error = error
{
print("STORAGE: Failed to upload image to storage \(error)")
}
if let downloadURL = metadata?.downloadURL()?.absoluteString
{
// Caches the image for faster display
Storage.imageCache.setObject(image, forKey: downloadURL as NSString)
print("STORAGE: Successfully uploaded image to storage")
_ = Post.post(caption: caption, title: title, imageUrl: downloadURL, creatorId: currentUserId)
}
}
}
}
#IBAction func signOutButtonPressed(_ sender: AnyObject)
{
// Doesn't listen to posts anymore
readPosts?.stop()
try! FIRAuth.auth()?.signOut()
User.currentUserId = nil
dismiss(animated: true, completion: nil)
}
}
and the FeedDetails.swift ( where the Detail are listed once you have clicked the button, button has class: linkbutton from the viewVC Controller:
//
// FeedDetailsController.swift
// MobileAppDemo
//
// Created by Mikko Hilpinen on 27.10.2016.
// Copyright © 2016 Mikkomario. All rights reserved.
//
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
import Firebase
import FirebaseAuth
import SwiftKeychainWrapper
fileprivate struct RegisterInfo
{
let email: String
let password: String
}
class FeedDetailsController: UIViewController
{
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
var post: Post!
override func viewDidLoad()
{
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool)
{
if User.currentUserId != nil
{
print("AUTH: USING EXISTING KEYCHAIN")
User.startTrackingCurrentUser()
performSegue(withIdentifier: "ToFeed", sender: nil)
}
else
{
print("AUTH: NO EXSTING KEYCHAIN")
}
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
print("AUTH: Preparing for segue \(segue.identifier)")
if let registrationVC = segue.destination as? RegisterVC
{
print("AUTH: Found registration VC")
if let info = sender as? RegisterInfo
{
print("AUTH: Sending email (\(info.email)) and password (\(info.password.characters.count) chars) information: ")
registrationVC.setBaseInfo(email: info.email, password: info.password)
}
}
}
#IBAction func signInButtonPressed(_ sender: UIButton)
{
if let email = emailField.text, let password = passwordField.text
{
FIRAuth.auth()?.signIn(withEmail: email, password: password)
{
(user, error) in
if let error = error
{
// TODO: Handle other errors here as well
switch FIRAuthErrorCode(rawValue: error._code)!
{
case .errorCodeUserNotFound:
print("AUTH: USER NOT FOUND -> CREATING NEW USER")
print("AUTH: Sending email \(email) and password \(password.characters.count) characters")
self.performSegue(withIdentifier: "RegisterUser", sender: RegisterInfo(email: email, password: password))
default: print("AUTH: ERROR IN EMAIL LOGIN \(error)") // TODO: Inform user
}
}
else
{
print("AUTH: EMAIL AUTH SUCCESSFUL")
User.currentUserId = user?.uid
User.startTrackingCurrentUser()
self.performSegue(withIdentifier: "ToFeed", sender: nil)
}
}
}
// TODO: Inform user that the field contents are missing
}
#IBAction func facebookButtonPressed(_ sender: UIButton)
{
// (Already logged in to FB)
if let fbAccessToken = FBSDKAccessToken.current()
{
print("AUTH: Already logged in to FB")
firebaseAuth(with: FIRFacebookAuthProvider.credential(withAccessToken: fbAccessToken.tokenString))
}
else
{
let facebookLogin = FBSDKLoginManager()
facebookLogin.logIn(withReadPermissions: ["public_profile", "email"], from: self)
{
(result, error) in
if let error = error
{
print("AUTH: UNABLE TO AUTHENTICATE WITH FACEBOOK")
print("AUTH: \(error)")
}
else if let result = result
{
if result.isCancelled
{
print("AUTH: USER CANCELLED FACEBOOK AUTH")
}
else
{
print("AUTH: FACEBOOK AUTH SUCCESS")
let credential = FIRFacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
self.firebaseAuth(with: credential)
}
}
}
}
}
fileprivate func firebaseAuth(with credential: FIRAuthCredential)
{
if FIRAuth.auth() == nil
{
print("AUTH: NO AUTH SERVICE AVAILABLE")
}
FIRAuth.auth()?.signIn(with: credential)
{
(user, error) in
if let error = error
{
print("AUTH: UNABLE TO AUTHENTICATE TO FIREBASE")
print("AUTH: \(error)")
}
else
{
if let user = user
{
print("AUTH: SUCCESSFULLY AUTHENTICATED WITH FIREBASE")
// Updates current user data
var userName = "User"
var image: UIImage?
if let retrievedName = user.displayName
{
userName = retrievedName
}
if let retrievedImageUrl = user.photoURL
{
if let data = try? Data(contentsOf: retrievedImageUrl)
{
image = UIImage(data: data)
}
}
User.post(uid: user.uid, provider: user.providerID, userName: userName, image: image)
{
user in
User.currentUser = user
User.startTrackingCurrentUser()
self.performSegue(withIdentifier: "ToFeed", sender: nil)
}
}
}
}
}
}
1) you need to be able to access the button through IBOutlet or programmatically
2) set the button to the cells indexPath.row and add an action to it
let post = posts[indexPath.row]
cell.configureCell(tableView: tableView, post: post)
// here you need to add
cell.yourButton.tag = indexPath.row
cell.yourButton.addTarget(self, action: #selector(FeedViewController.toFeedDetailAction(_:)), for: .touchUpInside)
return cell
3) when you then press the button you can identify your feed, because you have the button tag, so you know the feed from your Posts-Array
func toFeedDetailAction(_ sender: UIButton) {
let feedDetailsViewController = self.storyboard?.instantiateViewController(withIdentifier: "FeedDetailsIdentifier") as! FeedDetailsViewController
feedDetailsViewController.post = posts[sender.tag]
self.navigationController?.pushViewController(feedDetailsViewController, animated: true)
}
4) in your FeedDetailsViewController add
var post: Post!
5) in FeedDetailsViewController do what ever you want with the post
Hope this helps :)

How to Update UITableView with data passed from another ViewController?

I am trying to populate a UITableView with data passed to the ViewController from LoginViewController after the user logs in.
The current process is:
ViewController loads first, if user is not logged in LoginViewController pops up over the top. User logs in, details are fetched from the database (userDetails and communities). LoginViewController is then dismissed and ViewController is again visible.
The communities variable is being populated and values transferred from LoginViewController to ViewController.
I believe my problem is func tableView is run before the data is fetched from the user logging in.
print ("test 1: ",communities) just prints [],[],[],[]
However print ("test 2: ",communities) prints the correct values.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UsernameSentDelegate {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var receiveUsername: UILabel!
#IBOutlet weak var userEmailText: UILabel!
var userEmail: String?
var communities = [String]()
#IBOutlet weak var communitiesTableView: UITableView!
#IBAction func unwindToHome(_ segue: UIStoryboardSegue) {
}
//recieves email address from delegate from LoginViewController
func userLoggedIn(data: String) {
userEmailText.text = data
}
override func viewDidLoad() {
super.viewDidLoad()
self.communitiesTableView.delegate = self
self.communitiesTableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print ("test 1: ",communities) //not printing value
return self.communities.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let title = self.communities[indexPath.row]
let cell = UITableViewCell()
cell.textLabel?.text = title
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "loginView" {
let loginViewController: LoginViewController = segue.destination as! LoginViewController
loginViewController.delegate = self
}
if segue.identifier == "createCommunitySegue" {
let createCommunityController: CreateNewCommunity = segue.destination as! CreateNewCommunity
createCommunityController.myEmail = userEmailText.text
}
}
override func viewDidAppear(_ animated: Bool)
{
print ("test 2: ",communities) //prints values correctly
let isUserLoggedIn = UserDefaults.bool(UserDefaults.standard)(forKey: "isUserLoggedIn");
if(!isUserLoggedIn)
{
self.performSegue(withIdentifier: "loginView", sender: self);
}
}
#IBAction func logoutButtonTapped(_ sender: AnyObject) {
UserDefaults.set(UserDefaults.standard)(false, forKey: "isUserLoggedIn");
self.performSegue(withIdentifier: "loginView", sender: self);
}
#IBAction func createCommunityTapped(_ sender: AnyObject) {
}
}
This is the code for CreateCommunityViewController:
import UIKit
class CreateNewCommunity: UIViewController {
#IBOutlet weak var communityNameTextField: UITextField!
#IBOutlet weak var passwordTextField: UILabel!
#IBOutlet weak var emailLabel: UILabel!
var myEmail: String?
#IBAction func cancelButtonPapped(_ sender: AnyObject) {
self.performSegue(withIdentifier: "unwindCommunity", sender: self)
}
#IBAction func createCommunityButtonTapped(_ sender: AnyObject) {
let communityName = communityNameTextField.text;
if (communityName!.isEmpty){
displayMyAlertMessage(userMessage: "You must name your Community");
return;
}else{
func generateRandomStringWithLength(length: Int) -> String {
var randomString = ""
let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
for _ in 1...length {
let randomIndex = Int(arc4random_uniform(UInt32(letters.characters.count)))
let a = letters.index(letters.startIndex, offsetBy: randomIndex)
randomString += String(letters[a])
}
return randomString
}
let communityCode = generateRandomStringWithLength(length: 6)
passwordTextField.text = communityCode
let myUrl = URL(string: "http://www.quasisquest.uk/KeepScore/createCommunity.php?");
var request = URLRequest(url:myUrl!);
request.httpMethod = "POST";
let postString = "communityname=\(communityName!)&code=\(communityCode)&email=\(myEmail!)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
DispatchQueue.main.async
{
if (try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject]) != nil {
let myAlert = UIAlertController(title: "Community Registered", message: "Community Code:\(communityCode)", preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default){(action) in
self.dismiss(animated: true, completion: nil)
}
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil)
}
}
}
task.resume()
}
}
}
Try to call reload data in didSet. E.g. var communities = [] { didSet { yourtableview.realoadData()
}
}

Resources