Hot Reload

O desenvolvimento de telas para Xamarin.Forms já foi abordado antes por aqui, e o assunto continua popular, mesmo depois de 1 ano passado. É bem claro que a comunidade de Xamarin entendeu que precisa de uma forma mais rápida de desenvolver telas para Xamarin.Forms, até então a comunidade estava dividida entre pessoas que tinham uma licença LiveXAML e estavam felizes, e pessoas que ainda sofriam para desenvolver telas. Esse não é mais o caso, dado que agora existe uma solução simples, totalmente gratuita e open source: o HotReload.

Antes de tudo, vou já começar respondendo a pergunta principal:

mas funciona mesmo?

Sim! Eu e o Robson Amorim utilizamos somente ele nos últimos 2 meses em um ChatBot que desenvolvemos aqui na Lambda3, e ele funcionou perfeitamente durante todo o projeto.

O HotReload é open source e foi feito principalmente por duas pessoas: Andrei e Stanislav. O fato do projeto ter o código aberto e aceitar contribuições tem contribuído para sua evolução rápida, de 2 meses pra cá vimos várias melhorias no projeto, que continua sendo evoluído.

Hoje eu enxergo pelo menos três vantagens em utilizá-lo:

  • Totalmente gratuito
  • Funciona com qualquer IDE/Editor de texto
  • Permite visualizar a tela simultaneamente em quantos devices/emuladores você quiser

Xamarin.HotReload

Começar a utilizá-lo é extremamente simples, você precisa primeiro adicionar o pacote NuGet ao projeto compartilhado do Xamarin.Forms:

Install-Package Xamarin.HotReload

Agora é necessário adicionar esse trecho de código no App.xaml.cs:

using Xamarin.Forms;

namespace HotReloadSample
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
#if DEBUG
            HotReloader.Current.Start(this);     
#endif
            MainPage = new NavigationPage(new MainPage());
        }
    }
}

Ao rodar o app ele já estará preparado para receber modificações no XAML para atualizar a tela. Agora é preciso fazer a IDE enviar essas modificações no XAML. O jeito mais fácil se você estiver utilizando Visual Studio / Visual Studio for Mac é utilizando uma extensão. Para o Mac, a extensão pode ser baixada aqui, para o Windows, ela está no Marketplace.

Caso tenha problemas na configuração a extensão, sugiro ler o README do projeto aqui.

Agora a tela deve atualizar automaticamente ao salvar o XAML:

Xamarin Hot Reload

Xamarin Hot Reload

Utilizei o projeto InSpace como exemplo

Android

Se você estiver tentando configurar o HotReload em um emulador Android e não está conseguindo, talvez seja preciso redirecionar a porta com o adb. Execute o seguinte comando sempre que subir o emulador Android:

adb forward tcp:8000 tcp:8000

O executável adb fica no dentro das pastas do Android SDK, neste caminho: {PASTA_ANDROID_SDK}/platform-tools.

Se estiver utilizando um dispositivo físico Android, você precisa configurá-lo conforme descrito aqui.

Utilizando HotReload com outras IDEs e Editores

No Mac eu tenho usado bastante o Rider da JetBrains junto com o Visual Studio for Mac, portanto preciso configurar o HotReload sem as extensões para que eu possa utilizá-lo no Rider, no Code, no Vim, ou em qualquer outro editor que eu prefira.

Para isso é preciso buildar o projeto Observer do HotReload em modo Release, copiar o .exe gerado para a raíz do projeto Xamarin.Forms e utilizá-lo para ficar observando as mudanças no seu diretório.

No Mac:

mono Xamarin.Forms.HotReload.Observer.exe

No Windows:

.\Xamarin.Forms.HotReload.Observer.exe

Caso a porta utilizada seja a padrão, ele enviará o XAML editado para os devices/emuladores que estiverem ouvindo.

Atualizando iOS e Android ao mesmo tempo

No nosso projeto queríamos ser capazes de ver as mudanças no Android e no iOS ao mesmo tempo, o HotReload já suporta esse cenário, só é preciso fazer algumas alterações.

Se, assim como nós, você estiver usando o simulador iOS e o emulador Android, ambos rodando na mesma máquina, não será mais possível utilizar a mesma porta padrão do HotReload, remova a inicialização do HotReload do arquivo App.xaml.cs.

