Skip to content

Commit

Permalink
Multiple app support for cloud_firestore (flutter#476)
Browse files Browse the repository at this point in the history
  • Loading branch information
collinjackson authored and slightfoot committed Jun 5, 2018
1 parent 78174b9 commit 3f589f9
Show file tree
Hide file tree
Showing 16 changed files with 279 additions and 125 deletions.
5 changes: 5 additions & 0 deletions packages/cloud_firestore/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.5.1

* Expose the Firebase app corresponding to a Firestore
* Expose a constructor for a Firestore with a non-default Firebase app

## 0.5.0

* **Breaking change**. Move path getter to CollectionReference
Expand Down
1 change: 0 additions & 1 deletion packages/cloud_firestore/android/gradle.properties

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.TaskCompletionSource;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.firestore.CollectionReference;
import com.google.firebase.firestore.DocumentChange;
import com.google.firebase.firestore.DocumentReference;
Expand Down Expand Up @@ -72,14 +73,19 @@ private CloudFirestorePlugin(MethodChannel channel) {
this.channel = channel;
}

private FirebaseFirestore getFirestore(Map<String, Object> arguments) {
String appName = (String) arguments.get("app");
return FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName));
}

private CollectionReference getCollectionReference(Map<String, Object> arguments) {
String path = (String) arguments.get("path");
return FirebaseFirestore.getInstance().collection(path);
return getFirestore(arguments).collection(path);
}

private DocumentReference getDocumentReference(Map<String, Object> arguments) {
String path = (String) arguments.get("path");
return FirebaseFirestore.getInstance().document(path);
return getFirestore(arguments).document(path);
}

