Ускорение Change-Build-Run цикла на iOS (работает и на Android)

UPD: Чтобы это все работало в Xamarin Studio 6 необходимо установить плагин отсюда: Continuous.Client.MonoDevelop.mpack (собрано из guthub).

В последнее время наш iOS продукт достаточно разросся, и по разным причинам (обоснованным и не очень :)) состоит из ~20 проектов). Все это, вкупе с новыми версиями Xamarin очень негативно сказывается на времени сборки и запуска.
Ну а UI часть, как известно, требует частого перезапуска/пересборки ввиду множества мелких изменений.

Ждать по 1-2 минуты, чтобы проверить, как будет смотреться увеличенный на 1 пиксель отступ, слегка поднадоело, и было решено поискать решения.

Решением оказался проект Continuous, который позволяет изменять классы и «применять» изменения прямо в запущенном приложении, без перезапуска. Реализуется это с помощью динамической компиляции.

Динамическая компиляция, как известно, давно доступна и в .Net, и в Mono, но на iOS её применению мешают строгие ограничения Apple. Однако, как небезосновательно решили в Xamarin, использовать Reflection.Emit и прочие инструкции динамической компиляции в дебаге никто не мешает.
Так родился Xamarin Inspector, который реализует REPL в iOS, а за ним и Continuous.

В оригинале, Continuous позволяет выбрать файл с ViewController‘ом (через меню Tools->Visualize class), и при каких-либо изменениях в коде класса, этот Контроллер пересоздается и пушится в существующий NavigationController.
«Под капотом» это работает как «переименование» вашего ViewController‘a в класс с анонимным именем (например, из MyViewController в MyViewController123546af54e45f), создание его через конструктор (для этого, кстати, строго необходимо наличие конструктора без аргументов) и добавление этого созданного контроллера в навигационный стек.

В некоторых приложениях, возможно, этого и достаточно, но в нашем случае ViewController’ы инициализируются различными данными, повторно используются в нескольких местах, а также тесно взаимодействуют с другими сервисами, поэтому просто показа измененного VC явно не хватало (он показывался «пустой»).

На помощь пришел известный механизм IoC. При этом, когда измененный ViewController динамически создается после примененных изменений, он просто перерегистрируется в ioc-контейнере. После этого в запущенном приложении мы вызываем пересоздание экрана (например, через навигацию «назад-вперед»), и при повторном показе видим уже измененный пользовательский интерфейс.
При этом мы использовали тот факт, что «инжектированный» ViewController работает полностью в памяти запущенного приложения и ему, к примеру, доступны все статические свойства и методы вашего проекта.

Таким образом типичный ViewController в псевдо-коде выглядит примерно так:

public class MyViewController : UIViewController, IMyViewController
{
    public MyViewController() : base(null, null)
    {
        Container.RegisterType(typeof(IMyViewController), this.GetType());
    }

    public void Initialize(/* параметры инициализации ViewController'a */) {}
}

//использование в реальном коде:
var myViewController = Container.Resolve<IMyViewController>();
NavigationController.PushViewController((UIViewController)myViewController, true);

P.S. Существенный минус Continuous — отсутствие поддержки partial классов. Таким образом, использовать его в связке с xib или storyboard довольно непросто. Мы же все чаще используем XibFree, и при этом подходе проблем не возникает.

P.P.S. Необходимые шаги по установке Conrinuous подробно расписаны на GitHub, но на всякий случай еще раз:ъ

  1. Установить Xamarin Inspector
  2. Установить расширение Continuous в Xamarin Studio (Xamarin Studio -> Add-In Manager, Install From File: Continuous.Client.MonoDevelop.mpack (собрано из guthub))
  3. Добавить Nuget-пакет Continuous в проект
  4. Добавить код ниже в AppDelegate или Activity
    #if DEBUG
    new Continuous.Server.HttpServer(this).Run();
    #endif
    

P.P.P.S. Да, в Андроид работает тоже :) Но в Андроид UI-проблемы решаются, чаще всего, изменением axml разметки, а такие изменения Continuous пока не подхватывает.

Опубликовать в Facebook
Опубликовать в Google Plus

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *