You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase Firestore获取帖子时如何关联获取用户对象?

How to Fetch Posts with Associated User Objects from Firestore

Hey there! I’ve run into this exact scenario before—when you need to pull posts from Firestore but also populate the nested user data referenced by ID. Let’s walk through the cleanest, most efficient way to handle this.

First, let’s fix a small mismatch in your Post struct: right now, Firestore stores the creator as a user ID string, but your struct expects an AppUser?. That will cause data(as:) to fail. We’ll adjust the struct to first capture the user ID, then populate the AppUser object after fetching.

Step 1: Update the Post Struct

We’ll add a private property to hold the user ID (mapped directly from Firestore’s creator field) and keep the creator property for the actual user object:

import FirebaseFirestoreSwift

public struct Post: Codable {
    @DocumentID var id: String?
    public let content: String
    // Maps to the "creator" field in Firestore (which is a user ID string)
    private let creatorId: String
    // We'll populate this with the fetched user data
    public var creator: AppUser?
    
    // Custom coding keys to map Firestore's field name to our property
    enum CodingKeys: String, CodingKey {
        case id
        case content
        case creatorId = "creator"
    }
}

Step 2: Fetch Posts + Parallel User Requests

The key here is to avoid fetching users one-by-one (which is slow). Instead, we’ll:

  1. Fetch all posts first
  2. Collect all unique user IDs from the posts
  3. Fetch all those users in parallel
  4. Map the users back to their respective posts

Here’s the updated fetch function:

public func fetch(for schoolId: String) -> Promise<[Post]> {
    return Promise { resolver in
        fireStore
            .collection("schools").document(schoolId)
            .collection("posts").getDocuments { querySnapshot, err in
                guard let documents = querySnapshot?.documents else {
                    resolver.reject(Errors.firebaseError)
                    return
                }
                
                do {
                    // Parse all posts (creator will be nil at this point)
                    var posts = try documents.compactMap { try $0.data(as: Post.self) }
                    
                    // Get all unique creator IDs to avoid duplicate requests
                    let creatorIds = posts.map { $0.creatorId }
                    let uniqueCreatorIds = Array(Set(creatorIds))
                    
                    // If no users to fetch, return posts immediately
                    guard !uniqueCreatorIds.isEmpty else {
                        resolver.fulfill(posts)
                        return
                    }
                    
                    // Create promises for each user fetch
                    let userFetchPromises = uniqueCreatorIds.map { userId in
                        self.fetchUser(withId: userId)
                    }
                    
                    // Wait for all user requests to complete
                    when(fulfilled: userFetchPromises).done { users in
                        // Create a dictionary to map user IDs to AppUser objects
                        let userMap = Dictionary(uniqueKeysWithValues: zip(uniqueCreatorIds, users))
                        
                        // Populate the creator field for each post
                        posts = posts.map { post in
                            var mutablePost = post
                            mutablePost.creator = userMap[post.creatorId]
                            return mutablePost
                        }
                        
                        resolver.fulfill(posts)
                    }.catch { error in
                        resolver.reject(error)
                    }
                    
                } catch {
                    resolver.reject(error)
                }
            }
    }
}

// Helper function to fetch a single user from Firestore
private func fetchUser(withId userId: String) -> Promise<AppUser> {
    return Promise { resolver in
        fireStore.collection("users").document(userId).getDocument { snapshot, err in
            guard let snapshot = snapshot, let user = try? snapshot.data(as: AppUser.self) else {
                resolver.reject(Errors.userNotFound)
                return
            }
            resolver.fulfill(user)
        }
    }
}

Why This Works Better

  • Parallel Requests: Fetching all users at once is way faster than fetching them one after another, especially if you have lots of posts.
  • No Duplicate Requests: Using a set to get unique user IDs means we don’t waste time re-fetching the same user multiple times.
  • Clean Mapping: The user dictionary lets us quickly match each post to its creator without looping through users every time.

Bonus Tips

  • Handle Missing Users: If a user account has been deleted, creator will be nil—make sure your UI handles this gracefully (e.g., show "Unknown User").
  • Cache Users: Add a memory or disk cache for AppUser objects so you don’t re-fetch the same user every time you load posts.
  • Pagination: If you’re loading posts in chunks (pagination), apply the same logic to each page—fetch the page’s posts, then fetch the unique users for that page.

内容的提问来源于stack exchange,提问作者Roy Gunderson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 23:42:48