Skip to content

Commit c6759fa

Browse files
Allow user to handle PlatformExceptions caught by FirebaseAnalyticsObserver._sendScreenView(). (flutter#993)
1 parent 7d0d02e commit c6759fa

File tree

4 files changed

+107
-5
lines changed

4 files changed

+107
-5
lines changed

packages/firebase_analytics/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.1.0
2+
3+
* Allow user to handle `PlatformException`s caught by `FirebaseAnalyticsObserver._sendScreenView()`.
4+
15
## 1.0.6
26

37
* Allow user ID to be set to null.

packages/firebase_analytics/lib/observer.dart

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'package:flutter/services.dart';
56
import 'package:meta/meta.dart';
67
import 'package:flutter/widgets.dart';
78

@@ -19,8 +20,8 @@ String defaultNameExtractor(RouteSettings settings) => settings.name;
1920
/// A [NavigatorObserver] that sends events to Firebase Analytics when the
2021
/// currently active [PageRoute] changes.
2122
///
22-
/// When a route is pushed or poped, [nameExtractor] is used to extract a name
23-
/// from [RouteSettings] of the now active route and that name is send to
23+
/// When a route is pushed or popped, [nameExtractor] is used to extract a name
24+
/// from [RouteSettings] of the now active route and that name is sent to
2425
/// Firebase.
2526
///
2627
/// The following operations will result in sending a screen view event:
@@ -49,18 +50,39 @@ String defaultNameExtractor(RouteSettings settings) => settings.name;
4950
/// [PageRouteAware] and subscribing it to [FirebaseAnalyticsObserver]. See the
5051
/// [PageRouteObserver] docs for an example.
5152
class FirebaseAnalyticsObserver extends RouteObserver<PageRoute<dynamic>> {
53+
/// Creates a [NavigatorObserver] that sends events to [FirebaseAnalytics].
54+
///
55+
/// When a route is pushed or popped, [nameExtractor] is used to extract a
56+
/// name from [RouteSettings] of the now active route and that name is sent to
57+
/// Firebase. Defaults to `defaultNameExtractor`.
58+
///
59+
/// If a [PlatformException] is thrown while the observer attempts to send the
60+
/// active route to [analytics], `onError` will be called with the
61+
/// exception. If `onError` is omitted, the exception will be printed using
62+
/// `debugPrint()`.
5263
FirebaseAnalyticsObserver({
5364
@required this.analytics,
5465
this.nameExtractor = defaultNameExtractor,
55-
});
66+
Function(PlatformException error) onError,
67+
}) : _onError = onError;
5668

5769
final FirebaseAnalytics analytics;
5870
final ScreenNameExtractor nameExtractor;
71+
final void Function(PlatformException error) _onError;
5972

6073
void _sendScreenView(PageRoute<dynamic> route) {
6174
final String screenName = nameExtractor(route.settings);
6275
if (screenName != null) {
63-
analytics.setCurrentScreen(screenName: screenName);
76+
analytics.setCurrentScreen(screenName: screenName).catchError(
77+
(Object error) {
78+
if (_onError == null) {
79+
debugPrint('$FirebaseAnalyticsObserver: $error');
80+
} else {
81+
_onError(error);
82+
}
83+
},
84+
test: (Object error) => error is PlatformException,
85+
);
6486
}
6587
}
6688

packages/firebase_analytics/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: Flutter plugin for Google Analytics for Firebase, an app measuremen
33
solution that provides insight on app usage and user engagement on Android and iOS.
44
author: Flutter Team <flutter-dev@googlegroups.com>
55
homepage: https://github.com/flutter/plugins/tree/master/packages/firebase_analytics
6-
version: 1.0.6
6+
version: 1.1.0
77

88
flutter:
99
plugin:

packages/firebase_analytics/test/observer_test.dart

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:async';
6+
7+
import 'package:flutter/services.dart';
58
import 'package:mockito/mockito.dart';
69
import 'package:test/test.dart';
710

@@ -14,10 +17,23 @@ void main() {
1417
group('FirebaseAnalyticsObserver', () {
1518
FirebaseAnalytics analytics;
1619
FirebaseAnalyticsObserver observer;
20+
final List<String> printLog = <String>[];
21+
22+
void overridePrint(void Function() func) {
23+
final ZoneSpecification spec =
24+
ZoneSpecification(print: (_, __, ___, String msg) {
25+
// Add to log instead of printing to stdout
26+
printLog.add(msg);
27+
});
28+
return Zone.current.fork(specification: spec).run(func);
29+
}
1730

1831
setUp(() {
32+
printLog.clear();
1933
analytics = MockFirebaseAnalytics();
2034
observer = FirebaseAnalyticsObserver(analytics: analytics);
35+
when(analytics.setCurrentScreen(screenName: anyNamed('screenName')))
36+
.thenAnswer((Invocation invocation) => Future<void>.value());
2137
});
2238

2339
test('setCurrentScreen on route pop', () {
@@ -53,6 +69,66 @@ void main() {
5369

5470
verify(analytics.setCurrentScreen(screenName: 'foo')).called(1);
5571
});
72+
73+
test('handles only ${PlatformException}s', () async {
74+
observer = FirebaseAnalyticsObserver(
75+
analytics: analytics,
76+
nameExtractor: (RouteSettings settings) => 'foo',
77+
);
78+
79+
final PageRoute<dynamic> route = MockPageRoute();
80+
final PageRoute<dynamic> previousRoute = MockPageRoute();
81+
82+
// Throws non-PlatformExceptions
83+
when(analytics.setCurrentScreen(screenName: anyNamed('screenName')))
84+
.thenThrow(ArgumentError());
85+
86+
expect(() => observer.didPush(route, previousRoute), throwsArgumentError);
87+
88+
// Print PlatformExceptions
89+
Future<void> throwPlatformException() async =>
90+
throw PlatformException(code: 'a');
91+
92+
when(analytics.setCurrentScreen(screenName: anyNamed('screenName')))
93+
.thenAnswer((Invocation invocation) => throwPlatformException());
94+
95+
overridePrint(() => observer.didPush(route, previousRoute));
96+
97+
await pumpEventQueue();
98+
expect(
99+
printLog,
100+
<String>['$FirebaseAnalyticsObserver: ${PlatformException(code: 'a')}'],
101+
);
102+
});
103+
104+
test('runs onError', () async {
105+
PlatformException passedException;
106+
107+
final void Function(PlatformException error) handleError =
108+
(PlatformException error) {
109+
passedException = error;
110+
};
111+
112+
observer = FirebaseAnalyticsObserver(
113+
analytics: analytics,
114+
nameExtractor: (RouteSettings settings) => 'foo',
115+
onError: handleError,
116+
);
117+
118+
final PageRoute<dynamic> route = MockPageRoute();
119+
final PageRoute<dynamic> previousRoute = MockPageRoute();
120+
121+
final PlatformException thrownException = PlatformException(code: 'b');
122+
Future<void> throwPlatformException() async => throw thrownException;
123+
124+
when(analytics.setCurrentScreen(screenName: anyNamed('screenName')))
125+
.thenAnswer((Invocation invocation) => throwPlatformException());
126+
127+
observer.didPush(route, previousRoute);
128+
129+
await pumpEventQueue();
130+
expect(passedException, thrownException);
131+
});
56132
});
57133
}
58134

0 commit comments

Comments
 (0)