Angular.js: UIWebView๊ฐ€์žˆ๋Š” iOS 9์—์„œ ์œ„์น˜ ๋ณ€๊ฒฝ์— ๋Œ€ํ•œ ๋ฌดํ•œ ๋‹ค์ด์ œ์ŠคํŠธ (Safari / WKWebView๊ฐ€ ์•„๋‹˜)

์— ๋งŒ๋“  2015๋…„ 06์›” 30์ผ  ยท  154์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: angular/angular.js

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ„๋‹จํ•œ HTML์ด ๋ฌธ์ œ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

<!DOCTYPE html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.16/angular.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.16/angular-route.js"></script>
        <script>
            angular.module('fail', ['ngRoute'])
            .config(function($routeProvider) {
                $routeProvider
                .when('/a', {
                    template: '<a ng-href="#/b">a</a>'
                })
                .when('/b', {
                    template: '<a ng-href="#/a">b</a>'
                })
                .otherwise({
                    redirectTo: '/a'
                });
            });
        </script>
    </head>
    <body ng-app="fail">
        <div ng-view></div>
    </body>
</html>

์ด๊ฒƒ์€ ๋Œ€๋ถ€๋ถ„์˜ ์žฅ์น˜์—์„œ ์˜ˆ์ƒ๋Œ€๋กœ ์‹คํ–‰๋˜์ง€๋งŒ iOS 9์—์„œ๋Š” ๋ฌดํ•œ ๋‹ค์ด์ œ์ŠคํŠธ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
iOS 9 ๋ฒ ํƒ€ 2๋กœ iPad Air 2์™€ iPad 4 ์„ธ๋Œ€ ๋ชจ๋‘์—์„œ ์žฌํ˜„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
iOS์˜ ๋ฌธ์ œ ์ผ ์ˆ˜ ์žˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ์กฐ์‚ฌ ํ•  ๊ฐ€์น˜๊ฐ€์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

iOS $location bug

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์ด๊ฒƒ์— ๋Œ€ํ•ด ์กฐ๊ธˆ ๋” ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์œ„์น˜์™€ ๊ด€๋ จํ•˜์—ฌ Angular๋ฅผ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.
"์—…๋ฐ์ดํŠธ ๋ธŒ๋ผ์šฐ์ €"๋ถ€๋ถ„์—์„œ $ rootScope. $ evalAsync๋ฅผ $ rootScope. $ applyAsync๋กœ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ๋˜‘๊ฐ™์€ ์ผ์„ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. ์‹ค์ œ $ digest ์‹คํ–‰์„ ๋ณผ ๋•Œ๊นŒ์ง€ ์ฐจ์ด๊ฐ€ โ€‹โ€‹๋ถ„๋ช…ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. AngularJS๊ฐ€ ๋‹ค์ด์ œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ๋” ์ด์ƒ ๋”ํ‹ฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์„ ๋•Œ๊นŒ์ง€ ๋ฒ”์œ„ ํŠธ๋ฆฌ๋ฅผ ํƒ์ƒ‰ํ•˜๊ณ  $ watch () ๋ฐ”์ธ๋”ฉ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ˆ˜๋ช…์ฃผ๊ธฐ ๋™์•ˆ $ applyAsync () ํ์™€ $ evalAsync () ํ๊ฐ€ ๋ชจ๋‘ ํ”Œ๋Ÿฌ์‹œ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ๋งค์šฐ ๋‹ค๋ฅธ ๋‘ ๊ณณ์—์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

$ applyAsync () ํ๋Š” AngularJS๊ฐ€ ๋”ํ‹ฐ ๋ฐ์ดํ„ฐ ๊ฒ€์‚ฌ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— $ digest์˜ ๋งจ ์œ„์—์„œ ๋งŒ ํ”Œ๋Ÿฌ์‹œ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ $ applyAsync () ํ๋Š” $ digest ๋™์•ˆ ์ตœ๋Œ€ ํ•œ ๋ฒˆ ํ”Œ๋Ÿฌ์‹œ๋˜๋ฉฐ $ digest๊ฐ€ ์‹œ์ž‘๋˜๊ธฐ ์ „์— ํ๊ฐ€ ์ด๋ฏธ ์ฑ„์›Œ์ง„ ๊ฒฝ์šฐ์—๋งŒ ํ”Œ๋Ÿฌ์‹œ๋ฉ๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด์— $ evalAsync () ํ๋Š” $ digest ๋‚ด๋ถ€์˜ "dirty check"๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” while ๋ฃจํ”„์˜ ๋งจ ์œ„์— ํ”Œ๋Ÿฌ์‹œ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋‹ค์ด์ œ์ŠคํŠธ ์ค‘์— $ evalAsync () ํ์— ์ถ”๊ฐ€ ๋œ ๋ชจ๋“  ํ‘œํ˜„์‹์€ ๋™์ผํ•œ ๋‹ค์ด์ œ์ŠคํŠธ ๋‚ด์—์„œ ๋‚˜์ค‘์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์ด ์ฐจ์ด๋ฅผ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด $ watch () ๋ฐ”์ธ๋”ฉ ๋‚ด์—์„œ $ evalAsync ()์— ์˜ํ•ด ์ถ”๊ฐ€ ๋œ ๋น„๋™๊ธฐ์‹์ด ๋™์ผํ•œ ๋‹ค์ด์ œ์ŠคํŠธ์—์„œ ์‹คํ–‰๋œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. $ watch () ๋ฐ”์ธ๋”ฉ ๋‚ด์—์„œ $ applyAsync ()์— ์˜ํ•ด ์ถ”๊ฐ€ ๋œ ๋น„๋™๊ธฐ์‹์€ ์ดํ›„ ์‹œ์  (~ 10ms)์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์ด๋ฏธ ๋‹น์‹  ์ค‘ ์ผ๋ถ€์—๊ฒŒ ๋„์›€์ด๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค :-).


// update browser
    $rootScope.$watch(function $locationWatch() {
      var oldUrl = trimEmptyHash($browser.url());
      var newUrl = trimEmptyHash($location.absUrl());
      var oldState = $browser.state();
      var currentReplace = $location.$$replace;
      var urlOrStateChanged = oldUrl !== newUrl ||
        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);

      if (initializing || urlOrStateChanged) {
        initializing = false;

        $rootScope.$applyAsync(function() {
          var newUrl = $location.absUrl();
          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
              $location.$$state, oldState).defaultPrevented;

          // if the location was changed by a `$locationChangeStart` handler then stop
          // processing this location change
          if ($location.absUrl() !== newUrl) return;

          if (defaultPrevented) {
            $location.$$parse(oldUrl);
            $location.$$state = oldState;
          } else {
            if (urlOrStateChanged) {
              setBrowserUrlWithFallback(newUrl, currentReplace,
                                        oldState === $location.$$state ? null : $location.$$state);
            }
            afterLocationChange(oldUrl, oldState);
          }
        });
      }

      $location.$$replace = false;

      // we don't need to return anything because $evalAsync will make the digest loop dirty when
      // there is a change
    });

๋ชจ๋“  154 ๋Œ“๊ธ€

iOS 9์—์„œ ๋ฐœ์ƒํ•œ ์œ ์‚ฌํ•œ ๋ฌธ์ œ๋ฅผ ๋งŒ๋‚ฌ์ง€๋งŒ ๋‹ค๋ฅธ ์žฅ์น˜์—์„œ๋Š” ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

1.4.1 / ios 9์—์„œ santaslow๊ฐ€ ์ œ๊ณต ํ•œ ๋™์ผํ•œ ์ฝ”๋“œ๋กœ์ด ๋ฌธ์ œ๋ฅผ ์žฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

