在 Swift 中使用 Firebase 显示所有帖子?

Posted

技术标签:

【中文标题】在 Swift 中使用 Firebase 显示所有帖子?【英文标题】:Displaying all posts using Firebase in Swift? 【发布时间】:2020-11-15 02:33:27 【问题描述】:

我的目标是,当用户打开应用程序时,他们会看到该应用程序的每个用户的每篇帖子。我的数据树如下:

- posts 
    - UID
        - postKey
           --attributes 
- users 
    - UID 
       - attributes 

这是一个例子:

-posts
   -74anqEXU8kQHVr7IKoO3N9NNqDh1
     -MLzvs5VvXB_z7fhbh2p
        created_at: 1605242945.969368
        image_height: 414.6865671641791
        image_url: "https://firebasestorage...."
     -MLzvun01fNXNRbn7TPv
     -MM7zGyZ7GbenhlisJUZ
   -VGxqdzc2CkWWn39pa8xUofEWgNm2
   -pNvGg84JR0TbXvS4XlX8KbfBasz2
-users
   -74anqEXU8kQHVr7IKoO3N9NNqDh1
   -VGxqdzc2CkWWn39pa8xUofEWgNm2

我知道我必须对所有帖子进行快照,然后将其显示在主控制器 (SearchViewController) 的 viewDidLoad() 中。我不知道如何显示所有帖子。

此代码用于显示当前用户的帖子,存储在名为 UserService.swift 的文件中:

static func posts(for user: User, completion: @escaping ([Post]) -> Void) 
        
        let ref = Database.database().reference().child("posts").child(user.uid)
        
        ref.observeSingleEvent(of: .value, with:  (snapshot) in
            guard let snapshot = snapshot.children.allObjects as? [DataSnapshot] else 
                return completion([])
            
            
            let dispatchGroup = DispatchGroup()
            
            let posts: [Post] = snapshot.reversed().compactMap 
                guard let post = Post(snapshot: $0) else  return nil 
                
                dispatchGroup.enter()
                
                   SaveService.isPostSaved(post)  (isSaved) in
                    post.isSaved = isSaved
                    dispatchGroup.leave()
                
                
                return post
            
            
            dispatchGroup.notify(queue: .main, execute: 
                completion(posts)
            )
        )
    

然后在 SearchViewController 的 ViewDidLoad() 中:

        UserService.posts(for: User.current)  (posts) in
            self.posts = posts
            self.tableView.reloadData()
        

但是我如何为所有帖子执行此操作。当我尝试以下操作时,它不会让我指定随机用户:

let ref = Database.database().reference().child("posts").child(User)

知道我可以在“.child(User”框中添加什么来完成这项工作吗?或者如何创建一个 for 循环来正确迭代?

*编辑

这是有效的:


    static func allPosts(completion: @escaping ([Post]) -> Void) 
        let ref = Database.database().reference().child("posts")

        ref.observeSingleEvent(of: .value, with:  (snapshot) in
            var postsArray = [Post]()
            
            for userSnapshot in snapshot.children 
                guard let snapshot = (userSnapshot as AnyObject).children.allObjects as? [DataSnapshot] else 
                    return completion([])
                

                let dispatchGroup = DispatchGroup()

                let posts: [Post] = snapshot.reversed().compactMap 
                    guard let post = Post(snapshot:  $0) else  return nil 
                    
postsArray
                    postsArray.append(post)
                    
                    dispatchGroup.enter()

                    SaveService.isPostSaved(post)  (isSaved) in
                        post.isSaved = isSaved
                        dispatchGroup.leave()
                    
                    
                    return post
                
                dispatchGroup.notify(queue: .main, execute: 
                    completion(postsArray)
                )
            
        )
    

【问题讨论】:

【参考方案1】:

我可能忽略了一些东西,但似乎目标是

当用户打开应用时,他们会看到每个用户的每篇帖子

如果是这样,有时越简单越好;加载所有帖子,遍历它们,为每个帖子创建您的 Post 对象,然后重新加载 tableview (?)

let postsRef = self.ref.child("posts") //self.ref points to my firebase
postsRef.observeSingleEvent(of: .value, with:  snapshot in
    let allPosts = snapshot.children.allObjects as! [DataSnapshot]
    for postSnap in allPosts 
        let aPost = Post(initWithSnapshot: postSnap)
        self.postsArrray.append(post) //
    
    self.postTableView.reloadData()

这假设你的 Post 类有一个方便的 init 来从快照中填充它的属性。如果是这样,请在其中将 isSaved 设置为 true,因为使用保存的数据调用便利 init。

如果您在应用启动或视图加载时调用它,则无需回调和 dispatchQueues。

【讨论】:

我认为您的想法是正确的,但这会返回空白(没有帖子)。我想我需要再往下一层:帖子>用户UIDs> PostKeys。原始代码获取 currentUserUID 的快照,执行 postKeys 的 compactMap,然后返回它们。知道如何降低这个水平吗? @iosBeginnerGuy 是的,你是对的——我在结构中忽略了这一点。看来您的所有用户帖子都在 UID 中分组?如果是这样,我会建议一个不同的结构,其中 postId 是节点的键,然后用户 uid 存储为子节点。这将允许您一次阅读不同用户的帖子 - 只是最近 10 个或按日期或许多其他查询。照原样,您必须读取大量无关数据并过滤掉代码中的所有内容才能得到您想要的。 这是一个很好的建议,谢谢。我将更改数据模型。我确实让它工作了,我会编辑原始帖子。【参考方案2】:

你可以在 JSON 中观察更高一级,然后在回调中添加一个额外的循环:

let ref = Database.database().reference().child("posts")

ref.observeSingleEvent(of: .value, with:  (snapshot) in
    for userSnapshot in snapshot.children 
        guard let snapshot = userSnapshot.children.allObjects as? [DataSnapshot] else 
            return completion([])
        
    
        let dispatchGroup = DispatchGroup()
    
        let posts: [Post] = snapshot.reversed().compactMap 
            guard let post = Post(snapshot: $0) else  return nil 
            
            dispatchGroup.enter()
        
               SaveService.isPostSaved(post)  (isSaved) in
                post.isSaved = isSaved
                dispatchGroup.leave()
            
        
            return post
                
    
    dispatchGroup.notify(queue: .main, execute: 
        completion(posts)
    )

)

【讨论】:

此代码现在显示不同用户的帖子,但仅显示一个用户。我感觉需要修改的是这一行: let posts: [Post] = snapshot.reversed().compactMap guard let post = Post(snapshot: $0) else return nil 另外,Xcode 让我使用这个:guard let snapshot = (userSnapshot as AnyObject).children.allObjects as? [数据快照] 否则 我专注于如何遍历我的答案中的嵌套快照,因为这就是你所问的。您需要更改其余代码以将所有用户的帖子添加到单个数组中。所以将let posts: [Post]... 移到循环之外,然后在嵌套循环内添加到该数组。 谢谢!这行得通。我编辑了主要帖子以显示最终的工作。真的不能告诉你我是多么感激这一点,整个周末我都陷入了困境。你有所作为。再次感谢!

以上是关于在 Swift 中使用 Firebase 显示所有帖子?的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 中使用 Firebase 获取值属性

Swift 3 Firebase 到 MapKit

firebase 在 Swift 中关闭重载表?

如何在 UILabel 中显示名字?我正在使用 Firebase 和 Swift 开发 IOS 应用程序

Swift/xcode/iOS:使用 Firebase 检索已收集的数据并将其显示在单独的视图控制器中

Firebase tableview没有填充,Swift