Firebase strategy for @Shared property wrapper #2924
Replies: 6 comments 4 replies
-
My experiments can be tried out here: https://github.com/mortenbekditlevsen/swift-composable-architecture-firebase.git |
Beta Was this translation helpful? Give feedback.
-
You can now unwrap a collection of data in Firestore as an |
Beta Was this translation helpful? Give feedback.
-
Now the same API can be used for Firestore and RTDB. |
Beta Was this translation helpful? Give feedback.
-
@mortenbekditlevsen Thanks for sharing! We don't have a ton of Firebase experience, so we can't comment on some of the details, but to a few points:
Our current recommendation with read-only is via access control: public var firechatMessages: [Message] { _firechatMessages }
@Shared(.firebase(…)) private var _firechatMessages = [] But read-only has come up often enough that some kind of library API is under consideration. You may find it helpful to look at a proof of concept for GRDB we pushed earlier: shared-state-beta...shared-grdb This is by no means production-ready and only means to simplistically drive the Todos demo, but it does demonstrate how local, mutable
Interesting! I found that this didn't work for me when dealing with an identified array, though. Did you have any trouble when adding support? |
Beta Was this translation helpful? Give feedback.
-
I implemented a small experiment allowing adding a query to a path. Just supports
|
Beta Was this translation helpful? Give feedback.
-
I've pushed a version that supports saving collections when they are Every document in Firestore or 'piece of data' in RTDB has a string key. Sometimes it makes sense to include that string key as an id in your data entity, but not always. This makes it not straight forward to figure out under what key to store that data. If we have an This means that you currently need to provide an id when adding a new entity to a collection. And you may actually not care about it. That needs to be considered somehow. If we have an I think that the user could get full control of the latter by adding a protocol to your entity that means: My |
Beta Was this translation helpful? Give feedback.
-
Dear all,
I've been wanting to use Firebase with TCA for a long time, but I haven't been able to see a good fit with real-time updating values and the
Reducer
until the great series about shared state.Yesterday I started some experimentation with adding a firebase strategy, and I think that it could become a quite nice API.
My goals are:
Codable
entity and path to a collection would synchronize arrays, etc.(*) Could one imagine a read-only version of
@Shared
? I know it's not the basic use case for shared state, but for something like a live-updating query into some storage I think it could make sense. For now I just have an empty implementation of saving when dealing with collections.Example of using the strategy:
First, you need to model the schema for your database:
This schema defines that in the root of your database, you have a collection of
Chatroom
entities at the path/chatrooms
A
CollectionPath
has a built-inchild(_ id: String)
method, that will give you an identified document in the collection.In your Reducer's state, you could then have:
Which would synchronize the data in the path
/chatrooms/firechat
as aChatroom
entity into thefirechat
shared state variable.In the following, the
firechatMessages
will be synchronized with all of theMessage
s located in/chatrooms/firechat/messages
.Through the type-safe schema, the type of
firechatMessages
will automatically be inferred to be[Message]
.It is possible to overload the strategy with multiple 'versions' of subscribing to a collection. For instance, if you want to also get the ids of the entities in your collection, you could use the overload:
Where
KeyPair
is a generic that holds aString
key and the generic value. This could be exposed as[(String, Message)]
in the future, but currently that type is notEquatable
(even though I expected(String, Message)
to be implicitlyEquatable
due to SE-0283 ).Well, I expect that could be fixed using variadic generics.
But the point is: Based on the shape of the collection you give to your shared data, you can control details about the synchronization.
Another overload could be made for unordered keys and values like this: (I haven't implemented this yet):
With regards to the goals:
Use same API for firestore and the realtime database
Not yet done.
The database type (together with other details like database id and region, in case you have multiple databases) could be encoded in the
FirebasePath
type, so that the schema defines where your data lives, and the user would not need to know or care.Allow user to model a type-safe 'path' hierarchy that serves as a schema for your database(s)
Done
Model document and collection access differently in the path hierarchy
Done
Read-only access to collections
Well, currently it's just done with an empty implementation. This could be a
fatalError
or a runtime warning, but optimally it could be defined in the type system somehow.Allow access to collections to be dictated by the type of the variable annotated with the property wrapper
Yup, that works!
Considerations
Type-safe paths are too verbose
When spelling out a type-safe path, it's currently done with
FirebasePath().yourCollection.etc
. The only public initializer forFirebasePath
gives aFirebasePath<FirebasePaths.Root>
, and this basically defines the root of your schema.But I really don't like having to spell it out every time. If you could just do
.yourCollection.etc
it would look much nicer, but theFirebasePath
can't be made static because the type actually carries state, and you could easily imagine having the same entity type stored in different locations, so in that case the paths can't statically be known from the entity type.I hope that it would be possible to abbreviate this, to make initializing the firebase strategy look nicer.
Queries would be nice
Having access to a collection of data is fine, but the next step would probably be to add support for queries as well. Queries in Firestore and RTDB have different properties, so it would be interesting to see if those could be combined somehow.
Subscriptions to data are made when
State
is initializedPerhaps it is not a real issue, but in case you initialize state for a lot of features directly from the entry point of your app, and each state perhaps has a lot of shared data subscriptions, this might cause a lot of data to start being synchronized.
In case this were an actual issue, perhaps some kind of delayed initialization of synchronization of shared data could be implemented?
Beta Was this translation helpful? Give feedback.
All reactions