<!DOCTYPE html>
<html>
<head>
    <script src="../static/js/angular/angular.1.4.1.js"></script>
    <script src="../static/js/angular-route/angular-route.1.4.1.js"></script>
    <script>
        angular.module('fail', ['ngRoute'])
                .config(function ($routeProvider) {
                    $routeProvider
                            .when('/a', {
                                template: '<a ng-href="#/b">a</a>'
                            })
                            .when('/b', {
                                template: '<a ng-href="#/a">b</a>'
                            })
                            .otherwise({
                                redirectTo: '/a'
                            });
                }).factory('$exceptionHandler', ['$log', function($log) {
                    return function(exception, cause) {
                        var message = 'angularjs exception: '+exception.message+': caused by "' + cause+ '\njs stack:\n'+exception.stack;
                        $log.error(message);
                    };
                }]);
    </script>
</head>
<body ng-app="fail">
<div ng-view></div>
</body>
</html>

์œ„์˜ ์ฝ”๋“œ๋Š” ๋ฐ์Šคํฌํ†ฑ ๋ธŒ๋ผ์šฐ์ €, Android ๋ฐ ios 8 webview์—์„œ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜์ง€๋งŒ ios 9์—์„œ๋Š” ๋งํฌ๋ฅผ ํด๋ฆญํ•˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒ

2015-07-02 11:00:09 ... angularjs exception: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
http://errors.angularjs.org/1.4.1/$rootScope/infdig?p0=10&p1=%5B%5D: caused by "undefined
js stack:
file:///.../static/js/angular/angular.js:68:32
$digest<strong i="10">@file</strong>:///.../static/js/angular/angular.js:15705:35
$apply<strong i="11">@file</strong>:///.../static/js/angular/angular.js:15935:31
file:///.../static/js/angular/angular.js:12070:30
eventHandler<strong i="12">@file</strong>:///.../static/js/angular/angular.js:3264:25

๋” ์ด์ƒ iOS 9 ๋ฒ ํƒ€ 3์—์„œ ์žฌํ˜„ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

ios9 ๊ณต๊ฐœ ๋ฒ ํƒ€ (13A4293g)์—์„œ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์œ„์˜ ์ฝ”๋“œ๋ฅผ iOS 9 ๋ฒ ํƒ€ 3 (13A4293g)์—์„œ ํ™•์ธํ–ˆ๋Š”๋ฐ ๋” ์ด์ƒ ์˜ˆ์™ธ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ng-view๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์•ฑ์€ ์—ฌ์ „ํžˆ โ€‹โ€‹ios 9 ๋ฒ ํƒ€ 3์—์„œ infdig ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

ios9 ๊ณต๊ฐœ ๋ฒ ํƒ€ (13A4293g)์—์„œ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์˜ค๋ฅ˜ : [$ rootScope : infdig ] 10 $ digest () ๋ฐ˜๋ณต์— ๋„๋‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ค‘๋‹จ!
์ตœ๊ทผ 5 ํšŒ ๋ฐ˜๋ณต์—์„œ ์‹คํ–‰ ๋œ ๊ฐ์‹œ์ž : []
http://errors.angularjs.org/1.3.13/ $ rootScope / infdig? p0 = 10 & p1 = % 5B % 5D
file : ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C99-807B-EA17B2479E58/BAShops.app/www /lib/ionic/js/ionic.bundle.js:8762:32
$ digest @ file : /// Users / mac5 / Library / Developer / CoreSimulator / Devices / 749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE / data / Containers / Bundle / Application / 9B5EE368-F2A0-4C99-807B-EA17B2479E58 / BAShops. app / www / lib / ionic / js / ionic.bundle.js : 22980 : 35
$ apply @ file : /// Users / mac5 / Library / Developer / CoreSimulator / Devices / 749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE / data / Containers / Bundle / Application / 9B5EE368-F2A0-4C99-807B-EA17B2479E58 / BAShops. app / www / lib / ionic / js / ionic.bundle.js : 23205 : 31
file : ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C99-807B-EA17B2479E58/BAShops.app/www /lib/ionic/js/ionic.bundle.js:54879:24
eventHandler @ file : ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C99-807B-EA17B2479E58/BAShops.app /www/lib/ionic/js/ionic.bundle.js:11713:25
dispatchEvent @ [๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ]
triggerMouseEvent @ file : ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C99-807B-EA17B2479E58/BAShops.app /www/lib/ionic/js/ionic.bundle.js:2863:20
tapClick @ file : ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C99-807B-EA17B2479E58/BAShops.app /www/lib/ionic/js/ionic.bundle.js:2852:20
tapTouchEnd @ file : ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C99-807B-EA17B2479E58/BAShops.app /www/lib/ionic/js/ionic.bundle.js:2975:13

์ž์ฒด Angular ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด์žˆ๋Š” iOS 9์˜ ๊ณต๊ฐœ ๋ฒ ํƒ€ 3์—์„œ๋„์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. iOS 8์—์„œ๋Š” ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์œผ๋กœ ng-view ๋ฐ angular-route.js๋ฅผ ๋Œ€์ฒดํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์ง€์‹œ๋ฌธ์„ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์†”๋ฃจ์…˜์€ ์šฐ๋ฆฌ ์ž์‹ ์˜ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ ์ž˜ ์ž‘๋™ํ–ˆ์œผ๋ฉฐ ๋ชจ๋“  infdig ์˜ˆ์™ธ๋Š” ios 9 ๋ฒ ํƒ€ / ๋ฒ ํƒ€ 3์—์„œ ์‚ฌ๋ผ์กŒ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ์šฐ๋ฆฌ ์ž์‹ ์˜ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ๋งŒ์„์œ„ํ•œ ๋‹จ์ˆœํ™” ๋œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋„๋ก ๊ถŒ์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

(function (window) {
    'use strict';

    var myApp = angular.module("myApp");
    var $route = {};

    // replace $routeProvider.when with the function below:
    window.routeWhen = function(path, route) {
        $route[path] = route;
    };

    myApp.directive("myView", ['$compile', '$controller', '$http', '$rootScope', function ($compile, $controller, $http, $rootScope) {

        return {
            priority: -400,
            link: function (scope, element) {
                var parentScope = scope;
                scope = null;

                window.updateView = function (path) {
                    location.hash = '#'+url;
                    if (scope) scope.$destroy();
                    scope = parentScope.$new();

                    var route = $route[path];

                    var linkView = function(html) {
                        element.html(html);
                        var link = $compile(element.contents());
                        var controller = $controller(route.controller, {$scope: scope});
                        element.data('$ngControllerController', controller);
                        element.children().data('$ngControllerController', controller);
                        link(scope);
                        scope.$emit('$viewContentLoaded');
                        if (!$rootScope.$$phase && !scope.$$phase) scope.$apply();
                    };
                    if (route.templateCache) linkView(route.templateCache)
                    else if (route.template) {
                        route.templateCache = document.getElementById(route.template).innerHTML;
                        linkView(route.templateCache)
                    }
                    else $http.get(route.templateUrl;).success(function(html) {
                        route.templateCache = html;
                        linkView(html);
                    });
                };
                //updateView(initialPath);
                // call updateView(path) to set location at other places of the app
            }
        };
    }]);

})(window)

๋ฐฉ๊ธˆ iOS 9 ๋ฒ ํƒ€ 4๋ฅผ ์„ค์น˜ํ–ˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์‚ฌ๋žŒ?

iOS 9 ๋ฒ ํƒ€ 3์—์„œ ๋ดค์ง€๋งŒ ์—ฌ์ „ํžˆ iOS 9 ๋ฒ ํƒ€ 4์—์„œ๋ณด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

+1

์˜ˆ, ๊ฐ๋„ UI ๋ผ์šฐํ„ฐ์—์„œ๋„ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋™์•ˆ ๋ˆ„๊ตฌ๋“ ์ง€์ด ๋ฌธ์ œ์— ๋Œ€ํ•ด ์œ ํšจํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ?

์ตœ์‹  iOS9์˜ uiWebView์—์„œ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.

๋ˆ„๊ตฌ๋“ ์ง€์ด ๋ฌธ์ œ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

์ด๊ฒƒ์€ ์—ฌ์ „ํžˆ โ€‹โ€‹๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ์žฌ๊ฐœ

