Episódio V - Navegação usando MvvmCross

Publicado em 23/04/2016

Cover

Episódio V - Navegação usando MvvmCross

Esse post é a continuação da série sobre MvvmCross em Xamarin. Você pode ler os episódios anteriores aqui. Também é recomendada a leitura do artigo Preparando uma PCL para Xamarin, que pode ser encontrado aqui, onde eu explico sobre PCLs e o básico sobre assíncronia. No post de hoje iremos explicar o modelo de navegação do MvvmCross e entender um pouco como implementar modelos de navegação diferentes do que vem “de fábrica”. Vamos lá?

Como a navegação acontece?

Eu mencionei no episódio II, ainda que superficialmente, como funciona a exibição de telas no MvvmCross. Cada ViewModel representa uma abstração e a View é como aquele VM deve ser exibida na tela. Como nem tudo do seu app acontece na mesma tela, é necessário controlar a ordem de exibição das telas (no nosso caso, ViewModels) de forma que elas apareçam em uma determinada ordem. Para tanto, o framework MvvmCross faz uso do método ShowViewModel<TViewModel>, método localizado na classe MvxNavigatingObject. Ainda nesse post eu irei explicar o que são os parâmetros que esse método pode receber, mas por hora vamos entender o que acontece depois que o método é chamado.

Primeiro precisamos entender que as VMs ficam na área compartilhada do código. Isso quer dizer que elas são independentes de código específico de plataforma. Basta estudar as plataformas mobile por algumas horas para perceber que os modelos de navegação são muito diferentes, afinal as apis expostas e as abordagens de gestão de memórias não são as mesmas. Isso quer dizer que por si só o código de PCL não será capaz de controlar a navegação. Para contornar isso, o MvvmCross faz uso de injeção de dependencia, um conceito que eu explicarei em detalhes no próximo artigo.

Sendo assim, são necessários 3 métodos ShowViewModel para sair da ViewModel e chegar na UI. O primeiro é o da classe MvxNavigatingObject, que é chamado pelas nossas VMs. O segundo é o da interface IMvxViewDispatcher. A responsabilidade dessa interface é assegurar que uma outra interface, a IMvxViewPresenter, será chamada e que isso acontecerá no Thread de UI (por isso que a interface IMvxViewDispatcher também implementa a interface IMvxMainThreadDispatcher). Como já disse, o terceiro método é o método Show da interface IMvxViewPresenter, responsável por efetivamente carregar a View.

E eu preciso implementar tudo isso?!

Não mesmo! Enquanto é muito importante entender como tudo isso funciona por baixo dos panos para ser capaz de escrever o seu código de forma mais eficaz, em aplicações simples é provável que você não precise implementar a interface IMvxViewPresenter (e mesmo em aplicações mais complexas você provavelmente não vai precisar implementar um IMvxViewDispatcher). Isso acontece porque o MvvmCross já tem essas interfaces implementadas para os casos mais comuns. Basta buscar no código fonte do MvvmCross por MvxNome da plataformaViewDispatcher para ver como é implementado o Dispatcher de cada uma das plataformas. Abaixo estão os links diretos para os dispatchers e os presenters. É interessante que você dê uma olhada no códigos fonte da plataforma que você tem mais familiaridade antes de prosseguir a leitura.

Ao olhar os dispatchers e os presenters você deve ter notado que enquanto os Dispatchers são todos muito similares (fazem o Marshal para o UI thread e encaminham a requisição para o presenter) os presenters geralmente são mais complexos, tendo cada plataforma a sua interface própria e, em certos casos, até mais de um presenter predefinido. Isso ocorre por dois motivos. Primeiro porque as necessidades de exibição de ViewModel variam bastante entre cada plataforma suportada pelo MvvmCross. O segundo motivo é que apps diferentes tem necessidades complemente diferentes de exibição de conteúdo.

Um app como o Snapchat exibe o seu conteúdo de uma forma completamente diferente do WhatsApp, por exemplo. Não tem como esperar que uma solução de exibição de ViewModels funcione para ambos da mesma maneira. Claro que o exemplo foi extremo, mas não precisamos ir tão longe: Se você quiser implementar conceitos que são relativamente comuns como o de Bottom Navigation ou Navigation Drawer, você precisa de um presenter com algumas alterações, já que os presenters padrão tratam ViewModels como páginas e esse nem sempre é o caso.

Uma ViewModel não é página? Como assim?!

Vou esperar você tomar uma água e se recuperar do choque. Não, uma ViewModel não representa uma página. Como dividir o app em páginas é o jeito mais comum de se desenvolver apps, geralmente essa abordagem de 1 página = 1 VM basta. Esse não é o caso sempre. Se você tem componentes que aparecem em mais de uma página (como no exemplo acima, um Navigation Drawer) faz sentido que esse componente seja representado por uma ViewModel e que o seu presenter o trate um pouco diferente.

Usarei como exemplo a plataforma Android. Uma abordagem para implementar o Navigation Drawer seria criar um presenter que extende o MvxFragmentsPresenter e trata a VM que representa o Drawer de forma diferente. Já que o presenter do Android tem acesso à Activity que está sendo atualmente exibida, basta verificar o tipo da VM antes de exibir. Se for o tipo que precisa ser tratado de forma diferente, faça o que deve ser feito, caso contrário apenas chame o método base.

Quando você começa a pensar que uma ViewModel é na verdade uma abstração de um pedaço da tela, fica bem mais fácil de entender esses conceitos. A ViewModel representa um pedaço da tela; o que acontece é que na maior parte das vezes esse pedaço ocupa a tela toda.

Só tem isso de possibilidade de customização?

Negativo, caro leitor. Te lembra dos parâmetros adicionais do método ShowViewModel? Pois bem, você pode trata-los no seu presenter com o intuito de moldar a navegação de acordo com as necessidades do seu app. Os dois primeiros, parameterBundle e presentationBundle, são utilizados para passar informação para o seu presenter. O primeiro, obviamente, passa parâmetros e o segundo passa informações sobre a forma como os dados deverão ser apresentados. Um exemplo super prático disso pode ser visto nesse excelente artigo do Greg Shackles onde ele usa os bundles para sinalizar que a backstack deve ser esvaziada. O outro parâmetro é do tipo MvxRequestedBy e serve para que o presenter tenha ciência de quem foi o ViewModel que solicitou a exibição. Isso pode ser útil para definir se a ViewModel que está sendo exibida deve entrar na backstack ou não, por exemplo.

Além desses parâmetros, nós podemos ainda explorar o método ChangePresentation. Esse método serve para quando nós queremos sinalizar para o presenter que algo deve ser feito com a ViewModel atual. Esse método faz uso da classe MvxPresentationHint, que permite que informações sejam enviadas para o presenter a qualquer momento. Um exemplo disso, que inclusive já é implementado por padrão no framework, é o método Close. Esse invoca o método ChangePresentation passando uma MvxClosePresentationHint. Essa hint é por padrão tratada em todas as plataformas para encerrar a ViewModel atual, mostrando que apesar de simples esse padrão é extremamente eficaz e oferece um poder de customização enorme à plataforma.

Isso tem uso na vida real?

Com certeza! Apesar de tudo isso parecer algo abstrato e de outro mundo, quando você precisar de um custom presenter tudo fará sentido na sua cabela. Acredite, saber oque está nesse artigo é importante para quem quer fazer uma aplicação que vai além do feijão com arroz. No próximo artigo eu irei falar sobre injeção de dependencias, um tema crucial para quem quer fazer apps com MvvmCross. Espero que tenham gostado e até a próxima :)

blog comments powered by Disqus