title: Angular dart: Route links in Safari on iOS 7 stop working
body: <p>I have been tearing my hair out over this for over a week now...</p>
  
  <p><strong>TL;DR: hash based links stop working in Safari on iOS 7 after a certain number of clicks.</strong></p>
  
  <p>We had a strange issue in our Angular Dart application - people using iPhones reported that links were "freezing" after "a while".  The typical non helpful error reports, so we had to work hard to figure out exactly how to replicate the issue.</p>
  
  <p>It eventually became clear that after the same number of clicks each time, Safari on iOS 7 would just stop navigating to the hash based route links.</p>
  
  <p>After much diagnosis (removing bits of the app piece by piece until the issue stopped happening), I've managed to narrow it down to routes which contain parameters.  After these routes are followed a certain number of times, Safari on iOS 7 just stops following them - it's like they get "half" clicked - the link gets underlined, but it is never followed, and subsequently no amount of clicking on it will allow it to be followed.</p>
  
  <p>Here is a very simple dart application that will replicate the issue:</p>
  
  <p>In the root folder:</p>
  
  <p>pubspec.yaml:</p>
  
  <pre><code>name: DartRouteChecker
  description: A sample web application
  dependencies:
    angular: 0.14.0
    browser: any
  transformers:
  - angular
  </code></pre>
  
  <p>Then in the web folder:</p>
  
  <p>dartroutechecker.dart:</p>
  
  <pre><code>library dart_route_checker;
  
  import 'package:angular/angular.dart';
  import 'package:angular/application_factory.dart';
  
  class MyAppModule extends Module {
    MyAppModule() {
      bind(NgRoutingUsePushState, toValue: new NgRoutingUsePushState.value(false));
      bind(RouteInitializerFn, toImplementation: Routes);
    }
  }
  
  void main() {
    applicationFactory()
      .addModule(new MyAppModule())
      .run();
  }
  
  @Injectable()
  class Routes 
  {
    void call(Router router, RouteViewFactory views) {
      views.configure({
        'route1': ngRoute(path: '/route1', defaultRoute: true, view: 'route1.html'),
        'route2': ngRoute(path: '/route2/:id', view: 'route2.html')
      });
    }
  }
  </code></pre>
  
  <p>dartroutechecker.html:</p>
  
  <pre><code>&lt;!DOCTYPE html&gt;
  
  &lt;html&gt;
    &lt;head&gt;
      &lt;meta charset="utf-8"&gt;
      &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
      &lt;title&gt;DartRouteChecker&lt;/title&gt;
  
      &lt;script async type="application/dart" src="dartroutechecker.dart"&gt;&lt;/script&gt;
      &lt;script async src="packages/browser/dart.js"&gt;&lt;/script&gt;
  
      &lt;link rel="stylesheet" href="dartroutechecker.css"&gt;
    &lt;/head&gt;
    &lt;body&gt;
      &lt;ng-view&gt;&lt;/ng-view&gt;
    &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
  <p>dartroutechecker.css</p>
  
  <pre><code>body {
    background-color: #F8F8F8;
    font-family: 'Open Sans', sans-serif;
    font-size: 14px;
    font-weight: normal;
    line-height: 1.2em;
    margin: 15px;
  }
  
  h1, p {
    color: #333;
  }
  
  #sample_container_id {
    width: 100%;
    height: 400px;
    position: relative;
    border: 1px solid #ccc;
    background-color: #fff;
  }
  
  #sample_text_id {
    font-size: 24pt;
    text-align: center;
    margin-top: 140px;
    -webkit-user-select: none;
    user-select: none;
  }
  </code></pre>
  
  <p>route1.html:</p>
  
  <pre><code>&lt;h1&gt;ROUTE 1&lt;/h1&gt;
  &lt;div&gt;
  &lt;a href="#/route2/1"&gt;Go to second route&lt;/a&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p>route2.html:</p>
  
  <pre><code>&lt;h1&gt;ROUTE 2&lt;/h1&gt;
  &lt;div&gt;
  &lt;a href="#/route1"&gt;Go to first route&lt;/a&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p>Use pub build to build the app (generate javascript), then navigate to it using an iPhone with iOS 7 (I tested it on an iPhone 5 and an iPhone 4S both with iOS 7.1.2).  Click on "Go to second route" then "Go to first route" over and over again until you get to around 30 clicks - the link will then stop responding...  It's almost always exactly 30 clicks that will cause it (though I have once or twice seen it be around 43, though I can't be sure I didn't click something else during those times - I've done so much stinkin' clickin' in the last week that I feel I'm going slightly insane...)</p>
  
  <p>I can't replicate this issue when the iPhone is plugged in to a mac to debug it.  It also doesn't happen if neither of the routes contains a parameter.  And it happens with FEWER clicks if there are more routes defined in the route initialisation class, or if the routes contain multiple parameters.  Initially I thought that the problem might have been that one of our routes had an ampersand in it, but that didn't turn out to be the case.  And it doesn't happen at all in any other browsers, including Chrome on iOS.</p>
  
  <p>I suspect this might be a bug in Safari (possibly related to Safari not handling hashchanges correctly, and not handling browser history correctly when using appcache (which, BTW, I am not using here)), but this is a real showstopper for us here - many of our customers are using iPhones, so if our app keeps freezing on the standard browser on an iPhone, we're in a lot of strife.  </p>
  
  <p>Has anyone seen anything like this before?  Anyone have any suggestions for a workaround?  Or a new career?.....</p>
  
  <p><strong>EDIT:</strong></p>
  
  <p>We have tracked the issue down to the route method in route.dart:</p>
  
  <p><a href="https://github.com/angular/route.dart/blob/master/lib/client.dart#L480" rel="nofollow">https://github.com/angular/route.dart/blob/master/lib/client.dart#L480</a></p>
  
  <p>In the method gotoUrl (<a href="https://github.com/angular/route.dart/blob/master/lib/client.dart#L782" rel="nofollow">https://github.com/angular/route.dart/blob/master/lib/client.dart#L782</a>), if we change it from <code>route(url).then((success) {</code> to <code>new Future.value(true).then((success) {</code> then the issue no longer occurs - of course then we lose all the "preenter" etc etc functionality that routing provides.  But it seems that somewhere in that route function, something is going awry...</p>