iOS ๋ฌธ์ œ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์›นํ‚ท์—์„œ ์–ด๋”˜๊ฐ€์— ์ถ”์ ๋ฉ๋‹ˆ๊นŒ?

+1

+1

์—ฌ๊ธฐ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. Cordova ์•ฑ์€ iPad Safari์—์„œ ์›น์œผ๋กœ ์‹คํ–‰๋˜๋Š” ๊ฒฝ์šฐ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜์ง€๋งŒ Cordova ์•ฑ (UIWebView)์œผ๋กœ ์‹คํ–‰๋˜๋Š” ๊ฒฝ์šฐ ๋ฌดํ•œ ๋‹ค์ด์ œ์ŠคํŠธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

@borrull ๊ณผ ๋˜‘๊ฐ™์€ ๋ฌธ์ œ! ์ด๋ฏธ WKWebView๋กœ ์‹คํ—˜ํ–ˆ์œผ๋ฉฐ ๋ฌธ์ œ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๋Š” ๋กœ์ปฌ ํŒŒ์ผ ์„œ๋น™ (๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋กœ์ปฌ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์Œ)๊ณผ ์ฟ ํ‚ค๊ฐ€ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— WKWebView๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ iOS 9์˜ Cordova / Mobile Safari์™€ ํ•จ๊ป˜ UIWebView๋กœ ๋ญ”๊ฐ€๋ฅผํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ €๋Š” ํ˜„์žฌ Angular์—์„œ $ locationWatch๋ฅผ ๋””๋ฒ„๊น…ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ์—ฌ๋Ÿฌ ๋ฒˆ ๋‹ค๋ฅธ ์œ„์น˜๋กœ ์ „ํ™˜ ํ•œ ๋‹ค์Œ (10 ๋ฒˆ ํ›„) ) ๋‹ค์ด์ œ์ŠคํŠธ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

iOS9 bรฉta4์—์„œ ๋™์ผํ•œ ๋ฌธ์ œ
๊ฐ๋„ ๋ฌดํ•œ $ digest ๋ฃจํ”„; (

+1, WKWebView๊ฐ€ ์•„๋‹Œ โ€‹โ€‹UIWebView์—์„œ๋งŒ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ Cordova ์•ฑ์—์„œ๋งŒ UIWebView๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

+1

์ด๊ฒƒ์ด iOS ํŠน์ • ๋ฌธ์ œ์ธ ๊ฒฝ์šฐ ์›นํ‚ท ๋ฌธ์ œ ์ถ”์ ๊ธฐ์—์„œ ๋ฌธ์ œ๋ฅผ ์—ด๊ณ  ๋ฐ๋ชจ๋ฅผ ์ œ๊ณตํ•˜์‹ญ์‹œ์˜ค! +1์€ ์‹ค์ œ๋กœ ๋ธŒ๋ผ์šฐ์ € ๋ฒ„๊ทธ์ฒ˜๋Ÿผ ๋“ค๋ฆฌ๋ฏ€๋กœ ์•„๋ฌด๊ฒƒ๋„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ˆ„๊ตฐ๊ฐ€ ์กฐ์‚ฌ๋ฅผ ์œ„ํ•ด์ด ๋ฒ„๊ทธ๋ฅผ Apple์—๋ณด๊ณ  ํ–ˆ์Šต๋‹ˆ๊นŒ?

์ด ๋ฒ„๊ทธ๋ฅผ ์‚ฌ๊ณผ์—๋ณด๊ณ ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฒ„๊ทธ์— ๋Œ€ํ•œ์ฃผ์˜๋ฅผ ๋Œ๊ธฐ ์œ„ํ•ด ๋ชจ๋‘ ๋˜‘๊ฐ™์ดํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

+1 ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งํฌ๋ฅผ ์ œ๊ณตํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

๊ทธ๊ฒƒ์€ ๋ฒ„๊ทธ ๋ฆฌํฌํ„ฐ๋ฅผ ํ†ตํ•ด ์šฐ๋ฆฌ์˜ ๊ฐœ์ธ ์• ํ”Œ ๊ณ„์ •์— ์žˆ์Šต๋‹ˆ๋‹ค .. ๊ทธ๋ž˜์„œ ๊ณต๊ฐœ ๋งํฌ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค; (

openradar์— ๊ฒŒ์‹œํ•˜๊ณ  ์šฐ๋ฆฌ๊ฐ€ ์†์ผ ์ˆ˜ ์žˆ๋„๋ก rdar ID๋ฅผ ๊ณต์œ ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ!

iOS9 bรฉta 5์—์„œ ๋™์ผ :
๋ชจ๋ฐ”์ผ ์‚ฌํŒŒ๋ฆฌ์—์„œ ์ž‘๋™
๋กœ์ปฌ ํŒŒ์ผ์„ ์ œ๊ณต ํ•  ์ˆ˜์—†๊ณ  NSProtocol์„ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•  ์ˆ˜์—†๋Š” WKWebview์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
UIWebView์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

IOS 9 ๋ฒ ํƒ€ 5์—์„œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.

Apple์—๋„ ๋ฒ„๊ทธ๋ฅผ ์‹ ๊ณ ํ–ˆ์Šต๋‹ˆ๋‹ค. Open Radar ๋งํฌ : https://openradar.appspot.com/22186109 (์ด๋Š” ๊ฒŒ์œผ๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ๋ฒ„๊ทธ๋ฅผ ์‹ ๊ณ ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค). ๋ฒ„๊ทธ ๋ฌธ๊ตฌ / ์„ค๋ช…์„ ๊ฐœ์„  ํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ์˜๊ฒฌ์„ ๋‚จ๊ฒจ์ฃผ์„ธ์š” ;-) Xcode ํ”„๋กœ์ ํŠธ๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜์—ฌ Open Radar ํ‹ฐ์ผ“์— ๋ฒ„๊ทธ ์‹ ๊ณ ์™€ ํ•จ๊ป˜ ์ฒจ๋ถ€ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (OP์˜ JS์— ๋Œ€ํ•ด @santaslow ์—๊ฒŒ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค)

@borrull์—์„œ ๋™์ผํ•œ Xcode ํ”„๋กœ์ ํŠธ ๋ฒ„์ „์„ ๋งŒ๋“ค์—ˆ์ง€ ๋งŒ ng-route ๋Œ€์‹  ui-router๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ •ํ™•ํžˆ ๊ฐ™์€ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ๊ด€์‹ฌ์žˆ๋Š” ์‚ฌ๋žŒ๋“ค์€ ์—ฌ๊ธฐ์—์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. http://s000.tinyupload.com/index.php?file_id=87281871603760127355

์šฐ๋ฆฌ๋Š” ๋˜ํ•œ ์ด์™€ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ๋ณด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์•ต๊ทค๋Ÿฌ๊ฐ€ ํ˜ผํ•ฉ๋˜์–ด์žˆ์„ ๋•Œ location. * ์†์„ฑ์ด ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๋ฅผ ์ถ”์  ํ•  ์ˆ˜์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. location.hash์— ๊ฐ’์„ ํ• ๋‹นํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋ฉด (์œ„์น˜ ์„œ๋น„์Šค ๋’ค์—์„œ ์ˆ˜ํ–‰๋˜๋Š” ์ž‘์—…๊ณผ ๊ฐ™์ด) ์ฆ‰์‹œ ๋‹ค์‹œ ์ฝ์–ด ๋“ค์ด๋ฉด ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. popstate ๋ฐ hashchanged ์ด๋ฒคํŠธ์— ์—ฐ๊ฒฐ๋œ jqlite ํ•ธ๋“ค๋Ÿฌ์˜ ๊ฒฐ๊ณผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ถ€์ž‘์šฉ์ด์žˆ๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

์ปดํ“จํ„ฐ์—์žˆ์„ ๋•Œ ์ƒ˜ํ”Œ์„ ์—…๋กœ๋“œ ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

+1

@CleverCoder ์ƒ˜ํ”Œ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

์ฃผ๋ง ๋‚ด๋‚ด ๋ฌถ์ธ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์•„์นจ์— ์žฌํ˜„ ์‚ฌ๋ก€๋ฅผ ๋งˆ๋ฌด๋ฆฌํ•ด์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋„›์ง€ ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! iOS 9์ด ์นด์šดํŠธ ๋‹ค์šด๋˜๋ฉด์„œ์ด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜๋Š” ๋ฐ ๊ด€์‹ฌ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ตœ๋Œ€ํ•œ ๋นจ๋ฆฌ ์—…๋กœ๋“œํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

+1

๋‚˜๋Š” ์œ„์น˜ ํ•ด์‹œ ๋˜๋Š” href ์†์„ฑ ์„ค์ •์ด ์ฆ‰์‹œ "์ ์šฉ"๋˜์ง€ ์•Š๋Š” ๊ทผ๋ณธ ์›์ธ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒƒ์„ ์žฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
๋‹ค์Œ์€ XCode ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•œ ๋งํฌ์ž…๋‹ˆ๋‹ค.
https://www.dropbox.com/s/2jkwv2thhm86nly/iOS%209%20Location%20Bug.zip?dl=0
ํŒŒ์ผ์— ์•ก์„ธ์Šค ํ•  ์ˆ˜์—†๋Š” ๊ฒฝ์šฐ ์•Œ๋ ค์ฃผ์„ธ์š”.

location.hash์˜ ๊ฒฐ๊ณผ ๊ฐ’์„ ๊ด€์ฐฐํ•˜๊ณ  Safari๋ฅผ ๋””๋ฒ„๊ทธ์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. 'popstate'๋ฐ 'hashchange'์ด๋ฒคํŠธ์— ๊ธฐ๋ฐ˜ํ•œ ์ผ๋ถ€ ์ด๋ฒคํŠธ ๋ฐฐ๊ด€์˜ ๊ฒฐ๊ณผ๋กœ ๋ณ€๊ฒฝ์„ ์ง€์—ฐ์‹œํ‚ค๋Š” ๊ฒƒ์ด์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

  • ์…˜

state.go๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  window.location.href๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ํ˜„์žฌ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ฒ„๊ทธ๊ฐ€ ์ ์Šต๋‹ˆ๋‹ค.

runloop๊ฐ€ ํšŒ์ „ ํ•œ ํ›„์— location.hash์˜ ๊ฐ’์ด ์ •ํ™•ํ•ฉ๋‹ˆ๋‹ค. Angular๋Š” setTimeout (..., 0)์—์„œ location.hash ๊ฐ€์ ธ ์˜ค๊ธฐ๋ฅผ ์ง€์—ฐํ•˜์—ฌ์ด ๋ฌธ์ œ๋ฅผ ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์ด angular.js / src / ng / location.js์˜ ~ 2 ๋ณ€๊ฒฝ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@hober ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ๋„๋กœ ์‹œ๊ฐ„ ์ œํ•œ์„ ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค.

// update $location when $browser url changes
    $browser.onUrlChange(function(newUrl, newState) {
      $rootScope.$evalAsync(function() {
        var oldUrl = $location.absUrl();
        var oldState = $location.$$state;
        var defaultPrevented;

        $location.$$parse(newUrl);
        $location.$$state = newState;

        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
            newState, oldState).defaultPrevented;

        // if the location was changed by a `$locationChangeStart` handler then stop
        // processing this location change
        if ($location.absUrl() !== newUrl) return;

        if (defaultPrevented) {
          $location.$$parse(oldUrl);
          $location.$$state = oldState;
          setTimeout(function(){ setBrowserUrlWithFallback(oldUrl, false, oldState) }, 0);
        } else {
          initializing = false;
          afterLocationChange(oldUrl, oldState);
        }
      });
      if (!$rootScope.$$phase) $rootScope.$digest();
    });

๊ณผ

// update browser
    $rootScope.$watch(function $locationWatch() {
      var oldUrl = trimEmptyHash($browser.url());
      var newUrl = trimEmptyHash($location.absUrl());
      var oldState = $browser.state();
      var currentReplace = $location.$$replace;
      var urlOrStateChanged = oldUrl !== newUrl ||
        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);

      if (initializing || urlOrStateChanged) {
        initializing = false;

        $rootScope.$evalAsync(function() {
          var newUrl = $location.absUrl();
          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
              $location.$$state, oldState).defaultPrevented;

          // if the location was changed by a `$locationChangeStart` handler then stop
          // processing this location change
          if ($location.absUrl() !== newUrl) return;

          if (defaultPrevented) {
            $location.$$parse(oldUrl);
            $location.$$state = oldState;
          } else {
            if (urlOrStateChanged) {
              setTimeout(function(){ setBrowserUrlWithFallback(newUrl, currentReplace,
                  oldState === $location.$$state ? null : $location.$$state) }, 0);
            }
            afterLocationChange(oldUrl, oldState);
          }
        });
      }

      $location.$$replace = false;

      // we don't need to return anything because $evalAsync will make the digest loop dirty when
      // there is a change
    });

