In iOS 8, Apple introduced WKWebView as a replacement for the aging UIWebView. NSHipster has a great write-up of all of the changes, summarized as follows:

Boasting responsive 60fps scrolling, built-in gestures, streamlined communication between app and webpage, and the same JavaScript engine as Safari, WKWebView is one of the most significant announcements to come out of WWDC 2014.

Until recently, Cordova apps have been stuck using UIWebView. With the release of the Cordova iOS platform version 4.0.0, we can now take advantage of WKWebView using the cordova-plugin-wkwebview-engine plugin.

There's one large caveat: WKWebView does not allow XHR requests to file:// URIs. This is problematic for Ionic (particularly Angular) templates. Angular's default behavior is to load templates at runtime using XHR requests, which will fail in this case.

One workaround is to include another plugin called cordova-labs-local-webserver, which does exactly what it sounds like--it runs a local web server in your application's sandbox that serves all of the web content on localhost instead of from file:// URIs.

In my opinion, though, running a local web server on the phone itself seemed like overkill just to load Angular templates.

My first alternate workaround was to write a shell script that concatenated all the templates together and injected them into index.html at build time.

An even better solution is to use a gulp task to convert all the templates into a single JavaScript source file that can be referenced from index.html.

We use the gulp-angular-templatecache node module to achieve this. Add the following to your gulpfile, changing paths as needed:

var templateCache = require("gulp-angular-templatecache");

/**
 * Used to concatenate all HTML templates into a single JavaScript module.
 */
gulp.task("templates", function() {  
    return gulp.src(paths.templates)
        .pipe(templateCache({
            "filename": "templates.js",
            "root": "templates/",
            "module": "templates",
            standalone: true
        }))
        .pipe(gulp.dest("./www/js"));
});

After running the task, you'll end up with a generated file that looks like this:

angular.module("templates", [])  
  .run(["$templateCache", function($templateCache) {
      $templateCache.put("templates/Template1.html","HTML HERE");
      $templateCache.put("templates/Template2.html","HTML HERE");
// etc...
});

If you include a reference to this new file from index.html, you should then be able to reference the new module wherever you declare your root Angular module:

angular.module("YourApplication", ["ui.router", "ionic", "ngMockE2E", "templates"]);  

And that's it! Without any other changes, your templates will be loaded directly from the template cache, and Angular will not make XHR requests at runtime.