firebase / firebase-ios-sdk

Firebase SDK for Apple App Development
https://firebase.google.com
Apache License 2.0
5.62k stars 1.47k forks source link

FR: Make propertyWrapper for lazily loading related documents through references #6129

Closed Evertt closed 4 years ago

Evertt commented 4 years ago

Feature proposal

Let's say I have a collection users and a collection posts. And every post looks like this:

{
    "body": "Hello World",
    "user": "users/my-user"
}

So the user field is a document reference to a user document. It would be nice if my struct in Swift could look something like this:

struct Post: Codable {
    @DocumentID var id: String?
    var body: String
    @Reference var user: User
}

The new @Reference property wrapper is what I mean. So let's say you load the data from Firestore using the Swift Firestore encoder/decoder like so:

var posts: [Post] = []

postsCollectionReference.getDocuments { snapshot, error in
    guard let documents = snapshot?.documents else {
        print(error)
        return
    }

    posts = documents.compactMap { document in
        try? document.data(as: Post.self)
    }
}

Then the idea is that Firestore's encoder doesn't eagerly load all the related User models, but instead it just initializes the property wrapper @Reference with the string value of the user-reference. And then the property wrapper would function a little like lazy var in that it only loads the User document once some code tries to access it.

Is it clear what I mean?

edit

It'd be even better if there could be both a normal @Reference propertywrapper and a @LiveReference one, which is one that subscribes to live snapshots instead of just fetching a single one.

Evertt commented 4 years ago

Hmm, after giving it some thought, I'm afraid that this is maybe not possible? Since Firestore can always only work asynchronously. That's a real bummer.

wilhuff commented 4 years ago

Thanks for writing in!

I think your hunch is right. Transparent lazy loading is going to block the calling thread which isn't something we want to do. It would be too easy to accidentally block the main thread with this kind of interface. If Swift were to support async/await or promises this is something that could start to make sense. As it stands, this isn't something we can pursue.