๊ทธ๋ž˜์„œ setBrowserUrlWithFallback ๋ฉ”์„œ๋“œ ์ฃผ์œ„์— setTimout์„ ์ถ”๊ฐ€ํ–ˆ์ง€๋งŒ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ Angular์— ์˜์กดํ•˜์ง€ ์•Š๋Š” ์ถ•์†Œ ๋œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์ด๋ฉฐ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. Angular์—์„œ ์‹ค์ œ๋กœ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๋ช…ํ™•ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. https://gist.github.com/hober/a29b6c28ac1744c800dd

์ด๊ฒƒ์— ๋Œ€ํ•ด ์กฐ๊ธˆ ๋” ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์œ„์น˜์™€ ๊ด€๋ จํ•˜์—ฌ Angular๋ฅผ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.
"์—…๋ฐ์ดํŠธ ๋ธŒ๋ผ์šฐ์ €"๋ถ€๋ถ„์—์„œ $ rootScope. $ evalAsync๋ฅผ $ rootScope. $ applyAsync๋กœ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ๋˜‘๊ฐ™์€ ์ผ์„ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. ์‹ค์ œ $ digest ์‹คํ–‰์„ ๋ณผ ๋•Œ๊นŒ์ง€ ์ฐจ์ด๊ฐ€ โ€‹โ€‹๋ถ„๋ช…ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. AngularJS๊ฐ€ ๋‹ค์ด์ œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ๋” ์ด์ƒ ๋”ํ‹ฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์„ ๋•Œ๊นŒ์ง€ ๋ฒ”์œ„ ํŠธ๋ฆฌ๋ฅผ ํƒ์ƒ‰ํ•˜๊ณ  $ watch () ๋ฐ”์ธ๋”ฉ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ˆ˜๋ช…์ฃผ๊ธฐ ๋™์•ˆ $ applyAsync () ํ์™€ $ evalAsync () ํ๊ฐ€ ๋ชจ๋‘ ํ”Œ๋Ÿฌ์‹œ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ๋งค์šฐ ๋‹ค๋ฅธ ๋‘ ๊ณณ์—์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