Para o Android, a chamada vai ficar na MainActivity:

protected override void OnCreate(Bundle savedInstanceState)
{
    TabLayoutResource = Resource.Layout.Tabbar;
    ToolbarResource = Resource.Layout.Toolbar;

    base.OnCreate(savedInstanceState);

    global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

    var xfApplication = new App();
#if DEBUG
    HotReloader.Current.Start(xfApplication, 4290);
#endif
    LoadApplication(xfApplication);
}

Para o iOS, é preciso adicioná-la ao AppDelegate:

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    global::Xamarin.Forms.Forms.Init();

    var xfApplication = new App();
#if DEBUG
    HotReloader.Current.Start(xfApplication, 4291);
#endif
    LoadApplication(xfApplication);

    return base.FinishedLaunching(app, options);
}

Agora o HotReload está rodando no Android na porta 4290, e no iOS na porta 4291. É necessário rodar novamente o comando de forward do adb, agora se atentando para utilizar a porta 4290 que foi utilizada no Android.

Dica: você não precisa estar debugando nenhuma das aplicações se estiver utilizando o Observer, o HotReload fica de olho em qualquer alteração no XAML e envia para as portas configuradas.

O último passo agora é configurar o Observer do HotReload para enviar as modificações para múltiplas portas, dessa maneira é possível rodá-lo independente da IDE usada e do suporte a múltiplos IPs pelas extensões.

Passe os parâmetros dos endereços utilizados:

mono Xamarin.Forms.HotReload.Observer.exe u=http://127.0.0.1:4290,http://127.0.0.1:4291

Pronto! Duas telas sendo atualizadas simultaneamente!

Xamarin Hot Reload iOS and Android

A cereja no Bolo

Apesar de já ter a solução funcionando corretamente no iOS e no Android, eu achei que ter que digitar os comandos para subir o Observer com as portas corretas e fazer o forward da porta do Android era muito repetitivo e passível de erro, por isso decidi automatizar tudo num script Cake?.

Eu copiei o Observer para dentro da pasta /tools que o Cake utiliza por padrão e utilizei o seguinte script no meu build.cake, que já inclui todos os passos descritos acima:

Task("HotReload")
    .Does(() => {
        StartProcess("adb", "forward tcp:4290 tcp:4290");

        var urls = "u=http://127.0.0.1:4290,http://127.0.0.1:4291";
        var windowsProcess = "./tools/Xamarin.Forms.HotReload.Observer.exe";
        var processName = IsRunningOnWindows() ? windowsProcess : "mono";
        
        var processSettings = new ProcessSettings()
        {
            Arguments = IsRunningOnWindows() ? urls : $"{windowsProcess} {urls}"
        };

        using (var hotReloadProcess = StartAndReturnProcess(processName, processSettings))
        {
            hotReloadProcess.WaitForExit();
        }
    });

Para executar o script Cake agora é só executar no Mac:

./build.sh -Target=HotReload

No Windows:

./build.ps1 -Target HotReload

Com isso, sempre que editar uma tela no XAML tudo vai funcionar como mágica, tanto no Mac quanto no Windows.

Certifique-se que o adb está no PATH da sua máquina, caso contrário você receberá um erro ao rodar o script Cake.

Conclusão

Para o meu uso, a melhor solução é a última, com o Cake, pois tenho um script muito simples que compartilho com todas as pessoas do time e que já faz tudo que preciso para atualizar minhas telas no iOS e Android ao mesmo tempo, ainda consigo utilizá-lo de qualquer editor, até de um Notepad!

Editar XAML sem uma ferramenta de atualização é um impacto gigante na produtividade, e agora você não tem mais que se preocupar em pagar uma licença em dólar para ter isso! É uma experiência muito parecida com o que existe na Web.

Espero que comece a utilizar nos seus projetos e sinta o mesmo ganho de produtividade que tenho nos projetos em que atuo.

O sample utilizado nesse post está no meu GitHub para consulta de mais detalhes.

Imagem usada no post: David Švihovec, Unsplash

Mahmoud Ali

Desenvolvedor de Software na Lambda3, Microsoft MVP, amante de um bom café ☕️ e uma boa cerveja 🍺.