Firebase & SwiftUI
Firestore’s @FirestoreQuery property wrapper for SwiftUI

Firebase has long been a place for developers to rapidly prototype a functional front-end that interfaces with a full-fledged enterprise level cloud system.
For us Swift developers, Firebase has supported us as the language has grown. Firebase was made available via Swift Package Manager recently, and there’s a lot of new APIs that came out for SwiftUI with that particular release. Today, I want to walk us through a few, but really one in particular: @FirestoreQuery(collection:)
!!!
Prerequisites
I’m hoping this article is easily digestible if you know what you’re doing with Firebase. But if you’re on the steeper part of the learning curve — worry not.
Let me outline the bare minimum required for getting a FirestoreQuery
up and running.
- A Firebase project (set up or login to an existing Firebase project in your Firebase Console).
- An Xcode project with an iOS, macOS, or tvOS app target. (FirestoreQuery — or Firestore for that matter — does not yet work on watchOS)
- The Firebase Apple SDK downloaded and installed into your project via Cocoapods or Swift Package Manager.
- Link the library
FirebaseFirestoreSwift
to your target’s linked libraries and frameworks.
See the Firebase documentation for more details on installation.
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇
The API
FirebaseFirestoreSwift
has some new APIs that make querying for your documents a breeze.
@FirestoreQuery
The new @FirestoreQuery(collection:)
property wrapper is a pretty darn flexible little API. Here is a very simple example of how you can listen for a collection of documents, and display them in a List
view using SwiftUI:
If you run this code in your app you’ll… probably see nothing… if there’s nothing in your database, that is. Hopefully though you have an existing data model and BLAM, you’ve displayed like all of them in your list.
If you in fact don’t have any data in your database, leave your new app running in your simulator (or on device) and navigate to your Firestore dashboard in your Firebase project. Create a collection called
foos
and add a new object with an autogenerated id, and a string attribute calledbar
.
Like magic, there should now be a new view displayed in your list!
@DocumentID
You might’ve noticed a property wrapper in the model code above:
DocumentID
is a really easy way to tell Firebase that, when this object is encoded to Firebase, the system knows how to map this struct to a Firestore document. Peter Friese wrote a really wonderful article outlining this property wrapper, check it out here!
@ServerTimestamp
Another property wrapper that Firebase provides us is:
If this is left nil
then Firebase will auto-generate a UTC timestamp based on the server’s clock. This variable also handles race conditions across multiple clients, which is an incredibly complex set of problems to handle.
For example: Imagine a chat app that is leveraging a Chat
model object. Chat includes an array of Message
objects called messages
. As each user sends their message, we append the last message to Chat.messages
and save that new Message
document with a new updated
timestamp variable.
Normally, this would result in some unreliable race conditions. But, wrapping the updated
variable within a @ServerTimestamp
property wrapper will leverage Firebase to timestamp the documents in the order in which they were saved on the client side. That way, the chat is always in the order which the messages were sent, even when there is not a network connection. Pretty fancy!
Google, y’all write some cool code.
Predicates
Often times when we design apps with Firestore us savvy developers will rely on querying our data to show the user the data that they’re interested in. We need to filter our documents to get just the right amount of information to give our users the best UX possible. We also need to keep our number of document reads to a minimum (cause that sh*t’s expensive at scale!).
As the name implies, @FirestoreQuery
offers an easy way to query your collections using the @FirestoreQuery(collection:predicates:)
method:
Often times we need to dynamically add filters depending on the user’s search criteria. Well, the Firebase team has made this one pretty slick:
There are a bunch of different predicates that are viable to use:
isEqualTo(_ field: String, _ value: Any)
isIn(_ field: String, _ values: [Any])
isNotIn(_ field: String, _ values: [Any])
arrayContains(_ field: String, _ value: Any)
arrayContainsAny(_ field: String, _ values: [Any])
isLessThan(_ field: String, _ value: Any)
isGreaterThan(_ field: String, _ value: Any)
isLessThanOrEqualTo(_ field: String, _ value: Any)
isGreaterThanOrEqualTo(_ field: String, _ value: Any)
orderBy(_ field: String, _ value: Bool)
limitTo(_ value: Int)
limitToLast(_ value: Int)
Visit Google’s official documentation for more information on the intricacies of querying for your data:
Error Handling
The same way that we can access the predicates dynamically, we can also access an optional error to display or log errors as needed:
Alternatively, you specify the FirestoreQuery
as a Result<[Foo], Error>
value and handle the switch case in the body to conditionally show an error:
Conclusion
@FirestoreQuery
is an incredibly powerful little API that will make fetching data from Firestore a lot easier. For real, it’s helped me do a ton in a very short amount of time.
If you have any questions or adjustments on how this works leave me a comment here.
I hope to be writing more, so please subscribe to my blog and I’ll see you in the next one!