$ applyAsync () ํ๋Š” AngularJS๊ฐ€ ๋”ํ‹ฐ ๋ฐ์ดํ„ฐ ๊ฒ€์‚ฌ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— $ digest์˜ ๋งจ ์œ„์—์„œ ๋งŒ ํ”Œ๋Ÿฌ์‹œ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ $ applyAsync () ํ๋Š” $ digest ๋™์•ˆ ์ตœ๋Œ€ ํ•œ ๋ฒˆ ํ”Œ๋Ÿฌ์‹œ๋˜๋ฉฐ $ digest๊ฐ€ ์‹œ์ž‘๋˜๊ธฐ ์ „์— ํ๊ฐ€ ์ด๋ฏธ ์ฑ„์›Œ์ง„ ๊ฒฝ์šฐ์—๋งŒ ํ”Œ๋Ÿฌ์‹œ๋ฉ๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด์— $ evalAsync () ํ๋Š” $ digest ๋‚ด๋ถ€์˜ "dirty check"๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” while ๋ฃจํ”„์˜ ๋งจ ์œ„์— ํ”Œ๋Ÿฌ์‹œ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋‹ค์ด์ œ์ŠคํŠธ ์ค‘์— $ evalAsync () ํ์— ์ถ”๊ฐ€ ๋œ ๋ชจ๋“  ํ‘œํ˜„์‹์€ ๋™์ผํ•œ ๋‹ค์ด์ œ์ŠคํŠธ ๋‚ด์—์„œ ๋‚˜์ค‘์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์ด ์ฐจ์ด๋ฅผ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด $ watch () ๋ฐ”์ธ๋”ฉ ๋‚ด์—์„œ $ evalAsync ()์— ์˜ํ•ด ์ถ”๊ฐ€ ๋œ ๋น„๋™๊ธฐ์‹์ด ๋™์ผํ•œ ๋‹ค์ด์ œ์ŠคํŠธ์—์„œ ์‹คํ–‰๋œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. $ watch () ๋ฐ”์ธ๋”ฉ ๋‚ด์—์„œ $ applyAsync ()์— ์˜ํ•ด ์ถ”๊ฐ€ ๋œ ๋น„๋™๊ธฐ์‹์€ ์ดํ›„ ์‹œ์  (~ 10ms)์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์ด๋ฏธ ๋‹น์‹  ์ค‘ ์ผ๋ถ€์—๊ฒŒ ๋„์›€์ด๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค :-).


// update browser
    $rootScope.$watch(function $locationWatch() {
      var oldUrl = trimEmptyHash($browser.url());
      var newUrl = trimEmptyHash($location.absUrl());
      var oldState = $browser.state();
      var currentReplace = $location.$$replace;
      var urlOrStateChanged = oldUrl !== newUrl ||
        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);

      if (initializing || urlOrStateChanged) {
        initializing = false;

        $rootScope.$applyAsync(function() {
          var newUrl = $location.absUrl();
          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
              $location.$$state, oldState).defaultPrevented;

          // if the location was changed by a `$locationChangeStart` handler then stop
          // processing this location change
          if ($location.absUrl() !== newUrl) return;

          if (defaultPrevented) {
            $location.$$parse(oldUrl);
            $location.$$state = oldState;
          } else {
            if (urlOrStateChanged) {
              setBrowserUrlWithFallback(newUrl, currentReplace,
                                        oldState === $location.$$state ? null : $location.$$state);
            }
            afterLocationChange(oldUrl, oldState);
          }
        });
      }

      $location.$$replace = false;

      // we don't need to return anything because $evalAsync will make the digest loop dirty when
      // there is a change
    });

์—ฌ๊ธฐ ๋˜ ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” Angular ์ฝ”๋“œ๋ฒ ์ด์Šค์— ์ต์ˆ™ํ•˜์ง€ ์•Š์ง€๋งŒ ๋…ผ๋ฆฌ๋Š” ํ•ฉ๋ฆฌ์ ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ € url (...) ํ•จ์ˆ˜๋Š” ํ˜„์žฌ ์˜ฌ๋ฐ”๋ฅธ URL์„ ์ฆ‰์‹œ ๋ฐ˜ํ™˜ํ•˜๋Š” location.href์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ๋™์ผํ•œ ์‹คํ–‰ ๋ฃจํ”„์—์„œ ํ˜ธ์ถœ๋˜๊ธฐ ๋•Œ๋ฌธ์— $ digest ์•ˆ์ •ํ™”์ฃผ๊ธฐ ๋‚ด์—์„œ ๊ณ„์†ํ•ด์„œ ์ด์ „ URL์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์ด ํŒจ์น˜๋Š” 'pendingHref'๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํ• ๋‹น์„ ์ถ”์ ํ•˜๊ณ  ์„ค์ •๋œ ๊ฒฝ์šฐ ํ•ด๋‹น ๊ฐ’์„ ๋Œ€์‹  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ’์ด location.href์™€ ์ •๋ ฌ๋˜๋ฉด ๋ณด๋ฅ˜์ค‘์ธ ๊ฐ’์ด ์ง€์›Œ์ง‘๋‹ˆ๋‹ค. URL ์ง‘ํ•ฉ ์ค‘์— url () get์ด ํ˜ธ์ถœ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ๋ฅผ ํฌ์ฐฉํ•˜๊ธฐ ์œ„ํ•ด ํƒ€์ด๋จธ๊ฐ€ 0ms๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ์™„๋ฒฝํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ๋…ผ๋ฆฌ๋Š” ์ž‘๋™ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ฃผ๋กœ ์„ฑ๋Šฅ ์ง€์—ฐ์„ ์ผ์œผํ‚ค์ง€ ์•Š๋Š” ๋Œ€์ฒด ์ ‘๊ทผ ๋ฐฉ์‹์„ ๊ณ ๋ คํ•˜๊ธฐ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ Angular ํƒœ๊ทธ v1.4.3์„ ๊ธฐ๋ฐ˜์œผ๋กœํ•ฉ๋‹ˆ๋‹ค.

