Firebase 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:
- Fetch all posts first
- Collect all unique user IDs from the posts
- Fetch all those users in parallel
- 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,
creatorwill benil—make sure your UI handles this gracefully (e.g., show "Unknown User"). - Cache Users: Add a memory or disk cache for
AppUserobjects 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

