问题
I am trying to display a users "post" in a table view on iOS. When a users writes a post and hits the post button in my app, the post is saved to my firebase real time data base but it is not displaying in the tableview on the viewcontroller on which the code is written in. I'm not sure if its my code or something to do with firebase not responding and showing the data. Here is my code:
import Foundation
import UIKit
import Firebase
class HomeViewController:UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView:UITableView!
var posts = [Post] ()
override func viewDidLoad() {
super.viewDidLoad()
self.observePosts()
tableView = UITableView(frame: view.bounds, style: .plain)
let cellNib = UINib(nibName: "PostTableViewCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "postCell")
view.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
tableView.tableFooterView = UIView()
tableView.reloadData()
}
func observePosts() {
let postsRef = Database.database().reference().child("posts")
postsRef.observe(.childAdded, with: { snapshot in
print(snapshot.value)
var tempPosts = [Post]()
for child in snapshot.children {
if let childSnapshot = child as? DataSnapshot,
let dict = childSnapshot.value as? [String:Any],
let author = dict["author"] as? [String:Any],
let uid = author["uid"] as? String,
let fullname = author["username"] as? String,
let photoURL = author["photoURL"] as? String,
let url = URL (string:photoURL),
let text = dict["text"] as? String,
let timestamp = dict["timestamp"] as? Double {
let userProfile = UserProfile(uid: uid, fullname: fullname, photoURL: url)
let post = Post(id: childSnapshot.key, author: userProfile, text: text, timestamp: timestamp)
tempPosts.append(post)
}
}
self.posts = tempPosts
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
}
}
Also is this helps, here are my database rules:
{
"rules" :{
"posts" : {
".write" : true,
".read": true
},
"users" : {
".write" : true,
".read" : true
}
}
}
Here are examples of my PostTableViewCell, Post Class and my PostTableViewCell.xib: Post Class PostTableViewCell PostTableViewCell.xib
Also This is what it my code looks like when entering plain data.
import Foundation
import UIKit
import Firebase
class HomeViewController:UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView:UITableView!
var posts = ["This is a text"]
override func viewDidLoad() {
super.viewDidLoad()
tableView = UITableView(frame: view.bounds, style: .plain)
let cellNib = UINib(nibName: "PostTableViewCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "postCell")
view.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
tableView.tableFooterView = UIView()
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
return cell
}
}
And this is what it looks like when I run my app. TableView Loadout With Sample Data
回答1:
You should change postRef.observe(.value, with: in your code to postRef.observe(.childAdded, with: , this way observe function will be called every time new child is added to posts reference.
回答2:
Your rules are denying permission to read that node: they are set to the default which requires an authenticated user to read/write any data. You app needs to have authentication, so you would add something along these lines to the top of your main viewController
import UIKit
import Firebase
import FirebaseAuth
class ViewController: UIViewController,
and then add in authentication code. You will need to have a User set up in your Firebase Console and call this function in viewDidLoad
func authUser() {
Auth.auth().signIn(withEmail: "users email", password: "password", completion: { (auth, error) in
if let x = error { //example error checking
let err = x as NSError
switch err.code {
case AuthErrorCode.wrongPassword.rawValue:
print("wrong password")
case AuthErrorCode.invalidEmail.rawValue:
print("invalued email")
default:
print("unknown error")
}
} else { //no error, user is authed
if let user = auth?.user {
print("uid: \(user.uid)") //print their uid
}
}
})
}
OR
for testing you can allow anyone read/write access by changing the rules
{
"rules": {
".read": true,
".write": true
}
}
but that's very insecure so don't leave it that way.
EDIT
The other issue is in how your working with the read data from Firebase. The if statement will totally bail if anything is wrong - for example a specified child is not in one of the nodes being read.
To correct that evaluate each child separately and provide a default value in case the child is missing.
Assume a struture like this
posts
post_0
author
uid: "uid_0"
username: "uid 0 username"
url: "www.someurl.com"
post_text: "My post about posting"
and then the code to read all of the posts (and the child data) and output it to console.
func readPosts() {
let postsRef = self.ref.child("posts") //self.ref points to my firebase
postsRef.observeSingleEvent(of: .value, with: { snapshot in
print("inside the observe closure")
let allPosts = snapshot.children.allObjects as! [DataSnapshot]
for postSnap in allPosts {
let authorSnap = postSnap.childSnapshot(forPath: "author")
let uid = authorSnap.childSnapshot(forPath: "uid").value as? String ?? "NO UID!"
let username = authorSnap.childSnapshot(forPath: "username").value as? String ?? "NO USERNAME!"
let postTitle = postSnap.childSnapshot(forPath: "post_title").value as? String ?? "NO POST TITLE!"
let url = postSnap.childSnapshot(forPath: "url").value as? String ?? "NO URL!"
print(uid, username, postTitle, url)
}
}, withCancel: { error in
let err = error as NSError
print(err.localizedDescription)
})
}
and the output looks like this
uid_0 uid 0 username My post about posting www.someurl.com
uid_1 uid 1 username Things about posting www.anotherurl.com
回答3:
I am adding this as a separate answer since my first answer addressed a possible issue reading from Firebase. Now there's an issue with the tableView.
The attached code is a complete and runnable project - soup to nuts, other than a PostTableViewCell xib which is literally a UITableViewCell subclass and matching xib - no other code.
I hope you can use this as a template to compare against your code.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
class PostClass {
var post_text = ""
}
var tableView:UITableView!
var posts = [PostClass]()
override func viewDidLoad() {
super.viewDidLoad()
self.setupTableView()
//create two posts just to test the tableView
let p0 = PostClass()
p0.post_text = "Hello Row 0"
let p1 = PostClass()
p1.post_text = "And Row 1"
self.posts.append(contentsOf: [p0, p1])
}
func setupTableView() {
tableView = UITableView(frame: view.bounds, style: .plain)
let cellNib = UINib(nibName: "PostTableViewCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "postCell")
view.addSubview(tableView)
var layoutGuide:UILayoutGuide!
layoutGuide = view.safeAreaLayoutGuide
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()
}
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
let post = self.posts[indexPath.row]
cell.textLabel!.text = post.post_text
return cell
}
}
and an image of the result
来源:https://stackoverflow.com/questions/59091483/displaying-a-users-post-on-ios