Skip to content

Commit 989ebbf

Browse files
Support for firebase_core interoperating with native code that configures Firebase apps. (flutter#478)
1 parent 659aaa1 commit 989ebbf

File tree

11 files changed

+211
-86
lines changed

11 files changed

+211
-86
lines changed

packages/firebase_core/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.2.0
2+
3+
* **Breaking change**. Options API is now async to interoperate with native code that configures Firebase apps.
4+
* Provide a getter for the default app
5+
* Fix setting of GCM sender ID on iOS
6+
17
## 0.1.2
28

39
* Fix projectID on iOS

packages/firebase_core/android/src/main/java/io/flutter/plugins/firebase/core/FirebaseCorePlugin.java

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,21 @@ private FirebaseCorePlugin(Context context) {
3232
this.context = context;
3333
}
3434

35+
private Map asMap(FirebaseApp app) {
36+
Map<String, Object> appMap = new HashMap<>();
37+
appMap.put("name", app.getName());
38+
FirebaseOptions options = app.getOptions();
39+
Map<String, String> optionsMap = new HashMap<>();
40+
optionsMap.put("googleAppID", options.getApplicationId());
41+
optionsMap.put("GCMSenderID", options.getGcmSenderId());
42+
optionsMap.put("APIKey", options.getApiKey());
43+
optionsMap.put("databaseURL", options.getDatabaseUrl());
44+
optionsMap.put("storageBucket", options.getStorageBucket());
45+
optionsMap.put("projectID", options.getProjectId());
46+
appMap.put("options", optionsMap);
47+
return appMap;
48+
}
49+
3550
@Override
3651
public void onMethodCall(MethodCall call, final Result result) {
3752
switch (call.method) {
@@ -50,34 +65,31 @@ public void onMethodCall(MethodCall call, final Result result) {
5065
.setProjectId(optionsMap.get("projectId"))
5166
.setStorageBucket(optionsMap.get("storageBucket"))
5267
.build();
53-
if (name != null) {
54-
FirebaseApp.initializeApp(context, options, name);
55-
} else {
56-
FirebaseApp.initializeApp(context, options);
57-
}
68+
FirebaseApp.initializeApp(context, options, name);
5869
result.success(null);
5970
break;
6071
}
6172
case "FirebaseApp#allApps":
6273
{
6374
List<Map<String, Object>> apps = new ArrayList<>();
64-
Map<String, Object> appMap = new HashMap<>();
6575
for (FirebaseApp app : FirebaseApp.getApps(context)) {
66-
appMap.put("name", app.getName());
67-
FirebaseOptions options = app.getOptions();
68-
Map<String, String> optionsMap = new HashMap<>();
69-
optionsMap.put("googleAppID", options.getApplicationId());
70-
optionsMap.put("GCMSenderID", options.getGcmSenderId());
71-
optionsMap.put("APIKey", options.getApiKey());
72-
optionsMap.put("databaseURL", options.getDatabaseUrl());
73-
optionsMap.put("storageBucket", options.getStorageBucket());
74-
optionsMap.put("projectID", options.getProjectId());
75-
appMap.put("options", optionsMap);
76-
apps.add(appMap);
76+
apps.add(asMap(app));
7777
}
7878
result.success(apps);
7979
break;
8080
}
81+
case "FirebaseApp#appNamed":
82+
{
83+
String name = (String) call.arguments();
84+
try {
85+
FirebaseApp app = FirebaseApp.getInstance(name);
86+
result.success(asMap(app));
87+
} catch (IllegalStateException ex) {
88+
// App doesn't exist, so successfully return null.
89+
result.success(null);
90+
}
91+
break;
92+
}
8193
default:
8294
{
8395
result.notImplemented();

packages/firebase_core/example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
/* Begin PBXBuildFile section */
1010
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
1111
2AE3C69805AB6FB8C037C7BA /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 39D23D0C629B8A857DE66538 /* libPods-Runner.a */; };
12-
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
1312
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; };
13+
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
1414
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
1515
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1616
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
@@ -42,9 +42,9 @@
4242
/* Begin PBXFileReference section */
4343
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
4444
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
45+
2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
4546
39D23D0C629B8A857DE66538 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
4647
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
47-
2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
4848
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
4949
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
5050
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
@@ -276,7 +276,7 @@
276276
);
277277
inputPaths = (
278278
"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
279-
"${PODS_ROOT}/../../../../../../flutter/bin/cache/artifacts/engine/ios/Flutter.framework",
279+
"${PODS_ROOT}/.symlinks/flutter/ios/Flutter.framework",
280280
);
281281
name = "[CP] Embed Pods Frameworks";
282282
outputPaths = (

packages/firebase_core/example/lib/main.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ class MyApp extends StatelessWidget {
2626
print('Currently configured apps: $apps');
2727
}
2828

29+
Future<Null> _options() async {
30+
final FirebaseApp app = await FirebaseApp.appNamed(name);
31+
final FirebaseOptions options = await app?.options;
32+
print('Current options for app $name: $options');
33+
}
34+
2935
@override
3036
Widget build(BuildContext context) {
3137
return new MaterialApp(
@@ -43,6 +49,8 @@ class MyApp extends StatelessWidget {
4349
onPressed: _configure, child: const Text('initialize')),
4450
new RaisedButton(
4551
onPressed: _allApps, child: const Text('allApps')),
52+
new RaisedButton(
53+
onPressed: _options, child: const Text('options')),
4654
],
4755
),
4856
),

packages/firebase_core/ios/Classes/FirebaseCorePlugin.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Copyright 2017 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
15
#import <Flutter/Flutter.h>
26

37
@interface FLTFirebaseCorePlugin : NSObject<FlutterPlugin>

packages/firebase_core/ios/Classes/FirebaseCorePlugin.m

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Copyright 2017 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
15
#import "FirebaseCorePlugin.h"
26

37
#import <Firebase/Firebase.h>
@@ -7,7 +11,7 @@ @interface FIROptions (FLTFirebaseCorePlugin)
711
@end
812

913
@implementation FIROptions (FLTFirebaseCorePlugin)
10-
- (NSDictionary *)dictionary {
14+
- (NSDictionary *)flutterDictionary {
1115
return @{
1216
@"googleAppID" : self.googleAppID ?: [NSNull null],
1317
@"bundleID" : self.bundleID ?: [NSNull null],
@@ -24,6 +28,15 @@ - (NSDictionary *)dictionary {
2428
}
2529
@end
2630

31+
@implementation FIRApp (FLTFirebaseCorePlugin)
32+
- (NSDictionary *)flutterDictionary {
33+
return @{
34+
@"name" : self.name,
35+
@"options" : self.options.flutterDictionary,
36+
};
37+
}
38+
@end
39+
2740
@implementation FLTFirebaseCorePlugin
2841
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
2942
FlutterMethodChannel *channel =
@@ -58,20 +71,20 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
5871
options.storageBucket = optionsDictionary[@"storageBucket"];
5972
if (![optionsDictionary[@"deepLinkURLScheme"] isEqual:[NSNull null]])
6073
options.deepLinkURLScheme = optionsDictionary[@"deepLinkURLScheme"];
61-
if (![name isEqual:[NSNull null]]) {
62-
[FIRApp configureWithName:name options:options];
63-
} else {
64-
[FIRApp configureWithOptions:options];
65-
}
74+
[FIRApp configureWithName:name options:options];
6675
result(nil);
6776
} else if ([@"FirebaseApp#allApps" isEqualToString:call.method]) {
6877
NSDictionary<NSString *, FIRApp *> *allApps = [FIRApp allApps];
6978
NSMutableArray *appsList = [NSMutableArray array];
7079
for (NSString *name in allApps) {
7180
FIRApp *app = allApps[name];
72-
[appsList addObject:@{@"name" : app.name, @"options" : app.options.dictionary}];
81+
[appsList addObject:app.flutterDictionary];
7382
}
7483
result(appsList.count > 0 ? appsList : nil);
84+
} else if ([@"FirebaseApp#appNamed" isEqualToString:call.method]) {
85+
NSString *name = call.arguments;
86+
FIRApp *app = [FIRApp appNamed:name];
87+
result(app.flutterDictionary);
7588
} else {
7689
result(FlutterMethodNotImplemented);
7790
}

packages/firebase_core/lib/firebase_core.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
library firebase_core;
66

77
import 'dart:async';
8+
import 'dart:io' show Platform;
89
import 'dart:ui' show hashValues;
910

1011
import 'package:flutter/services.dart';

packages/firebase_core/lib/src/firebase_app.dart

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,71 @@ part of firebase_core;
66

77
class FirebaseApp {
88
@visibleForTesting
9-
const FirebaseApp({this.name, @required this.options});
9+
const FirebaseApp({@required this.name}) : assert(name != null);
1010

11-
/// Gets the name of this app.
12-
///
13-
/// If null, the default name is used.
11+
/// The name of this app.
1412
final String name;
1513

16-
/// Gets a copy of the options for this app. These are non-modifiable.
17-
final FirebaseOptions options;
14+
static final String defaultAppName =
15+
Platform.isIOS ? '__FIRAPP_DEFAULT' : '[DEFAULT]';
1816

1917
@visibleForTesting
2018
static const MethodChannel channel = const MethodChannel(
2119
'plugins.flutter.io/firebase_core',
2220
);
2321

24-
static Map<String, FirebaseApp> _namedApps = <String, FirebaseApp>{};
22+
/// A copy of the options for this app. These are non-modifiable.
23+
///
24+
/// This getter is asynchronous because apps can also be configured by native
25+
/// code.
26+
Future<FirebaseOptions> get options async {
27+
final Map<dynamic, dynamic> app = await channel.invokeMethod(
28+
'FirebaseApp#appNamed',
29+
name,
30+
);
31+
assert(app != null);
32+
return new FirebaseOptions.from(app['options']);
33+
}
2534

2635
/// Returns a previously created FirebaseApp instance with the given name,
2736
/// or null if no such app exists.
28-
factory FirebaseApp.named(String name) => _namedApps[name];
37+
static Future<FirebaseApp> appNamed(String name) async {
38+
final Map<dynamic, dynamic> app = await channel.invokeMethod(
39+
'FirebaseApp#appNamed',
40+
name,
41+
);
42+
return app == null ? null : new FirebaseApp(name: app['name']);
43+
}
2944

30-
/// Configures an app with the given name.
45+
/// Returns the default (first initialized) instance of the FirebaseApp.
46+
static final FirebaseApp instance = new FirebaseApp(name: defaultAppName);
47+
48+
/// Configures an app with the given [name] and [options].
49+
///
50+
/// Configuring the default app is not currently supported. Plugins that
51+
/// can interact with the default app should configure it automatically at
52+
/// plugin registration time.
3153
///
32-
/// If an app with that name has already been configured, asserts that the
33-
/// [options] haven't changed.
34-
static Future<FirebaseApp> configure(
35-
{String name, @required FirebaseOptions options}) {
36-
final FirebaseApp existingApp = _namedApps[name];
54+
/// Changing the options of a configured app is not supported. Reconfiguring
55+
/// an existing app will assert that the options haven't changed.
56+
static Future<FirebaseApp> configure({
57+
@required String name,
58+
@required FirebaseOptions options,
59+
}) async {
60+
assert(name != null);
61+
assert(name != defaultAppName);
62+
assert(options != null);
63+
assert(options.googleAppID != null);
64+
final FirebaseApp existingApp = await FirebaseApp.appNamed(name);
3765
if (existingApp != null) {
38-
assert(existingApp.options == options);
39-
return new Future<FirebaseApp>.sync(() => existingApp);
66+
assert(await existingApp.options == options);
67+
return existingApp;
4068
}
41-
assert(options.googleAppID != null);
42-
_namedApps[name] = new FirebaseApp(name: name, options: options);
43-
return channel.invokeMethod('FirebaseApp#configure', <String, dynamic>{
44-
'name': name,
45-
'options': options.asMap,
46-
}).then((dynamic _) => _namedApps[name]);
69+
await channel.invokeMethod(
70+
'FirebaseApp#configure',
71+
<String, dynamic>{'name': name, 'options': options.asMap},
72+
);
73+
return new FirebaseApp(name: name);
4774
}
4875

4976
/// Returns a list of all extant FirebaseApp instances, or null if there are
@@ -52,24 +79,23 @@ class FirebaseApp {
5279
final List<dynamic> result = await channel.invokeMethod(
5380
'FirebaseApp#allApps',
5481
);
55-
return result?.map<FirebaseApp>((dynamic app) {
56-
return new FirebaseApp(
57-
name: app['name'],
58-
options: new FirebaseOptions.from(app['options']),
59-
);
60-
})?.toList();
82+
return result
83+
?.map<FirebaseApp>(
84+
(dynamic app) => new FirebaseApp(name: app['name']),
85+
)
86+
?.toList();
6187
}
6288

6389
@override
6490
bool operator ==(dynamic other) {
6591
if (identical(this, other)) return true;
6692
if (other is! FirebaseApp) return false;
67-
return other.name == name && other.options == options;
93+
return other.name == name;
6894
}
6995

7096
@override
71-
int get hashCode => hashValues(name, options);
97+
int get hashCode => name.hashCode;
7298

7399
@override
74-
String toString() => '$FirebaseApp($name, $options)';
100+
String toString() => '$FirebaseApp($name)';
75101
}

packages/firebase_core/lib/src/firebase_options.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class FirebaseOptions {
101101
'bundleID': bundleID,
102102
'clientID': clientID,
103103
'trackingID': trackingID,
104-
'gcmSenderID': gcmSenderID,
104+
'GCMSenderID': gcmSenderID,
105105
'projectID': projectID,
106106
'androidClientID': androidClientID,
107107
'googleAppID': googleAppID,

packages/firebase_core/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: Flutter plugin for Firebase Core, enabling connecting to multiple
33
Firebase apps.
44
author: Flutter Team <flutter-dev@googlegroups.com>
55
homepage: https://github.com/flutter/plugins/tree/master/packages/firebase_core
6-
version: 0.1.2
6+
version: 0.2.0
77

88
flutter:
99
plugin:

0 commit comments

Comments
 (0)