Description
Is there an existing issue for this?
- I have searched the existing issues.
Which plugins are affected?
Core, Cloud_Firestore
Which platforms are affected?
iOS
Description
On an iOS device, using includeMetadataChanges
does not work correctly for single document snapshots (which works correctly for collection snapshots which was fixed in this week's commit #12739) for both:
- First query execution
- Regaining/Losing connection to the database
The following examples were tested on an iOS Simulator, but the same problem exists on real devices as well.
These examples compare the behavior of Collection snapshots (which work as expected) and Document snapshots (that behave unexpectedly).
Example: Collection Snapshot (works as expected)
final collectionRef = FirebaseFirestore.instance.collection('col');
colectionRef
.snapshots(includeMetadataChanges: true)
.listen((event) => print('FromCache: ${event.metadata.isFromCache}'));
Launching the app
When we launch the app, first we receive data from the cache and then we receive data from the database (as expected):
flutter: FromCache: true
flutter: FromCache: false
Regaining connection to DB
Process: After disconnecting the simulator from an internet connection, I will send a modification to the database. Then I will enable the internet connection.
final update = {'ts': DateTime.now().toIso8601String()};
collectionRef.doc('doc').update(update);
Received events (as expected):
flutter: FromCache: true // This event was received by disconnecting the internet connection
flutter: FromCache: true // Received by sending a modification
flutter: FromCache: false // Received when connected to the internet
flutter: FromCache: false // Received after all modifications have been written to DB
Example: Document Snapshot (incorrect behavior)
final documentRef = FirebaseFirestore.instance.collection('col').doc('doc');
documentRef
.snapshots(includeMetadataChanges: true)
.listen((event) => print('FromCache: ${event.metadata.isFromCache}'));
Launching the app (for the second time - the first time we don't have any data cached so we will receive the event fromCache: false
)
When we launch the app, we receive data from the cache, but we do not receive the event from the server (which would be expected).
flutter: FromCache: true
The FromCache: false
event is not received when the query is first executed, but it will be received only after data is updated in the database (or we send a modification from the app).
Regaining connection to DB
Process: After disconnecting the simulator from an internet connection, I will send a modification to the database. Then I will enable the internet connection.
final update = {'ts': DateTime.now().toIso8601String()};
collectionRef.doc('doc').update(update);
Received events:
flutter: FromCache: true // Event received after sending the modification
As opposed to Collection Reference, we did not receive the event fromCache: true
after we lost the database connection. We also do not receive fromCache: false
events after the connection is regained nor after all modifications are written.
We will receive the fromCache: false
event only after the data in the database is updated which is not expected behavior.
Expected behavior:
Both single document snapshots and collection snapshots should behave consistently.
The correct behavior is of the collection snapshots.
Appendix:
This issue has already been reported once for collection snapshots: #12722.
The conclusion was a new ticket to ios sdk (firebase/firebase-ios-sdk#12869), where the solution was to change addSnapshotListenerWithOptions:options
to addSnapshotListenerWithOptions:optionsWithSourceAndMetadata
.
Thus a bug fix was pushed to flutterfire.
However going into the fix (#12739), I noticed that only file FLTQuerySnapshotStreamHandler.m
was updated with this change, but file FLTDocumentSnapshotStreamHandler.m
does not have these changes applied.
Reproducing the issue
Create a new Flutter app and connect it to Firebase. In this app, create a new listener that will observe document snapshots with metadata changes.
final documentRef = FirebaseFirestore.instance.collection('col').doc('doc');
documentsRef
.snapshots(includeMetadataChanges: true)
.listen((event) => print('FromCache: ${event.metadata.isFromCache}'));
Test both of these scenarios:
- First query execution
- Regaining/Losing connection to the database
An app I used for testing is available at:
https://github.com/TypeSoft-Ltd/firebase-metadata-changes-bug
To test different behavior between collection and document snapshots, change the useDocumentReference
bool property to true
and false
on widget TestMetadataChangesPage
in the app/widget/app_root.dart
Firebase Core version
2.31.0
Flutter Version
3.19.5
Relevant Log Output
No response
Flutter dependencies
Expand Flutter dependencies
snippet
Dart SDK 3.3.3
Flutter SDK 3.19.5
firebase_metadata_changes 1.0.0+1
dependencies:
- cloud_firestore 4.17.3 [cloud_firestore_platform_interface cloud_firestore_web collection firebase_core firebase_core_platform_interface flutter meta]
- cupertino_icons 1.0.8
- firebase_core 2.31.0 [firebase_core_platform_interface firebase_core_web flutter meta]
- flutter 0.0.0 [characters collection material_color_utilities meta vector_math sky_engine]
dev dependencies:
- flutter_lints 3.0.2 [lints]
- flutter_test 0.0.0 [flutter test_api matcher path fake_async clock stack_trace vector_math leak_tracker_flutter_testing async boolean_selector characters collection leak_tracker leak_tracker_testing material_color_utilities meta source_span stream_channel string_scanner term_glyph vm_service]
transitive dependencies:
- _flutterfire_internals 1.3.33 [collection firebase_core firebase_core_platform_interface flutter meta]
- async 2.11.0 [collection meta]
- boolean_selector 2.1.1 [source_span string_scanner]
- characters 1.3.0
- clock 1.1.1
- cloud_firestore_platform_interface 6.2.3 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface]
- cloud_firestore_web 3.12.3 [_flutterfire_internals cloud_firestore_platform_interface collection firebase_core firebase_core_web flutter flutter_web_plugins]
- collection 1.18.0
- fake_async 1.3.1 [clock collection]
- firebase_core_platform_interface 5.0.0 [collection flutter flutter_test meta plugin_platform_interface]
- firebase_core_web 2.17.0 [firebase_core_platform_interface flutter flutter_web_plugins meta web]
- flutter_web_plugins 0.0.0 [flutter characters collection material_color_utilities meta vector_math]
- leak_tracker 10.0.0 [clock collection meta path vm_service]
- leak_tracker_flutter_testing 2.0.1 [flutter leak_tracker leak_tracker_testing matcher meta]
- leak_tracker_testing 2.0.1 [leak_tracker matcher meta]
- lints 3.0.0
- matcher 0.12.16+1 [async meta stack_trace term_glyph test_api]
- material_color_utilities 0.8.0 [collection]
- meta 1.11.0
- path 1.9.0
- plugin_platform_interface 2.1.8 [meta]
- sky_engine 0.0.99
- source_span 1.10.0 [collection path term_glyph]
- stack_trace 1.11.1 [path]
- stream_channel 2.1.2 [async]
- string_scanner 1.2.0 [source_span]
- term_glyph 1.2.1
- test_api 0.6.1 [async boolean_selector collection meta source_span stack_trace stream_channel string_scanner term_glyph]
- vector_math 2.1.4
- vm_service 13.0.0
- web 0.5.1
Expand flutter doctor -v
snippet
[✓] Flutter (Channel stable, 3.19.5, on macOS 14.0 23A344 darwin-arm64, locale en-CZ)
• Flutter version 3.19.5 on channel stable at /Users/pavel/fvm/versions/3.19.5
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 300451adae (6 weeks ago), 2024-03-27 21:54:07 -0500
• Engine revision e76c956498
• Dart version 3.3.3
• DevTools version 2.31.1
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
• Android SDK at /Users/pavel/Library/Android/sdk
• Platform android-34, build-tools 34.0.0
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 17.0.9+0-17.0.9b1087.7-11185874)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 15.0)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 15A240d
• CocoaPods version 1.15.2
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2023.2)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 17.0.9+0-17.0.9b1087.7-11185874)
[✓] VS Code (version 1.89.0)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.88.0
[✓] Connected device (4 available)
• sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64 • Android 13 (API 33)
(emulator)
• iPhone 15 Pro Max (mobile) • 41E888F5-2E90-412D-89AA-89D1409FFF49 • ios •
com.apple.CoreSimulator.SimRuntime.iOS-17-0 (simulator)
• macOS (desktop) • macos • darwin-arm64 • macOS 14.0 23A344
darwin-arm64
• Chrome (web) • chrome • web-javascript • Google Chrome 124.0.6367.119
! Error: Browsing on the local area network for Development’s iPhone. Ensure the device is unlocked and attached
with a cable or associated with the same local area network as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)
[✓] Network resources
• All expected network resources are available.
• No issues found!
Additional context and comments
No response