After searching for a few hours for the answer to this question, I have found 1 post that was similar here: However I tried to replicate but I believe the difference in language syntax made it very hard to translate.
Within my application, users are allowed to make posts, the structure for the post in Firsestore looks like this:
The creator
is a userId of a user that also lives in the database.
I am aware of how to fetch things from Firestore when my structs conform to Codable and they map 1 to 1 but I have not experienced having to fetch nested data after an initial fetch.
Question By querying my backend for posts, how can I also create the user object that lives inside?
Here is the post object I was expecting to create:
import FirebaseFirestoreSwift
public struct Post: Codable {
/// The school id
@DocumentID var id: String?
/// The name of the school
public let content: String
/// The user who made the post
public var creator: AppUser?
}
I want to create appUser
from the creator
field that is returned. Should I build the content and then have some sort of promise.then
to fetch the user? Or can i do both at the same time?
Here is what I think I should be doing
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
}
let posts = documents.compactMap { queryDocumentSnapshot -> Post? in
return try? queryDocumentSnapshot.data(as: Post.self)
}
let postsWithUser: [Post] = posts.map { post in
//Fetch User and return an updated struct
}
resolver.fulfill(postsWithUser)
}
}
}
I solved it! Basically, we want to let the first fetch complete. Then we iterate through each post.id and call FetchUser()
i which is a function i built that returns Promise<User>
func fetchTopLevelPost(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
}
let posts = documents.compactMap { queryDocumentSnapshot -> Post? in
return try? queryDocumentSnapshot.data(as: Post.self)
}
resolver.fulfill(posts)
}
}
}
func fetchPostUser(for posts: [Post]) -> Promise<[Post]> {
let allPromise = posts.map{ FetchUser().fetch(for: $0.creatorId) }
return Promise { resolver in
when(fulfilled: allPromise).done { users in
let completePost = zip(users, posts).map(post.init(completeData:))
resolver.fulfill(completePost)
}
.catch { error in
resolver.reject(Errors.firebaseError)
}
}
}
Here is the callsite:
public func fetch(for schoolId: String) -> Promise<[Post]> {
return fetchTopLevelPost(for: schoolId)
.then { self.fetchPostUser(for: $0) }
}