在 iOS 上显示用户帖子
Posted
技术标签:
【中文标题】在 iOS 上显示用户帖子【英文标题】:Displaying a users post on iOS 【发布时间】:2019-11-28 14:53:03 【问题描述】:我正在尝试在 ios 上的表格视图中显示用户“帖子”。当用户在我的应用程序中写帖子并点击帖子按钮时,帖子会保存到我的 firebase 实时数据库中,但它没有显示在编写代码的视图控制器上的 tableview 中。我不确定如果它是我的代码或与firebase有关的东西没有响应并显示数据。这是我的代码:
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
这也有帮助,这是我的数据库规则:
"rules" :
"posts" :
".write" : true,
".read": true
,
"users" :
".write" : true,
".read" : true
这是我的PostTableViewCell
、Post
类和我的PostTableViewCell.xib
的示例: Post Class PostTableViewCell PostTableViewCell.xib
这也是我的代码在输入纯数据时的样子。
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
这就是我运行应用程序时的样子。 TableView Loadout With Sample Data
【问题讨论】:
你需要做一些调试。在self.posts = tempPosts
行下,尝试:print("[TEST] \(self.posts.count)")
。您将知道 Firebase 是否正在返回数据。
另外,不需要 self.tableView.reloadData() 两次(一次在 viewDidLoad 中,一次在你的观察方法中)。
你在 cell.set(post: posts[indexPath.row])
方法中做了什么?您是否有可能根本不会将信息写入单元 UI?如果当时找到有效的帖子,我会尝试记录。
@allenhinson214 看来您没有进入 Firebase 观察者内部。抱歉,我从未使用过 Firebase 数据库,我不知道它是如何工作的。您是否检查过您的 Firebase 数据库中是否包含数据?
【参考方案1】:
我将此作为单独的答案添加,因为我的第一个答案解决了从 Firebase 读取的可能问题。现在 tableView 出现了问题。
附加的代码是一个完整且可运行的项目 - 简直是疯了,除了 PostTableViewCell xib,它实际上是 UITableViewCell 子类和匹配 xib - 没有其他代码。
我希望您可以将此作为模板与您的代码进行比较。
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
结果图片
【讨论】:
请看一下我更新的问题,它显示了我的 TableView 与示例数据的样子,还显示了我的PostTableViewCell
、Post
类和 PostTableViewCell.xib
代码
您还认为是我的 Firebase 规则不允许帖子显示吗?我是否需要为“帖子”和“用户”创建规则以允许显示数据?
@allenhinson214 正如我们上面所讨论的,根据您所说的,您读取 firebase 的代码在将您的 firebase 数据输出到控制台时工作正常。这意味着您的 tableView 有问题。如果您完全删除您的代码并使用我的代码,遵循设计模式,它也适用于您。一旦显示了某些内容,您就可以进一步优化该代码以满足您的需求。
好的,我会这样做并让您知道会发生什么,感谢您提供的所有帮助和解释。
所以我像这样尝试了您的代码,它像您的一样完美地显示了两个示例帖子,但是当我去自定义并添加我的代码以从我的 firebase 数据库中获取帖子时,没有数据仍然显示。我仍然不确定为什么会遇到这个问题,这可能是我的 Firebase 规则不允许帖子显示在视图表上的原因吗?我已经用我现在拥有的当前规则更新了我的问题,它仍然在控制台中打印帖子,所以从 firebase 获取数据的代码正在工作,只是没有显示它。【参考方案2】:
您应该将代码中的 postRef.observe(.value, with:
更改为 postRef.observe(.childAdded, with:
,这样每次将新的孩子添加到帖子引用时都会调用观察函数。
【讨论】:
我尝试更改它并添加您建议的代码,但它仍然没有显示帖子。我仍然对此感到困惑,不知道为什么会这样做。 我根本不显示帖子或不显示新帖子?. value
监听所有类型的数据更改。所以我认为这里的代码还可以。
这个答案没有解决这个问题,因为问题不在于 如何 来自 Firebase 的数据,而在于 if 数据被读取并且可能是 tableView 配置问题。此外,如果函数更改为 .childAdded,firebase 闭包中的代码也需要更改。话虽如此,这是一个 100% 的好建议,可以防止每次发生更改时重新加载整个数据集。【参考方案3】:
您的规则拒绝读取该节点的权限:它们设置为默认值,需要经过身份验证的用户才能读取/写入任何数据。您的应用需要进行身份验证,因此您可以在主视图控制器的顶部添加一些类似的内容
import UIKit
import Firebase
import FirebaseAuth
class ViewController: UIViewController,
然后添加验证码。您需要在 Firebase 控制台中设置一个用户并在 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
)
或
为了测试,您可以通过更改规则来允许任何人读/写访问
"rules":
".read": true,
".write": true
但这很不安全,所以不要这样。
编辑
另一个问题是您如何处理从 Firebase 读取的数据。如果有任何错误,if 语句将完全保释 - 例如,指定的子节点不在正在读取的节点之一中。
纠正单独评估每个孩子并提供默认值以防孩子丢失。
假设这样的结构
posts
post_0
author
uid: "uid_0"
username: "uid 0 username"
url: "www.someurl.com"
post_text: "My post about posting"
然后是读取所有帖子(和子数据)并将其输出到控制台的代码。
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)
)
输出看起来像这样
uid_0 uid 0 username My post about posting www.someurl.com
uid_1 uid 1 username Things about posting www.anotherurl.com
【讨论】:
所以我按照你建议的代码放在我的主视图控制器上,它为人们提供登录或注册的选项,这两个选项都是将他们引导到另一个视图控制器的按钮。当我使用此代码和相同的 firebase 规则运行应用程序时,它仍然给我同样的 permission_denied 错误。我回去更改了每个人的读写规则,当我运行应用程序时,它没有给我那个错误,但它仍然没有显示任何数据。 我将我的规则更改为 "rules": ".read": "auth !=null", ".write": "auth !=null"
,这似乎可以在调试器中没有给我一个 permission_denied 语句,但仍然没有显示来自 Firebase 的任何数据
@allenhinson214 这是一个开始,但是您是否根据我的答案中的代码对您的用户进行身份验证?
这是正确的,我正在遵循您在答案中包含的代码,并且在调试器中消除了 permission_denied 错误以及我在上一条评论中显示的数据库规则。
@allenhinson214 好。现在,重新阅读 cmets 到我提出的确保填充 tableView 数据源的方法的问题。一旦您知道您实际上将来自 Firebase 的数据存储在 self.posts 中,您就可以进行更多故障排除。如果未填充该数组,则您的 Firebase 代码存在问题。如果填充了该数组,则错误在于您的 tableView。在继续前进之前,您必须建立一些基线。以上是关于在 iOS 上显示用户帖子的主要内容,如果未能解决你的问题,请参考以下文章