Skip to content

Commit

Permalink
Allow user to handle PlatformExceptions caught by FirebaseAnalyticsOb…
Browse files Browse the repository at this point in the history
…server._sendScreenView(). (firebase#993)
  • Loading branch information
bparrishMines authored Jan 3, 2019
1 parent 7d0d02e commit c6759fa
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 5 deletions.
4 changes: 4 additions & 0 deletions packages/firebase_analytics/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.1.0

* Allow user to handle `PlatformException`s caught by `FirebaseAnalyticsObserver._sendScreenView()`.

## 1.0.6

* Allow user ID to be set to null.
Expand Down
30 changes: 26 additions & 4 deletions packages/firebase_analytics/lib/observer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/services.dart';
import 'package:meta/meta.dart';
import 'package:flutter/widgets.dart';

Expand All @@ -19,8 +20,8 @@ String defaultNameExtractor(RouteSettings settings) => settings.name;
/// A [NavigatorObserver] that sends events to Firebase Analytics when the
/// currently active [PageRoute] changes.
///
/// When a route is pushed or poped, [nameExtractor] is used to extract a name
/// from [RouteSettings] of the now active route and that name is send to
/// When a route is pushed or popped, [nameExtractor] is used to extract a name
/// from [RouteSettings] of the now active route and that name is sent to
/// Firebase.
///
/// The following operations will result in sending a screen view event:
Expand Down Expand Up @@ -49,18 +50,39 @@ String defaultNameExtractor(RouteSettings settings) => settings.name;
/// [PageRouteAware] and subscribing it to [FirebaseAnalyticsObserver]. See the
/// [PageRouteObserver] docs for an example.
class FirebaseAnalyticsObserver extends RouteObserver<PageRoute<dynamic>> {
/// Creates a [NavigatorObserver] that sends events to [FirebaseAnalytics].
///
/// When a route is pushed or popped, [nameExtractor] is used to extract a
/// name from [RouteSettings] of the now active route and that name is sent to
/// Firebase. Defaults to `defaultNameExtractor`.
///
/// If a [PlatformException] is thrown while the observer attempts to send the
/// active route to [analytics], `onError` will be called with the
/// exception. If `onError` is omitted, the exception will be printed using
/// `debugPrint()`.
FirebaseAnalyticsObserver({
@required this.analytics,
this.nameExtractor = defaultNameExtractor,
});
Function(PlatformException error) onError,
}) : _onError = onError;

final FirebaseAnalytics analytics;
final ScreenNameExtractor nameExtractor;
final void Function(PlatformException error) _onError;

void _sendScreenView(PageRoute<dynamic> route) {
final String screenName = nameExtractor(route.settings);
if (screenName != null) {
analytics.setCurrentScreen(screenName: screenName);
analytics.setCurrentScreen(screenName: screenName).catchError(
(Object error) {
if (_onError == null) {
debugPrint('$FirebaseAnalyticsObserver: $error');
} else {
_onError(error);
}
},
test: (Object error) => error is PlatformException,
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/firebase_analytics/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for Google Analytics for Firebase, an app measuremen
solution that provides insight on app usage and user engagement on Android and iOS.
author: Flutter Team <[email protected]>
homepage: https://github.com/flutter/plugins/tree/master/packages/firebase_analytics
version: 1.0.6
version: 1.1.0

flutter:
plugin:
Expand Down
76 changes: 76 additions & 0 deletions packages/firebase_analytics/test/observer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:flutter/services.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';

Expand All @@ -14,10 +17,23 @@ void main() {
group('FirebaseAnalyticsObserver', () {
FirebaseAnalytics analytics;
FirebaseAnalyticsObserver observer;
final List<String> printLog = <String>[];

void overridePrint(void Function() func) {
final ZoneSpecification spec =
ZoneSpecification(print: (_, __, ___, String msg) {
// Add to log instead of printing to stdout
printLog.add(msg);
});
return Zone.current.fork(specification: spec).run(func);
}

setUp(() {
printLog.clear();
analytics = MockFirebaseAnalytics();
observer = FirebaseAnalyticsObserver(analytics: analytics);
when(analytics.setCurrentScreen(screenName: anyNamed('screenName')))
.thenAnswer((Invocation invocation) => Future<void>.value());
});

test('setCurrentScreen on route pop', () {
Expand Down Expand Up @@ -53,6 +69,66 @@ void main() {

verify(analytics.setCurrentScreen(screenName: 'foo')).called(1);
});

test('handles only ${PlatformException}s', () async {
observer = FirebaseAnalyticsObserver(
analytics: analytics,
nameExtractor: (RouteSettings settings) => 'foo',
);

final PageRoute<dynamic> route = MockPageRoute();
final PageRoute<dynamic> previousRoute = MockPageRoute();

// Throws non-PlatformExceptions
when(analytics.setCurrentScreen(screenName: anyNamed('screenName')))
.thenThrow(ArgumentError());

expect(() => observer.didPush(route, previousRoute), throwsArgumentError);

// Print PlatformExceptions
Future<void> throwPlatformException() async =>
throw PlatformException(code: 'a');

when(analytics.setCurrentScreen(screenName: anyNamed('screenName')))
.thenAnswer((Invocation invocation) => throwPlatformException());

overridePrint(() => observer.didPush(route, previousRoute));

await pumpEventQueue();
expect(
printLog,
<String>['$FirebaseAnalyticsObserver: ${PlatformException(code: 'a')}'],
);
});

test('runs onError', () async {
PlatformException passedException;

final void Function(PlatformException error) handleError =
(PlatformException error) {
passedException = error;
};

observer = FirebaseAnalyticsObserver(
analytics: analytics,
nameExtractor: (RouteSettings settings) => 'foo',
onError: handleError,
);

final PageRoute<dynamic> route = MockPageRoute();
final PageRoute<dynamic> previousRoute = MockPageRoute();

final PlatformException thrownException = PlatformException(code: 'b');
Future<void> throwPlatformException() async => throw thrownException;

when(analytics.setCurrentScreen(screenName: anyNamed('screenName')))
.thenAnswer((Invocation invocation) => throwPlatformException());

observer.didPush(route, previousRoute);

await pumpEventQueue();
expect(passedException, thrownException);
});
});
}

Expand Down

0 comments on commit c6759fa

Please sign in to comment.