Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/firebase_database/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 0.0.8

* Added missing offline persistence and query functionality on Android
* Fixed startAt query behavior on iOS
* Persistence methods no longer throw errors on failure, return false instead
* Updates to docs and tests

## 0.0.7

* Fixed offline persistence on iOS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseException;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;
import com.google.firebase.database.ValueEventListener;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
Expand All @@ -31,7 +33,7 @@ public class FirebaseDatabasePlugin implements MethodCallHandler {

// Handles are ints used as indexes into the sparse array of active observers
private int nextHandle = 0;
private final SparseArray<EventObserver> observers = new SparseArray<EventObserver>();
private final SparseArray<EventObserver> observers = new SparseArray<>();

public static void registerWith(PluginRegistry.Registrar registrar) {
final MethodChannel channel =
Expand All @@ -43,14 +45,68 @@ private FirebaseDatabasePlugin(MethodChannel channel) {
this.channel = channel;
}

private static DatabaseReference getReference(Map<String, ?> arguments) {
@SuppressWarnings("unchecked")
private DatabaseReference getReference(Map<String, Object> arguments) {
String path = (String) arguments.get("path");
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
if (path != null) reference = reference.child(path);
return reference;
}

private Query getQuery(Map<String, Object> arguments) {
Query query = getReference(arguments);
@SuppressWarnings("unchecked")
Map<String, Object> parameters = (Map<String, Object>) arguments.get("parameters");
Object orderBy = parameters.get("orderBy");
if ("child".equals(orderBy)) {
query = query.orderByChild((String) parameters.get("orderByChildKey"));
} else if ("key".equals(orderBy)) {
query = query.orderByKey();
} else if ("value".equals(orderBy)) {
query = query.orderByValue();
} else if ("priority".equals(orderBy)) {
query = query.orderByPriority();
}
if (parameters.containsKey("startAt")) {
Object startAt = parameters.get("startAt");
String startAtKey = (String) parameters.get("startAtKey");
if (startAt instanceof Boolean) {
query = query.startAt((Boolean) startAt, startAtKey);
} else if (startAt instanceof String) {
query = query.startAt((String) startAt, startAtKey);
} else {
query = query.startAt((Double) startAt, startAtKey);
}
}
if (parameters.containsKey("endAt")) {
Object endAt = parameters.get("endAt");
String endAtKey = (String) parameters.get("endAtKey");
if (endAt instanceof Boolean) {
query = query.endAt((Boolean) endAt, endAtKey);
} else if (endAt instanceof String) {
query = query.endAt((String) endAt, endAtKey);
} else {
query = query.endAt((Double) endAt, endAtKey);
}
}
if (arguments.containsKey("equalTo")) {
Object equalTo = arguments.get("equalTo");
if (equalTo instanceof Boolean) {
query = query.equalTo((Boolean) equalTo);
} else if (equalTo instanceof String) {
query = query.equalTo((String) equalTo);
} else {
query = query.equalTo((Double) equalTo);
}
}
if (arguments.containsKey("limitToFirst")) {
query = query.limitToFirst((int) arguments.get("limitToFirst"));
}
if (arguments.containsKey("limitToLast")) {
query = query.limitToLast((int) arguments.get("limitToLast"));
}
return query;
}

private class DefaultCompletionListener implements DatabaseReference.CompletionListener {
private final Result result;

Expand Down Expand Up @@ -79,8 +135,8 @@ private class EventObserver implements ChildEventListener, ValueEventListener {

private void sendEvent(String eventType, DataSnapshot snapshot, String previousChildName) {
if (eventType.equals(requestedEventType)) {
Map<String, Object> arguments = new HashMap<String, Object>();
Map<String, Object> snapshotMap = new HashMap<String, Object>();
Map<String, Object> arguments = new HashMap<>();
Map<String, Object> snapshotMap = new HashMap<>();
snapshotMap.put("key", snapshot.getKey());
snapshotMap.put("value", snapshot.getValue());
arguments.put("handle", handle);
Expand Down Expand Up @@ -119,34 +175,54 @@ public void onDataChange(DataSnapshot snapshot) {
}
}

@SuppressWarnings("unchecked")
@Override
public void onMethodCall(MethodCall call, final Result result) {
Map<String, Object> arguments = (Map<String, Object>) call.arguments;
Map<String, Object> arguments = call.arguments();
switch (call.method) {
case "FirebaseDatabase#goOnline":
FirebaseDatabase.getInstance().goOnline();
break;
{
FirebaseDatabase.getInstance().goOnline();
result.success(null);
break;
}

case "FirebaseDatabase#goOffline":
FirebaseDatabase.getInstance().goOffline();
break;
{
FirebaseDatabase.getInstance().goOffline();
result.success(null);
break;
}

case "FirebaseDatabase#purgeOutstandingWrites":
FirebaseDatabase.getInstance().purgeOutstandingWrites();
break;
{
FirebaseDatabase.getInstance().purgeOutstandingWrites();
result.success(null);
break;
}

case "FirebaseDatabase#setPersistenceEnabled":
{
boolean isEnabled = (boolean) arguments.get("enabled");
FirebaseDatabase.getInstance().setPersistenceEnabled(isEnabled);
Boolean isEnabled = (Boolean) arguments.get("enabled");
try {
FirebaseDatabase.getInstance().setPersistenceEnabled(isEnabled);
result.success(true);
} catch (DatabaseException e) {
// Database is already in use, e.g. after hot reload/restart.
result.success(false);
}
break;
}

case "FirebaseDatabase#setPersistenceCacheSizeBytes":
{
long cacheSize = (long) arguments.get("cacheSize");
FirebaseDatabase.getInstance().setPersistenceCacheSizeBytes(cacheSize);
Long cacheSize = (Long) arguments.get("cacheSize");
try {
FirebaseDatabase.getInstance().setPersistenceCacheSizeBytes(cacheSize);
result.success(true);
} catch (DatabaseException e) {
// Database is already in use, e.g. after hot reload/restart.
result.success(false);
}
break;
}

Expand All @@ -171,31 +247,38 @@ public void onMethodCall(MethodCall call, final Result result) {
break;
}

case "Query#keepSynced":
{
boolean value = (Boolean) arguments.get("value");
getQuery(arguments).keepSynced(value);
break;
}

case "Query#observe":
{
String eventType = (String) arguments.get("eventType");
int handle = nextHandle++;
EventObserver observer = new EventObserver(eventType, handle);
observers.put(handle, observer);
if (eventType.equals(EVENT_TYPE_VALUE)) {
getReference(arguments).addValueEventListener(observer);
getQuery(arguments).addValueEventListener(observer);
} else {
getReference(arguments).addChildEventListener(observer);
getQuery(arguments).addChildEventListener(observer);
}
result.success(handle);
break;
}

case "Query#removeObserver":
{
DatabaseReference reference = getReference(arguments);
Query query = getQuery(arguments);
int handle = (Integer) arguments.get("handle");
EventObserver observer = observers.get(handle);
if (observer != null) {
if (observer.requestedEventType.equals(EVENT_TYPE_VALUE)) {
reference.removeEventListener((ValueEventListener) observer);
query.removeEventListener((ValueEventListener) observer);
} else {
reference.removeEventListener((ChildEventListener) observer);
query.removeEventListener((ChildEventListener) observer);
}
observers.delete(handle);
result.success(null);
Expand Down
2 changes: 2 additions & 0 deletions packages/firebase_database/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
FirebaseDatabase.instance.setPersistenceEnabled(true);
_counterRef.keepSynced(true);
_counterSubscription = _counterRef.onValue.listen((Event event) {
setState(() {
_counter = event.snapshot.value ?? 0;
Expand Down
29 changes: 26 additions & 3 deletions packages/firebase_database/ios/Classes/FirebaseDatabasePlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ - (FlutterError *)flutterError {
}
id startAt = parameters[@"startAt"];
if (startAt) {
query = [query queryStartingAtValue:startAt childKey:parameters[@"endAtKey"]];
query = [query queryStartingAtValue:startAt childKey:parameters[@"startAtKey"]];
}
id endAt = parameters[@"endAt"];
if (endAt) {
Expand Down Expand Up @@ -109,16 +109,39 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
};
if ([@"FirebaseDatabase#goOnline" isEqualToString:call.method]) {
[[FIRDatabase database] goOnline];
result(nil);
} else if ([@"FirebaseDatabase#goOffline" isEqualToString:call.method]) {
[[FIRDatabase database] goOffline];
result(nil);
} else if ([@"FirebaseDatabase#purgeOutstandingWrites" isEqualToString:call.method]) {
[[FIRDatabase database] purgeOutstandingWrites];
result(nil);
} else if ([@"FirebaseDatabase#setPersistenceEnabled" isEqualToString:call.method]) {
NSNumber *value = call.arguments[@"enabled"];
[FIRDatabase database].persistenceEnabled = value.boolValue;
@try {
[FIRDatabase database].persistenceEnabled = value.boolValue;
result([NSNumber numberWithBool:YES]);
} @catch (NSException *exception) {
if ([@"FIRDatabaseAlreadyInUse" isEqualToString:exception.name]) {
// Database is already in use, e.g. after hot reload/restart.
result([NSNumber numberWithBool:NO]);
} else {
@throw;
}
}
} else if ([@"FirebaseDatabase#setPersistenceCacheSizeBytes" isEqualToString:call.method]) {
NSNumber *value = call.arguments[@"cacheSize"];
[FIRDatabase database].persistenceCacheSizeBytes = value.unsignedIntegerValue;
@try {
[FIRDatabase database].persistenceCacheSizeBytes = value.unsignedIntegerValue;
result([NSNumber numberWithBool:YES]);
} @catch (NSException *exception) {
if ([@"FIRDatabaseAlreadyInUse" isEqualToString:exception.name]) {
// Database is already in use, e.g. after hot reload/restart.
result([NSNumber numberWithBool:NO]);
} else {
@throw;
}
}
} else if ([@"DatabaseReference#set" isEqualToString:call.method]) {
[getReference(call.arguments) setValue:call.arguments[@"value"]
andPriority:call.arguments[@"priority"]
Expand Down
4 changes: 2 additions & 2 deletions packages/firebase_database/lib/src/database_reference.dart
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ class DatabaseReference extends Query {
/// priority (including no priority), they are sorted by key. Numeric keys
/// come first (sorted numerically), followed by the remaining keys (sorted
/// lexicographically).

///
/// Note that priorities are parsed and ordered as IEEE 754 double-precision
/// floating-point numbers. Keys are always stored as strings and are treated
/// as numbers only when they can be parsed as a 32-bit integer
/// as numbers only when they can be parsed as a 32-bit integer.
Future<Null> setPriority(dynamic priority) async {
return _database._channel.invokeMethod(
'DatabaseReference#setPriority',
Expand Down
51 changes: 32 additions & 19 deletions packages/firebase_database/lib/src/firebase_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,52 +32,65 @@ class FirebaseDatabase {
/// Gets a DatabaseReference for the root of your Firebase Database.
DatabaseReference reference() => new DatabaseReference._(this, <String>[]);

/// Attempts to sets the database persistence to [enabled].
///
/// This property must be set before calling methods on database references
/// and only needs to be called once per application. The returned `Future`
/// will complete with `true` if the operation was successful or `false` if
/// the persistence could not be set (because database references have
/// already been created).
///
/// The Firebase Database client will cache synchronized data and keep track
/// of all writes you’ve initiated while your application is running. It
/// seamlessly handles intermittent network connections and re-sends write
/// operations when the network connection is restored.
///
/// However by default your write operations and cached data are only stored
/// in-memory and will be lost when your app restarts. By setting this value
/// to YES, the data will be persisted to on-device (disk) storage and will
/// in-memory and will be lost when your app restarts. By setting [enabled]
/// to `true`, the data will be persisted to on-device (disk) storage and will
/// thus be available again when the app is restarted (even when there is no
/// network connectivity at that time). Note that this property must be set
/// before creating your first Database reference and only needs to be called
/// once per application.
Future<Null> setPersistenceEnabled(bool enabled) {
/// network connectivity at that time).
Future<bool> setPersistenceEnabled(bool enabled) {
return _channel.invokeMethod(
"FirebaseDatabase#setPersistenceEnabled",
<String, dynamic>{'enabled': enabled},
'FirebaseDatabase#setPersistenceEnabled',
<String, bool>{'enabled': enabled},
);
}

/// Attempts to set the size of the persistence cache.
///
/// By default the Firebase Database client will use up to 10MB of disk space
/// to cache data. If the cache grows beyond this size, the client will start
/// removing data that hasn’t been recently used. If you find that your
/// application caches too little or too much data, call this method to change
/// the cache size. This property must be set before creating your first
/// FIRDatabaseReference and only needs to be called once per application.
/// the cache size.
///
/// This property must be set before calling methods on database references
/// and only needs to be called once per application. The returned `Future`
/// will complete with `true` if the operation was successful or `false` if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Future] instead of Future?

/// the value could not be set (because database references have already been
/// created).
///
/// Note that the specified cache size is only an approximation and the size
/// on disk may temporarily exceed it at times. Cache sizes smaller than 1 MB
/// or greater than 100 MB are not supported.
Future<Null> setPersistenceCacheSizeBytes(int cacheSize) {
Future<bool> setPersistenceCacheSizeBytes(int cacheSize) {
return _channel.invokeMethod(
"FirebaseDatabase#setPersistenceCacheSizeBytes",
<String, dynamic>{'cacheSize': cacheSize},
'FirebaseDatabase#setPersistenceCacheSizeBytes',
<String, int>{'cacheSize': cacheSize},
);
}

/// Resumes our connection to the Firebase Database backend after a previous
/// goOffline call.
/// [goOffline] call.
Future<Null> goOnline() {
return _channel.invokeMethod("FirebaseDatabase#goOnline");
return _channel.invokeMethod('FirebaseDatabase#goOnline');
}

/// Shuts down our connection to the Firebase Database backend until goOnline
/// is called.
/// Shuts down our connection to the Firebase Database backend until
/// [goOnline] is called.
Future<Null> goOffline() {
return _channel.invokeMethod("FirebaseDatabase#goOffline");
return _channel.invokeMethod('FirebaseDatabase#goOffline');
}

/// The Firebase Database client automatically queues writes and sends them to
Expand All @@ -91,6 +104,6 @@ class FirebaseDatabase {
/// affected event listeners, and the client will not (re-)send them to the
/// Firebase Database backend.
Future<Null> purgeOutstandingWrites() {
return _channel.invokeMethod("FirebaseDatabase#purgeOutstandingWrites");
return _channel.invokeMethod('FirebaseDatabase#purgeOutstandingWrites');
}
}
Loading