-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Firestore platform interface (v2) #1633
Conversation
method channel implementations.
port tests to the new interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initial comments as this is still WIP
|
||
class MethodChannelDocumentReference extends DocumentReferencePlatform { | ||
MethodChannelDocumentReference() { | ||
channel.setMethodCallHandler(_callHandler); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like you are calling setMethodCallHandler
multiple times. I think this will cause the previous handler for that channel to be overwritten.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, though each instance of the platform has its own copy of the MethodChannel (I HOPE)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hterkelsen you were right, even across different instances of a MethodChannel, only the LAST call to setMethodCallHandler
seems to be called.
@mklim said: "There's only one handler per channel. A way to thin the Handler code is to actually move the implementation of the methods into other classes, and write the Handler itself just to plumb from method call and response to the business logic and back."
} | ||
|
||
@visibleForTesting | ||
static const MethodChannel channel = MethodChannel( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the original implementation I see that the channel is initialized with FirestoreMessageCodec
. You probably need to move FirestoreMessageCodec
to this package and make sure the channels are initialized with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I think I didn't realize this. I'll get this done!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh my, this codec complicates things 😭
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because of how the Codec operates (it basically uses the frontend of the plugin to decode some data types), I changed the initialization code of the MethodChannel Implementations to allow to pass a codec in; that way we can pass a codec with access to "userland" from the plugin, without having to migrate EVERY data type defined in the plugin (I tried, it was painful, and still didn't solve my issue).
This is the problematic bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code above probably explains why there's so much "static" code, and initialization guards in different places of the plugin; since we're creating new instances of the plugin all the time the message codec needs to hydrate a serialized DocumentReference!
Fixing this is very involved; we may be able to inject a factory function so the codec doesn't need to create instances in there, but maybe this particular part of the plugin could benefit from some extra design work (I didn't find any other place where layers are pierced like this, so far)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is hydrating a serialized DocumentReference expensive? It's not clear to me how much overhead is involved in that, compared to the general overhead of moving serialized snapshots in and out of the Dart VM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, it is not very expensive because:
- There's a bunch of init guards/static code that prevents multiple initialization
- We hydrate the moment we see a DcoumentReference that needs to be hydrated.
However, logic-wise, it is expensive, because the deserialization needs to be able to basically instantiate a whole new plugin.
If we want to break that up, we'd probably need to hoist all the data types to the platform interface. At that moment, to maintain current feature parity, we'd need hydration on getters (or similar). The cost of that hydration would depend on the complexity of the returned data.
(Or maybe there's something I'm not seeing, there's a chance I've been focusing on this code for too long already :P)
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
class PlatformWriteBatch { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs. Also I think it would be clearer if this was named PlatformWriteBatchHandle
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, docs are missing pretty much everywhere. I'm giving myself some time before I migrate this to add *Handle at the end, but I agree it's clearer.
} | ||
|
||
// TODO: this needs to be more precise | ||
class PlatformQuerySnapshot { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should probably model this class on https://firebase.google.com/docs/reference/android/com/google/firebase/firestore/QuerySnapshot
I would remove asMap()
as well, and make the app-facing plugin use the typed data
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I've been avoiding this type for a while. I have a suspicion that the data that the backend sends back through the channel to the frontend is not entirely identical to the one documented, so I'll model this as I go (I might be wrong)
...loud_firestore/cloud_firestore_platform_interface/lib/src/interfaces/firestore_platform.dart
Outdated
Show resolved
Hide resolved
a custom MessageCodec, that comes from userland. Removed auto-init from the platform interfaces, so the plugin can pass the correct MessageCodec.
MethodChannelFirestore platform.
`PlatformWriteBatchHandle`
implementation.
Some more tests are passing now; after adding the Transaction implementation:
|
This way, all MethodChannel implementations can share the same MethodChannel instance, and register additional method handlers after the class has been created.
Also, simplify test initialization now that all MethodChannel implementations share the same instance.
Added Query implementation:
|
Added DocumentReference implementation.
|
(Currently fixing dartfmt and analyzer) |
_instance = instance; | ||
} | ||
|
||
// Actual interface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am leaning towards removing this non-Dartdoc comment (in all PlatformInterface
subclasses) since people will be follow the example of this plugin and it clutters up the class a bit. If the code is self-documenting only Dartdoc comments will be necessary.
|
||
typedef _MultiMethodChannelHandler(MethodCall call); | ||
|
||
class MultiMethodChannel extends MethodChannel { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if we should move this into a package (in the Flutter SDK? in the plugin_platform_interface package?) since it will be useful for firebase_database and firebase_storage. /cc @amirh
@@ -15,6 +15,8 @@ flutter: | |||
pluginClass: FLTCloudFirestorePlugin | |||
|
|||
dependencies: | |||
cloud_firestore_platform_interface: | |||
path: ../cloud_firestore_platform_interface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should eventually be pointing to a hosted version (probably by splitting this into two PRs)
'transactionTimeout': timeout.inMilliseconds | ||
}); | ||
|
||
// Wrap the user-supplied [TransactionHandler] into something that can be passed to the Platform implementation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Wrap the user-supplied [TransactionHandler] into something that can be passed to the Platform implementation. | |
// Wrap the user-supplied [TransactionHandler] into something that can be passed to the platform implementation. |
}; | ||
|
||
// Ensure the MethodChannel platforms are initialized | ||
Firestore.platform; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels unintuitive that this has side effects. What do you think about doing a method instead?
// Ensure the MethodChannel platforms are initialized | ||
Firestore.platform; | ||
|
||
// All the MethodChannel implementation share the same channel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// All the MethodChannel implementation share the same channel | |
// All the MethodChannel implementations share the same channel |
|
||
/// The platform instance that talks to the native side of the plugin. | ||
@visibleForTesting | ||
static final FirestorePlatform platform = FirestorePlatform.instance ?? (FirestorePlatform.instance = MethodChannelFirestore(FirestoreMessageCodec())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems a bit unintuitive to be doing the assignment like this.
(Closing in favor of #1670) |
Description
Continuation of the WIP for the cloud_firestore federation; platform interface + core plugin changes.
This v2 attempts to address the concerns raised in #1568.
This is still incomplete, but I've implemented the minimal scaffolding code so I can run the firestore core plugin tests on top of the new implementation. I'm using that test output to track progress, and ensure feature parity with the old version.
Missing things:
00:02 +23 -23: Some tests failed.
cloud_firestore_platform_interface
package.Related Issues
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]
). This will ensure a smooth and quick review process.///
).flutter analyze
) does not report any problems on my PR.Breaking Change
Does your PR require plugin users to manually update their apps to accommodate your change?