private Map<String, Object> parseQuerySnapshot(QuerySnapshot querySnapshot) {
Expand Down Expand Up @@ -248,7 +254,7 @@ public void onMethodCall(MethodCall call, final Result result) {
final Task<Map<String, Object>> transactionTCSTask = transactionTCS.getTask();

final Map<String, Object> arguments = call.arguments();
FirebaseFirestore.getInstance()
getFirestore(arguments)
.runTransaction(
new Transaction.Function<Void>() {
@Nullable
Expand Down Expand Up @@ -379,7 +385,8 @@ protected Void doInBackground(Void... voids) {
case "WriteBatch#create":
{
int handle = nextBatchHandle++;
WriteBatch batch = FirebaseFirestore.getInstance().batch();
final Map<String, Object> arguments = call.arguments();
WriteBatch batch = getFirestore(arguments).batch();
batches.put(handle, batch);
result.success(handle);
break;
Expand Down Expand Up @@ -573,6 +580,8 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) {
writeDouble(stream, ((GeoPoint) value).getLongitude());
} else if (value instanceof DocumentReference) {
stream.write(DOCUMENT_REFERENCE);
writeBytes(
stream, ((DocumentReference) value).getFirestore().getApp().getName().getBytes(UTF8));
writeBytes(stream, ((DocumentReference) value).getPath().getBytes(UTF8));
} else {
super.writeValue(stream, value);
Expand All @@ -588,9 +597,13 @@ protected Object readValueOfType(byte type, ByteBuffer buffer) {
readAlignment(buffer, 8);
return new GeoPoint(buffer.getDouble(), buffer.getDouble());
case DOCUMENT_REFERENCE:
final byte[] bytes = readBytes(buffer);
final String path = new String(bytes, UTF8);
return FirebaseFirestore.getInstance().document(path);
final byte[] appNameBytes = readBytes(buffer);
String appName = new String(appNameBytes, UTF8);
final FirebaseFirestore firestore =
FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName));
final byte[] pathBytes = readBytes(buffer);
final String path = new String(pathBytes, UTF8);
return firestore.document(path);
default:
return super.readValueOfType(type, buffer);
}
Expand Down
34 changes: 28 additions & 6 deletions packages/cloud_firestore/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,34 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

void main() {
runApp(new MaterialApp(title: 'Firestore Example', home: new MyHomePage()));
Future<void> main() async {
final FirebaseApp app = await FirebaseApp.configure(
name: 'test',
options: const FirebaseOptions(
googleAppID: '1:79601577497:ios:5f2bcc6ba8cecddd',
gcmSenderID: '79601577497',
apiKey: 'AIzaSyArgmRGfB5kiQT6CunAOmKRVKEsxKmy6YI-G72PVU',
projectID: 'flutter-firestore',
),
);
final Firestore firestore = new Firestore(app: app);

runApp(new MaterialApp(
title: 'Firestore Example', home: new MyHomePage(firestore: firestore)));
}

class MessageList extends StatelessWidget {
MessageList({this.firestore});

final Firestore firestore;

@override
Widget build(BuildContext context) {
return new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('messages').snapshots,
stream: firestore.collection('messages').snapshots,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
final int messageCount = snapshot.data.documents.length;
Expand All @@ -35,10 +52,15 @@ class MessageList extends StatelessWidget {
}

class MyHomePage extends StatelessWidget {
CollectionReference get messages => Firestore.instance.collection('messages');
MyHomePage({this.firestore});
final Firestore firestore;
CollectionReference get messages => firestore.collection('messages');

Future<Null> _addMessage() async {
messages.document().setData(<String, String>{'message': 'Hello world!'});
final DocumentReference document = messages.document();
document.setData(<String, dynamic>{
'message': 'Hello world!',
});
}

@override
Expand All @@ -47,7 +69,7 @@ class MyHomePage extends StatelessWidget {
appBar: new AppBar(
title: const Text('Firestore Example'),
),
body: new MessageList(),
body: new MessageList(firestore: firestore),
floatingActionButton: new FloatingActionButton(
onPressed: _addMessage,
tooltip: 'Increment',
Expand Down
1 change: 1 addition & 0 deletions packages/cloud_firestore/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ dependencies:
sdk: flutter
cloud_firestore:
path: ../
firebase_core: "^0.2.1"

flutter:
uses-material-design: true
96 changes: 48 additions & 48 deletions packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,17 @@ - (FlutterError *)flutterError {
}
@end

FIRFirestore *getFirestore(NSDictionary *arguments) {
FIRApp *app = [FIRApp appNamed:arguments[@"app"]];
return [FIRFirestore firestoreForApp:app];
}

FIRDocumentReference *getDocumentReference(NSDictionary *arguments) {
return [getFirestore(arguments) documentWithPath:arguments[@"path"]];
}

FIRQuery *getQuery(NSDictionary *arguments) {
FIRQuery *query = [[FIRFirestore firestore] collectionWithPath:arguments[@"path"]];
FIRQuery *query = [getFirestore(arguments) collectionWithPath:arguments[@"path"]];
NSDictionary *parameters = arguments[@"parameters"];
NSArray *whereConditions = parameters[@"where"];
for (id item in whereConditions) {
Expand Down Expand Up @@ -139,9 +148,10 @@ - (void)writeValue:(id)value {
[self writeBytes:(UInt8 *)&latitude length:8];
[self writeBytes:(UInt8 *)&longitude length:8];
} else if ([value isKindOfClass:[FIRDocumentReference class]]) {
FIRDocumentReference *documentReference = value;
NSString *documentPath = [documentReference path];
FIRDocumentReference *document = value;
NSString *documentPath = [document path];
[self writeByte:DOCUMENT_REFERENCE];
[self writeUTF8:document.firestore.app.name];
[self writeUTF8:documentPath];
} else {
[super writeValue:value];
Expand Down Expand Up @@ -171,8 +181,10 @@ - (id)readValueOfType:(UInt8)type {
return [[FIRGeoPoint alloc] initWithLatitude:latitude longitude:longitude];
}
case DOCUMENT_REFERENCE: {
NSString *appName = [self readUTF8];
FIRFirestore *firestore = [FIRFirestore firestoreForApp:[FIRApp appNamed:appName]];
NSString *documentPath = [self readUTF8];
return [[FIRFirestore firestore] documentWithPath:documentPath];
return [firestore documentWithPath:documentPath];
}
default:
return [super readValueOfType:type];
Expand Down Expand Up @@ -240,8 +252,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
result(error.flutterError);
};
if ([@"Firestore#runTransaction" isEqualToString:call.method]) {
[[FIRFirestore firestore] runTransactionWithBlock:^id(FIRTransaction *transaction,
NSError **pError) {
[getFirestore(call.arguments) runTransactionWithBlock:^id(FIRTransaction *transaction,
NSError **pError) {
NSNumber *transactionId = call.arguments[@"transactionId"];
NSNumber *transactionTimeout = call.arguments[@"transactionTimeout"];

Expand Down Expand Up @@ -272,12 +284,11 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
} else if ([@"Transaction#get" isEqualToString:call.method]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSNumber *transactionId = call.arguments[@"transactionId"];
NSString *path = call.arguments[@"path"];
FIRDocumentReference *documentReference = [[FIRFirestore firestore] documentWithPath:path];
FIRDocumentReference *document = getDocumentReference(call.arguments);
FIRTransaction *transaction = transactions[transactionId];
NSError *error = [[NSError alloc] init];

FIRDocumentSnapshot *snapshot = [transaction getDocument:documentReference error:&error];
FIRDocumentSnapshot *snapshot = [transaction getDocument:document error:&error];

if (error != nil) {
result([FlutterError errorWithCode:[NSString stringWithFormat:@"%tu", [error code]]
Expand All @@ -297,58 +308,51 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
} else if ([@"Transaction#update" isEqualToString:call.method]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSNumber *transactionId = call.arguments[@"transactionId"];
NSString *path = call.arguments[@"path"];
FIRDocumentReference *documentReference = [[FIRFirestore firestore] documentWithPath:path];
FIRDocumentReference *document = getDocumentReference(call.arguments);
FIRTransaction *transaction = transactions[transactionId];

[transaction updateData:call.arguments[@"data"] forDocument:documentReference];
[transaction updateData:call.arguments[@"data"] forDocument:document];
result(nil);
});
} else if ([@"Transaction#set" isEqualToString:call.method]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSNumber *transactionId = call.arguments[@"transactionId"];
NSString *path = call.arguments[@"path"];
FIRDocumentReference *documentReference = [[FIRFirestore firestore] documentWithPath:path];
FIRDocumentReference *document = getDocumentReference(call.arguments);
FIRTransaction *transaction = transactions[transactionId];

[transaction setData:call.arguments[@"data"] forDocument:documentReference];
[transaction setData:call.arguments[@"data"] forDocument:document];
result(nil);
});
} else if ([@"Transaction#delete" isEqualToString:call.method]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSNumber *transactionId = call.arguments[@"transactionId"];
NSString *path = call.arguments[@"path"];
FIRDocumentReference *documentReference = [[FIRFirestore firestore] documentWithPath:path];
FIRDocumentReference *document = getDocumentReference(call.arguments);
FIRTransaction *transaction = transactions[transactionId];

[transaction deleteDocument:documentReference];
[transaction deleteDocument:document];
result(nil);
});
} else if ([@"DocumentReference#setData" isEqualToString:call.method]) {
NSString *path = call.arguments[@"path"];
NSDictionary *options = call.arguments[@"options"];
FIRDocumentReference *reference = [[FIRFirestore firestore] documentWithPath:path];
FIRDocumentReference *document = getDocumentReference(call.arguments);
if (![options isEqual:[NSNull null]] &&
[options[@"merge"] isEqual:[NSNumber numberWithBool:YES]]) {
[reference setData:call.arguments[@"data"]
options:[FIRSetOptions merge]
completion:defaultCompletionBlock];
[document setData:call.arguments[@"data"]
options:[FIRSetOptions merge]
completion:defaultCompletionBlock];
} else {
[reference setData:call.arguments[@"data"] completion:defaultCompletionBlock];
[document setData:call.arguments[@"data"] completion:defaultCompletionBlock];
}
} else if ([@"DocumentReference#updateData" isEqualToString:call.method]) {
NSString *path = call.arguments[@"path"];
FIRDocumentReference *reference = [[FIRFirestore firestore] documentWithPath:path];
[reference updateData:call.arguments[@"data"] completion:defaultCompletionBlock];
FIRDocumentReference *document = getDocumentReference(call.arguments);
[document updateData:call.arguments[@"data"] completion:defaultCompletionBlock];
} else if ([@"DocumentReference#delete" isEqualToString:call.method]) {
NSString *path = call.arguments[@"path"];
FIRDocumentReference *reference = [[FIRFirestore firestore] documentWithPath:path];
[reference deleteDocumentWithCompletion:defaultCompletionBlock];
FIRDocumentReference *document = getDocumentReference(call.arguments);
[document deleteDocumentWithCompletion:defaultCompletionBlock];
} else if ([@"DocumentReference#get" isEqualToString:call.method]) {
NSString *path = call.arguments[@"path"];
FIRDocumentReference *reference = [[FIRFirestore firestore] documentWithPath:path];
[reference getDocumentWithCompletion:^(FIRDocumentSnapshot *_Nullable snapshot,
NSError *_Nullable error) {
FIRDocumentReference *document = getDocumentReference(call.arguments);
[document getDocumentWithCompletion:^(FIRDocumentSnapshot *_Nullable snapshot,
NSError *_Nullable error) {
if (error) {
result(error.flutterError);
} else {
Expand Down Expand Up @@ -379,10 +383,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
result(handle);
} else if ([@"Query#addDocumentListener" isEqualToString:call.method]) {
__block NSNumber *handle = [NSNumber numberWithInt:_nextListenerHandle++];
FIRDocumentReference *reference =
[[FIRFirestore firestore] documentWithPath:call.arguments[@"path"]];
FIRDocumentReference *document = getDocumentReference(call.arguments);
id<FIRListenerRegistration> listener =
[reference addSnapshotListener:^(FIRDocumentSnapshot *snapshot, NSError *_Nullable error) {
[document addSnapshotListener:^(FIRDocumentSnapshot *snapshot, NSError *_Nullable error) {
if (error) result(error.flutterError);
[self.channel invokeMethod:@"DocumentSnapshot"
arguments:@{
Expand Down Expand Up @@ -414,35 +417,32 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
result(nil);
} else if ([@"WriteBatch#create" isEqualToString:call.method]) {
__block NSNumber *handle = [NSNumber numberWithInt:_nextBatchHandle++];
FIRWriteBatch *batch = [[FIRFirestore firestore] batch];
FIRWriteBatch *batch = [getFirestore(call.arguments) batch];
_batches[handle] = batch;
result(handle);
} else if ([@"WriteBatch#setData" isEqualToString:call.method]) {
NSNumber *handle = call.arguments[@"handle"];
NSString *path = call.arguments[@"path"];
NSDictionary *options = call.arguments[@"options"];
FIRDocumentReference *reference = [[FIRFirestore firestore] documentWithPath:path];
FIRDocumentReference *document = getDocumentReference(call.arguments);
FIRWriteBatch *batch = [_batches objectForKey:handle];
if (![options isEqual:[NSNull null]] &&
[options[@"merge"] isEqual:[NSNumber numberWithBool:YES]]) {
[batch setData:call.arguments[@"data"] forDocument:reference options:[FIRSetOptions merge]];
[batch setData:call.arguments[@"data"] forDocument:document options:[FIRSetOptions merge]];
} else {
[batch setData:call.arguments[@"data"] forDocument:reference];
[batch setData:call.arguments[@"data"] forDocument:document];
}
result(nil);
} else if ([@"WriteBatch#updateData" isEqualToString:call.method]) {
NSNumber *handle = call.arguments[@"handle"];
NSString *path = call.arguments[@"path"];
FIRDocumentReference *reference = [[FIRFirestore firestore] documentWithPath:path];
FIRDocumentReference *document = getDocumentReference(call.arguments);
FIRWriteBatch *batch = [_batches objectForKey:handle];
[batch updateData:call.arguments[@"data"] forDocument:reference];
[batch updateData:call.arguments[@"data"] forDocument:document];
result(nil);
} else if ([@"WriteBatch#delete" isEqualToString:call.method]) {
NSNumber *handle = call.arguments[@"handle"];
NSString *path = call.arguments[@"path"];
FIRDocumentReference *reference = [[FIRFirestore firestore] documentWithPath:path];
FIRDocumentReference *document = getDocumentReference(call.arguments);
FIRWriteBatch *batch = [_batches objectForKey:handle];
[batch deleteDocument:reference];
[batch deleteDocument:document];
result(nil);
} else if ([@"WriteBatch#commit" isEqualToString:call.method]) {
NSNumber *handle = call.arguments[@"handle"];
Expand Down
1 change: 1 addition & 0 deletions packages/cloud_firestore/lib/cloud_firestore.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:ui' show hashValues, hashList;

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
import 'package:meta/meta.dart';
Expand Down
6 changes: 4 additions & 2 deletions packages/cloud_firestore/lib/src/collection_reference.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ class CollectionReference extends Query {
return null;
}
return new CollectionReference._(
_firestore, (new List<String>.from(_pathComponents)..removeLast()));
firestore,
(new List<String>.from(_pathComponents)..removeLast()),
);
}

/// A string containing the slash-separated path to this CollectionReference
Expand All @@ -44,7 +46,7 @@ class CollectionReference extends Query {
childPath = new List<String>.from(_pathComponents)
..addAll(path.split(('/')));
}
return new DocumentReference._(_firestore, childPath);
return new DocumentReference._(firestore, childPath);
}

/// Returns a `DocumentReference` with an auto-generated ID, after
Expand Down
Loading

0 comments on commit 3f589f9

Please sign in to comment.