diff --git a/src/ng/browser.js b/src/ng/browser.js
index 928de95..3b9957e 100644
--- a/src/ng/browser.js
+++ b/src/ng/browser.js
@@ -87,7 +87,9 @@ function Browser(window, document, $log, $sniffer) {
   var cachedState, lastHistoryState,
       lastBrowserUrl = location.href,
       baseElement = document.find('base'),
-      reloadLocation = null;
+      reloadLocation = null,
+      pendingHref = null,
+      pendingHrefTimer = null;

   cacheState();
   lastHistoryState = cachedState;
@@ -124,6 +126,18 @@ function Browser(window, document, $log, $sniffer) {
     if (location !== window.location) location = window.location;
     if (history !== window.history) history = window.history;

+    // Schedule cleaning up pendingHref on the next run loop for setting URL. This is to handle
+    // the case where the browser doesn't update the location.* properties immediately
+    if (!pendingHrefTimer && pendingHref && url) {
+      pendingHrefTimer = setTimeout(function () {
+        if (location.href == pendingHref) {
+          console.log('Actual href updated... setting pendingHref to null from setTimeout');
+          pendingHref = null;
+        }
+        pendingHrefTimer = null;
+      }, 0);
+    }
+
     // setter
     if (url) {
       var sameState = lastHistoryState === state;
@@ -147,6 +161,7 @@ function Browser(window, document, $log, $sniffer) {
         // Do the assignment again so that those two variables are referentially identical.
         lastHistoryState = cachedState;
       } else {
+        pendingHref = url;
         if (!sameBase || reloadLocation) {
           reloadLocation = url;
         }
@@ -161,10 +176,22 @@ function Browser(window, document, $log, $sniffer) {
       return self;
     // getter
     } else {
+      var href = location.href.replace(/%27/g, "'");
+      if (pendingHref) {
+        //console.log('.. using pendingHref for url() return value');
+        href = pendingHref;
+      }
+
+      if (location.href == pendingHref) {
+        console.log('Actual href updated... setting pendingHref to null in getter');
+        pendingHref = null;
+      }
+
+      //var href = location.href.replace(/%27/g,"'");
       // - reloadLocation is needed as browsers don't allow to read out
       //   the new location.href if a reload happened.
       // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
-      return reloadLocation || location.href.replace(/%27/g,"'");
+      return reloadLocation || href;
     }
   };

์†”๋ฃจ์…˜์— ๋Œ€ํ•ด @CleverCoder ์—๊ฒŒ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค! ๋งค๋ ฅ์ฒ˜๋Ÿผ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค! : +1 :

๋ฟก ๋นต๋€จ
์•ต๊ทค๋Ÿฌ ํŒ€์— ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผํ•˜๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@viattik ๋ฒ„๊ทธ๊ฐ€ UIWebView (Apple) ์ž์ฒด์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ๋„ ํŒ€์ด iOS9์˜ UIWebView์— ๋Œ€ํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์ฑ„ํƒํ•œ๋‹ค๋ฉด ์ €๋ฅผ ๋†€๋ผ๊ฒŒ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํ•ญ์ƒ ์‹œ๋„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค ...

์•ˆ๋…•ํ•˜์„ธ์š”.
๋‹ค๋ฅธ ๋ธŒ๋ผ์šฐ์ €์—๋Š” ๋งŽ์€ ๋ฒ„๊ทธ๊ฐ€ ์žˆ์œผ๋ฉฐ ๊ฐ๋„ ์ฝ”๋“œ์˜ ๋ฒ„๊ทธ์— ๋Œ€ํ•œ ๋งŽ์€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.
๊ณต์‹์ ์œผ๋กœ UIWebView๋ฅผ ์ง€์›ํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ๋งŽ์€ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์•ฑ์ด ์†์ƒ ๋  ๊ฒƒ์ด๋ฉฐ Apple์ด ๋ฒ„๊ทธ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ๊นŒ์ง€ ์ตœ์‹  iOS์˜ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์•ฑ์—์„œ ์•ต๊ทค๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฒƒ์€ ์ œ๊ฐ€ ๋งํ•˜๊ณ  ์‹ถ์€ ๊ฝค ํฐ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์‹œ๋„ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@viattik ๋‚˜๋Š” ์ „์ ์œผ๋กœ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  btw : Apple์€ UIWebView ๋ฒ„๊ทธ๋ฅผ ์ˆ˜์ •ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋‚ฎ๋‹ค๊ณ  ์šฐ๋ฆฌ์—๊ฒŒ ์ „๋‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์‹ค์ œ๋กœ : ์‹œ๋„ํ•ด๋ณด์‹ญ์‹œ์˜ค ;-)

์—ฌ๋Ÿฌ๋ถ„, ์ด์ƒ์ ์œผ๋กœ๋Š” ์žฌํ˜„ ์‚ฌ๋ก€์™€ ํ•จ๊ป˜ ์ด๊ฒƒ์„ ์›นํ‚ท ๋ฒ„๊ทธ๋กœ ๊ฒŒ์‹œ ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด WebKit ์ธก์˜ ์ผ๋ถ€ ์—ฐ๋ฝ์ฒ˜์— ๋Œ€ํ•ด ํ›„์† ์กฐ์น˜๋ฅผ ์ทจํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. https://bugs.webkit.org/

๋ฟก ๋นต๋€จ
์›นํ‚ท ๋ฒ„๊ทธ์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. iOS9์˜ UIWebView์—์„œ๋งŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. iOS9์˜ Safari๋Š” ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

@ raftheunis87 ๊ท€ํ•˜์˜ ์ฝ”๋“œ ์ œ์•ˆ์— ๊ฐ์‚ฌ ๋“œ๋ฆฌ๋ฉฐ ์™„๋ฒฝํ•˜๊ฒŒ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค.

@ raftheunis87 @CleverCoder ๋Š” ionic-angular๋กœ ์ž‘์—…ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๊นŒ? ๋” ์ž์„ธํ•˜๊ฒŒ ์–˜๊ธฐํ•ด ์ฃผ ์‹œ๊ฒ ์–ด์š”?

@abrahamrkj ๋‚˜๋Š” ์ด์˜จ์— ๋Œ€ํ•œ ๊ฒฝํ—˜์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ionic์„ ์‚ฌ์šฉํ•  ๋•Œ ์‚ฌ์šฉ์ž ์ •์˜๊ฐ€ ๊ฐ๋„์— ์žˆ์Šต๋‹ˆ๊นŒ? ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๋™์ผํ•œ ์ˆ˜์ •์ด ์ด์˜จ ๊ฐ๋„์—์„œ๋„ ์ž‘๋™ํ•œ๋‹ค๊ณ  ๋งํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค ...

@ raftheunis87 https://github.com/driftyco/ionic/tree/master/js ์ด๊ฒƒ์€ ๊ทธ๋“ค์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฐ๋„์ž…๋‹ˆ๋‹ค.

pull ์š”์ฒญ์˜ ๊ฒฝ์šฐ @CleverCoder +1. ๋‚˜๋Š” ์ด๊ฒƒ์ด ๋งŽ์€ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์•ฑ์„ ๋ง์น  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์ด ์ค‘์š”ํ•œ ๋ฌธ์ œ๋ผ๋Š”

๋‚˜๋Š” ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ฒ˜๋Ÿผ Angular ์ฝ”๋“œ๋ฒ ์ด์Šค์— ๊ฐ€๊น์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์ €ํ•˜์ง€๋งŒ ํ’€ ์š”์ฒญ์ด ๊ฐ€๊นŒ์šด ๋ฏธ๋ž˜์—์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณง ์†”๋ฃจ์…˜์„ ๋‹ค์‹œ ๋ฐฉ๋ฌธํ•˜์—ฌ ๋ฐฉํƒ„์œผ๋กœ ๋งŒ๋“ค ๊ฒƒ์ž…๋‹ˆ๋‹ค. window.location ๊ฐ์ฒด์˜ ์†์„ฑ์ด '์ฆ‰์‹œ'๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ด์ƒํ•˜๊ฒŒ ๋ณด์ž…๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ํ•œ ํ…Œ์ŠคํŠธ์—์„œ 'popstate'๋ฐ 'hashchange'์ด๋ฒคํŠธ ํ›„ํฌ๊ฐ€ ์ œ์ž๋ฆฌ์— ์žˆ์ง€ ์•Š๋Š” ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์œ ์ง€๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜์—ฌ ์ง€์—ฐ๋œ ๋ณ€๊ฒฝ์˜ ์›์ธ์ด ์‹ค์ œ๋กœ ๋ญ”๊ฐ€๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ฒŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. else .. ์•„๋งˆ๋„ ๊ทธ ์‚ฌ๊ฑด์˜ ํƒ€์ด๋ฐ์ด ๋ฐ”๋€Œ์—ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค (๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋‚ด๊ฐ€ ๊ด€์ฐฐ ํ•œ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค).
๋‚˜๋Š” ์•ž์œผ๋กœ ๋ฉฐ์น  ๋™์•ˆ ์ด๊ฒƒ์„ ์ง€์ผœ ๋ณผ ๊ฒƒ์ด๋ฉฐ, ๋” ๋‚˜์€ ํ‘œ๋ฉด์ด ์—†๋‹ค๋ฉด, ์ง€๊ธˆ๊นŒ์ง€ ๋‚ด๊ฐ€ ์•Œ๊ณ ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์กฐ๊ธˆ ๋” ๊นŠ์ด ํŒŒ๊ณ ๋“ค ๊ฒƒ์ด๋ฉฐ, ํ–‰๋™์˜ ๋ณ€ํ™”๋ฅผ ๋‹ค๋ฃจ๊ธฐ์— ๋” ์ข‹์€ ๊ณณ์ด ์—†๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ˜ผ๋ž€ ์Šค๋Ÿฌ์šฐ๋ฉด ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์‚ฌ๊ฑด๊ณผ ๊ด€๋ จํ•˜์—ฌ ์•„์ง ์™„์ „ํžˆ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๋Š” ๋งŽ์€ ์ผ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
๊ฑด๋ฐฐ!

... ์˜ˆ, @borrull , ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. Apple์ด ๋” ์ด์ƒ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์œผ๋ฉด ์ด๊ฒƒ์€ ์‹ฌ๊ฐํ•œ ๋˜‘๋”ฑ ๊ฑฐ๋ฆฌ๋Š” ์‹œํ•œ ํญํƒ„์ด๋ฉฐ ์‹ค์ œ๋กœ ์ž˜๋ชป๋œ ๋ˆ„๋ฅด๊ธฐ ๋ฐ ์†๊ฐ€๋ฝ ํฌ์ธํŒ…์œผ๋กœ ์ด์–ด์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ž„์‹œ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ์„œ ํƒ€์ด๋จธ์˜ ํŒฌ์€ ์•„๋‹ˆ์ง€๋งŒ (์ฐจ๋ผ๋ฆฌ ์ด๋Ÿฌํ•œ ๋‚ด์žฅ ์†์„ฑ์— ๋Œ€ํ•œ ๋กœ์ง ํ๋ฆ„์„ ๊ฐœ์„ ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค), ์ผ๋‹จ ์„ค์ • ํ•œ ๊ฐ’ ๋ณ€๊ฒฝ์— ์˜์กด ํ•  ์ˆ˜ ์—†๋‹ค๋ฉด, ์šฐ๋ฆฌ๋Š” ์–ด๋””์— ๊ทธ๋ฆด๊นŒ์š”? ์„ ? ์šฐ๋ฆฌ๊ฐ€ ์‹ ๋ขฐํ•  ์ˆ˜์—†๋Š” ๋‹ค๋ฅธ ์†์„ฑ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ์ด์ƒํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@CleverCoder ๊ทธ๋ƒฅ ๊ฐ์‚ฌ์˜ ๋ง์„ ์ „ํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. ํŒจ์น˜๊ฐ€ ์ •๋ง ํ•˜๋ฃจ๋ฅผ ๊ตฌํ–ˆ์Šต๋‹ˆ๋‹ค!

@CleverCoder ์ฃผ์–ด์ง„ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์•ต๊ทค๋Ÿฌ์—์„œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ณ  ์•ต๊ทค๋Ÿฌ ์†Œ์Šค๋ฅผ ํŒจ์น˜ํ•˜์ง€ ์•Š๊ณ  ์ œ๊ณต๋˜๋Š” ์†”๋ฃจ์…˜์„ ํŒŒ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

cssua๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์ด ์„ค์ •์„ ํŠน์ • ํ™˜๊ฒฝ์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋„๋ก ๊ตฌ์„ฑ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    app.config(['$provide', ($provide) => {
        $provide.decorator('$browser', ['$delegate', ($delegate) => {
            var origUrl = $delegate.url;

            var pendingHref = null;
            var pendingHrefTimer = null;

            var newUrl = function (url, replace, state) {

                if (url) {
                    // setter
                    var result = origUrl(url, replace, state);

                    if (window.location.href != url) {

                        if (pendingHref != url) {
                            pendingHref = url;

                            if (pendingHrefTimer) clearTimeout(pendingHrefTimer);

                            pendingHrefTimer = setTimeout(function () {
                                if (window.location.href == pendingHref) {
                                    pendingHref = null;
                                }
                                pendingHrefTimer = null;
                            }, 0);
                        }
                    }

                    return result;
                } else {
                    // getter
                    if (pendingHref == window.location.href) {
                        pendingHref = null;
                    }

                    return pendingHref || origUrl(url, replace, state);
                }
            };

            $delegate.url = newUrl;

            return $delegate;
        }]);
    }]);

@CleverCoder ์ฐธ์กฐ # 12635

@ jd-carroll : ์ •๋ง ํฅ๋ฏธ ๋กญ ๋„ค์š”. ์‹œ๊ฐ„์ด ์žˆ์œผ๋ฉด ์˜ค๋Š˜ ์กฐ๊ธˆ ๋Šฆ๊ฒŒ ๋‹ค์‹œ ๋ฐฉ๋ฌธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๊ฑด์œผ๋กœ ๊ฝค ํ ๋ป‘ ์ –์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋‹จ์ง€ ๋” ๋งŽ์€ ์ˆ˜์ˆ˜๊ป˜๋ผ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๊ทธ๊ฒƒ์€ location. * ๊ฐ’ ์—…๋ฐ์ดํŠธ์— ์ง€์—ฐ์„ ๋„์ž…ํ•ด์„œ๋Š” ์•ˆ๋˜๋Š” ๋ณ„๋„์˜ ๋ฌธ์ œ์ฒ˜๋Ÿผ ๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

@realityfilter : ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ๋Œ€ํ•ด ์–ธ๊ธ‰ํ•˜๋Š” ๊ฒƒ์ด ์žฌ๋ฏธ

๋ชจ๋‘ ์•ˆ๋…•,
์ด ์ˆ˜์ •์ด ์ˆ˜์ •ํ•˜๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ์— ๋ฒ„๊ทธ๋ฅผ ๋„์ž…ํ–ˆ๋‹ค๊ณ  ๋ง๋ถ™์ด๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

ํ…œํ”Œ๋ฆฟ์—๋Š” href = "#"๋ฐ ng-click = "someCall ()"์„ ์‚ฌ์šฉํ•˜๋Š” ์•ต์ปค ํƒœ๊ทธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. href๋กœ ์ธํ•ด์ด ์ˆ˜์ •์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์ดํŠธ๊ฐ€ index.html๋กœ ์ด๋™ํ–ˆ์Šต๋‹ˆ๋‹ค. href๋ฅผ ์ œ๊ฑฐํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Ionic์˜ ๋’ค๋กœ ๋ฒ„ํŠผ ํƒ์ƒ‰ ์ค‘์— ์•ฑ์ด ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค. ๋จผ์ € ์ƒˆ๋ณด๊ธฐ๋กœ ์ด๋™ ํ•œ ๋‹ค์Œ ๋ถ€๋ถ„์ ์œผ๋กœ ์ด์ „๋ณด๊ธฐ๋กœ ๋Œ์•„์˜จ ๋‹ค์Œ Ionic์˜ ๋ชจ๋“  ํ•ด์ƒ๋„์—์„œ IOS9 ๋ฒ ํƒ€์˜ ์ƒˆ๋ณด๊ธฐ๋กœ ๋‹ค์‹œ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.

iOS 9 ๋ฒ ํƒ€ 5 13A4325c, Angular 1.4.0 (cordova-ios 3.9.1 ํฌํ•จ)๊ณผ ๋™์ผํ•œ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ UIWebView ๋ฒ„๊ทธ๊ฐ€ ์ˆ˜์ •๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

Angular v1.2.27์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋™์ผํ•œ ๋ฌธ์ œ

1.2.27 ๋ฆด๋ฆฌ์Šค๊นŒ์ง€ ์ถ”์ ํ–ˆ์ง€๋งŒ ๋ฒ„๊ทธ๋Š” ์ด์ „ 1.2.26 ๋ฆด๋ฆฌ์Šค์— ์—†์Šต๋‹ˆ๋‹ค.

ํŠนํžˆ์ด ์ปค๋ฐ‹ ์ด ๋ฒ”์ธ์ž…๋‹ˆ๋‹ค.

@damrbaby ๋‚ด๊ฐ€ ๋ญ”๊ฐ€ ํ•  ์ˆ˜์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ Mobile Safari์—์„œ ์ตœ์‹  ๋ฒ„์ „์˜ angular์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์€ iOS 9์—์„œ uiWebView๋กœ ๋ฌด์–ธ๊ฐ€๋ฅผํ•ด์•ผํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋ถ„๋ช…ํžˆํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Angular ์†Œ์Šค ์ฝ”๋“œ์—์„œ ๋ณ€๊ฒฝ ํ•œ ์‚ฌํ•ญ์ด ๋ฐ˜๋“œ์‹œ ๋‚˜์œ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.

@CleverCoder @realityfilter @ jyc66 ๊ฐ์‚ฌํ•˜๋‹ค๊ณ  ๋งํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

iOS9 GM Seed์— ์—ฌ์ „ํžˆ ๋ฌธ์ œ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์•ฑ์„ ์—…๋ฐ์ดํŠธํ•˜์‹ญ์‹œ์˜ค!

iOS9 GM (13A340)์— ์—ฌ์ „ํžˆ ๋ฌธ์ œ๊ฐ€ ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ด๋Š” Apple์ด ๋ฌด์–ธ๊ฐ€๋ฅผ ๋ง๊ฐ€ ๋œจ ๋ ธ๊ณ  ์•ฑ์ด ์ถฉ๋Œํ•˜์ง€ ์•Š๋„๋ก ์•ฑ์„ ๋‹ค์‹œ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค (์ผ๋ถ€๋Š” ๋ช‡ ๋‹ฌ ๋˜๋Š” 1 ๋…„ ์ด์ƒ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Œ). ์ดํ•ด๊ฐ€๊ฐ‘๋‹ˆ๋‹ค :(. Apple์ด iOS9 ์ถœ์‹œ๋ฅผ ์œ„ํ•ด์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋„๋กํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์˜ค๋ž˜๋œ ์•ฑ์—์„œ ์ตœ์‹  Angular ๋ฒ„์ „์œผ๋กœ ์ด๋™ํ•˜๋ฉด ๋‹ค๋ฅธ ๊ธฐ๋Šฅ๋„ ์†์ƒ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Angular๊ฐ€ iOS9์— ๋ฌธ์ œ๊ฐ€์žˆ๋Š” ์œ ์ผํ•œ ํ”„๋ ˆ์ž„ ์›Œํฌ์ธ์ง€ ์˜์‹ฌ ์Šค๋Ÿฝ์Šต๋‹ˆ๊นŒ?

๋”ฐ๋ผ์„œ Ionic ํŒ€์˜ @adamdbradley , @perrygovier ๋ฐ @mhartington ์€ Ionic ๋ฐ ์ผ๋ฐ˜ Angular ์•ฑ์—์„œ๋„ ์ž‘๋™ํ•˜๋Š” ์ˆ˜์ •์„ ์œ„ํ•ด ํ•˜๋ฃจ ์ข…์ผ ์ž‘์—… ํ•ด ์™”์Šต๋‹ˆ๋‹ค. ๋ชฉํ‘œ๋Š” Angular๋ฅผ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€์—†๋Š” ๋“œ๋กญ ์ธ ์ˆ˜์ •์ด๋ฉฐ ๋Œ€๋ถ€๋ถ„์˜ 1.2+ Angular ๋ฒ„์ „์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ์ด ํŒจ์น˜ ๋ฅผ ์ ์šฉํ•˜์—ฌ $browser ๋ฅผ ์žฅ์‹ํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š” ํ˜„์žฌ ๋ฒˆ๋“ค ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค. ์ฐธ๊ณ  : ์ด๊ฒƒ์€ Angular 1.4.3์„ ๊ธฐ๋ฐ˜์œผ๋กœํ•˜๋ฉฐ Angular์—์„œ browser.js ์ผ์ข…์˜ "์ˆ˜์ •์ด์žˆ๋Š” ๋ณต์ œ"์ž…๋‹ˆ๋‹ค. https://github.com/driftyco/ionic/blob/ios9-patch/js /angular/service/decorators/ios9-browser-fix.js

๋˜ํ•œ CDN์— ํŒจ์น˜๋ฅผ ๋ฐฐ์น˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜์— CDN ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๊ถŒ์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. CDN ํŒŒ์ผ์€ ๊ฑฐ๊ธฐ์—๋งŒ ์žˆ์œผ๋ฏ€๋กœ ์ง€๊ธˆ ํ…Œ์ŠคํŠธํ•˜๊ธฐ๊ฐ€ ๋” ์‰ฝ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธํ•˜๋ ค๋ฉด angular ๋˜๋Š” ionic.bundle.js ํŒŒ์ผ ์•„๋ž˜์—์ด ์Šคํฌ๋ฆฝํŠธ ํƒœ๊ทธ๋ฅผ ๋ฐฐ์น˜ํ•˜์‹ญ์‹œ์˜ค.

<script src="https://code.ionicframework.com/patch/ios9-$browser-patch.js"></script>

๋˜ํ•œ ํ˜„์žฌ iOS9์—์„œ ์‹คํ–‰ ์ค‘์ธ์ง€ ์—ฌ๋ถ€์— ๊ด€๊ณ„์—†์ด ํŒจ์น˜๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. iOS 9 UIWebView์—์„œ๋งŒ ์‹คํ–‰๋˜๋„๋ก ๊ณง ์ˆ˜์ • ๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์—์„œ ํ•ด๋‹น Ionic ๋ฌธ์ œ๋ฅผ ๋”ฐ๋ฅด์„ธ์š” : https://github.com/driftyco/ionic/issues/4082#issuecomment -139079725

์•ˆ๋…•ํ•˜์„ธ์š”,

UI-sref๋Š” ๋งค๋ ฅ์ฒ˜๋Ÿผ ์ž‘๋™ํ•˜์ง€๋งŒ $ state.go๋Š” ๋’ค๋กœ ๋ฒ„ํŠผ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค.
์ด ์ˆ˜์ • ์‚ฌํ•ญ์„ ์ ์šฉํ•œ ํ›„์—๋„ ํŽ˜์ด์ง€๊ฐ€ ๋งŽ์ด ๊นœ๋ฐ•์ž…๋‹ˆ๋‹ค.

๋ฌธ์•ˆ ์ธ์‚ฌ,
์•„์ œ์ด ์‹ฑ

2015 ๋…„ 9 ์›” 10 ์ผ ๋ชฉ์š”์ผ ์˜ค์ „ 6:23์— Max Lynch [email protected] ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ผ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ @adamdbradley https://github.com/adamdbradley , @perrygovier
https://github.com/perrygovier ๋ฐ @mhartington
Ionic ํŒ€์˜ https://github.com/mhartington ์ด ์ž‘์—… ์ค‘์ž…๋‹ˆ๋‹ค.
Ionic ๋ฐ ์ผ๋ฐ˜ Angular ์•ฑ์—์„œ ์ž‘๋™ํ•˜๋Š” ์ˆ˜์ • ์‚ฌํ•ญ์— ๋Œ€ํ•ด ํ•˜๋ฃจ ์ข…์ผ
์ž˜. ๋ชฉํ‘œ๋Š” ์ˆ˜์ •์ด ํ•„์š”์—†๋Š” ๋“œ๋กญ ์ธ ์ˆ˜์ •์ž…๋‹ˆ๋‹ค.
Angular๋Š” ๋Œ€๋ถ€๋ถ„์˜ 1.2+ Angular ๋ฒ„์ „์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ $ browser๋ฅผ ๊พธ๋ฏธ๊ณ  ์ˆ˜์ •ํ•˜๋Š” ํ˜„์žฌ ๋ฒˆ๋“ค ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค.
์ด ํŒจ์น˜ ์ ์šฉ
https://github.com/angular/angular.js/issues/12241#issuecomment -130744518.
์ฐธ๊ณ  : ์ด๊ฒƒ์€ Angular 1.4.3์„ ๊ธฐ๋ฐ˜์œผ๋กœํ•˜๋ฉฐ ์ผ์ข…์˜ "์ˆ˜์ • ๋œ ๋ณต์ œ"์ž…๋‹ˆ๋‹ค.
์ ์ ˆํ•œ Angular์˜ browser.js :
https://github.com/driftyco/ionic/blob/ios9-patch/js/angular/service/decorators/ios9-browser-fix.js

๋˜ํ•œ CDN์— ํŒจ์น˜๋ฅผ ๋ฐฐ์น˜ํ–ˆ์Šต๋‹ˆ๋‹ค. CDN ํŒŒ์ผ ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
ํ”„๋กœ๋•์…˜์˜ ๊ฒฝ์šฐ์—๋งŒ ์žˆ์œผ๋ฏ€๋กœ ์ง€๊ธˆ ํ…Œ์ŠคํŠธํ•˜๊ธฐ๊ฐ€ ๋” ์‰ฝ์Šต๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์–ด๋–ป๊ฒŒ ์ง„ํ–‰๋˜๋Š”์ง€ ์•Œ๋ ค์ฃผ์‹ญ์‹œ์˜ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธํ•˜๋ ค๋ฉด์ด ์Šคํฌ๋ฆฝํŠธ ํƒœ๊ทธ๋ฅผ ๊ฐ๋„ ๋˜๋Š”
ionic.bundle.js ํŒŒ์ผ :