Refactor DeviceLogReader
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index 3f57bd2..8af09f4 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:convert';
 import 'dart:io';
 
 import 'package:path/path.dart' as path;
@@ -62,6 +63,8 @@
 
   final String name;
 
+  _IOSDeviceLogReader _logReader;
+
   bool get isLocalEmulator => false;
 
   bool get supportsStartPaused => false;
@@ -220,7 +223,15 @@
   @override
   TargetPlatform get platform => TargetPlatform.iOS;
 
-  DeviceLogReader createLogReader() => new _IOSDeviceLogReader(this);
+  DeviceLogReader get logReader {
+    if (_logReader == null)
+      _logReader = new _IOSDeviceLogReader(this);
+
+    return _logReader;
+  }
+
+  void clearLogs() {
+  }
 }
 
 class _IOSDeviceLogReader extends DeviceLogReader {
@@ -228,15 +239,65 @@
 
   final IOSDevice device;
 
+  final StreamController<String> _linesStreamController =
+      new StreamController<String>.broadcast();
+
+  Process _process;
+  StreamSubscription _stdoutSubscription;
+  StreamSubscription _stderrSubscription;
+
+  Stream<String> get lines => _linesStreamController.stream;
+
   String get name => device.name;
 
-  // TODO(devoncarew): Support [clear].
-  Future<int> logs({ bool clear: false, bool showPrefix: false }) async {
-    return await runCommandAndStreamOutput(
-      <String>[device.loggerPath],
-      prefix: showPrefix ? '[$name] ' : '',
-      filter: new RegExp(r'Runner')
-    );
+  bool get isReading => _process != null;
+
+  Future get finished =>
+      _process != null ? _process.exitCode : new Future.value(0);
+
+  Future start() async {
+    if (_process != null) {
+      throw new StateError(
+          '_IOSDeviceLogReader must be stopped before it can be started.');
+    }
+    _process = await runCommand(<String>[device.loggerPath]);
+    _stdoutSubscription =
+        _process.stdout.transform(UTF8.decoder)
+                       .transform(const LineSplitter()).listen(_onLine);
+    _stderrSubscription =
+        _process.stderr.transform(UTF8.decoder)
+                       .transform(const LineSplitter()).listen(_onLine);
+    _process.exitCode.then(_onExit);
+  }
+
+  Future stop() async {
+    if (_process == null) {
+      throw new StateError(
+          '_IOSDeviceLogReader must be started before it can be stopped.');
+    }
+    _stdoutSubscription?.cancel();
+    _stdoutSubscription = null;
+    _stderrSubscription?.cancel();
+    _stderrSubscription = null;
+    await _process.kill();
+    _process = null;
+  }
+
+  void _onExit(int exitCode) {
+    _stdoutSubscription?.cancel();
+    _stdoutSubscription = null;
+    _stderrSubscription?.cancel();
+    _stderrSubscription = null;
+    _process = null;
+  }
+
+  RegExp _runnerRegex = new RegExp(r'Runner');
+
+  void _onLine(String line) {
+    if (!_runnerRegex.hasMatch(line))
+      return;
+
+    _linesStreamController.add(line);
   }
 
   int get hashCode => name.hashCode;