Xamarin.Forms, MVVM i nawigacja

Przy pierwszym spotkaniu z aplikacją pisaną w Xamarin.Forms i wzorcu MVVM można poczuć spore zagubienie w jaki sposób zacząć. Znalezienie odpowiedniego rozwiązania zajęło mi sporo czasu, aż w końcu udało mi się wypracować podejście, które jak dla mnie sprawdza się przy niewielkiej aplikacji. Poniżej opiszę kilka rozwiązań, które zastosowałem w swoim projekcie, a na końcu tego wpisu zamieszczę zbiór linków, które są skarbnicą wiedzy w tym temacie.

Struktura projektu

Struktura projektu jest strukturą typową dla wzorca MVVM, a przedstawiam ją tutaj głównie jako wprowadzenie do dalszych punktów. Projekt składa się z katalogów:

  • Models – gdzie umieszczam wszystkie klasy zawierające czyste dane biznesowe,
  • ViewModels – zawiera modele widoku, odpowiedzialne za logikę prezentacyjną,
  • Views – widoki, najczęściej pliki XAML,
  • Services – klasy wykonujące operacje i zwracające dane do modeli widoku – rozdzielone na katalogi Interface i Implementation, w celu lepszej organizacji projektu.

Bootstrapper

Aby ułatwić sobie zarządzanie inicjalizacji aplikacji stworzyłem klasę Bootstrap, które wywoływana jest w konstruktorze klasy App. W niej odbywa się konfiguracja okna głównego, rejestracja serwisów, a także powiązanie widoków z ich modelami.

W metodzie Run następuje stworzenie kontenera DI (Dependency Injection) i jego konfiguracja. Gdy nasze zależności są już zarejestrowane, konfigurowana jest fabryka widoków i w ostatnim kroku ustawiana jest strona główna aplikacji.

Dependency Injection/Autofac i refleksje

W przykładzie jako kontener rozwiązujący zależności wykorzystałem Autofac. W metodzie ConfigureViewModels za pomocą mechanizmu refleksji pobieram wszystkie klasy z przestrzeni nazw ViewModels, zakończone wyrażeniem ViewModel i rejestruję je w kontenerze. Pozwala mi to w późniejszych krokach na stworzenie modelu widoku z rozwiązanymi już zależnościami i powiązanie go z odpowiednim widokiem.

Analogicznie sytuacja wygląda dla serwisów. Metoda ConfigureServices przeszukuje przestrzeń nazw Services i wyciąga z niej wszystkie klasy oraz rejestruje je jako domyślne implementacje dla interfejsów jakie są przez nie implementowane. Dwa serwisy wymagają indywidualnego podejścia, ponieważ muszą istnieć jako singletony, stąd też oznaczone zostają jako SingleInstance

Dawniej odkrywanie klas za pomocą refleksji było dla mnie nie do przyjęcia, ponieważ uważałem, że może to powodować trudne do wykrycia błędy. Przekonałem się jednak, że mechanizm ręcznego rejestrowania klas również nie jest idealnym rozwiązaniem – często zapominałem zarejestrować świeżo dodaną klasę, co skutkowało koniecznością ponownego przebudowania i uruchomienia kodu. Dodatkowo każdorazowe dodanie klasy wiązało się z modyfikacją innych komponentów. Stąd też podejście automatyczne wydaje się być całkiem rozsądne. Dodaję nowy model widoku lub serwis i nie muszę się niczym martwić.

Lazy<INavigation>

Mając już zarejestrowane wszystkie modele widoku i wykorzystywane przez nie serwisy możemy przejść do implementacji nawigacji. INavigation to interfejs dostarczony przez Xamarin.Forms, który udostępnia metody pozwalające na przełączanie stron w aplikacji, niezależnie od używanej platformy. Jego rejestracja w kontenerze wymaga od nas jednak specjalnego podejścia.  Implementację można uzyskać z obiektu MainPage, ale on z kolei powiązany jest z modelem widoku, który zawiera już w sobie serwis z nawigacją. Tworzy się tutaj pewnego rodzaju cykliczna zależność z którą możemy sobie poradzić używając leniwe inicjowanie (ang. Lazy Initialization) i kontener Autofac, który ten mechanizm wspiera. Podczas rejestrowania interfejsu do kontenera przekazujemy delegat, który wywołany zostaje dopiero przy pierwszym użyciu.


Natomiast w konstruktorze klasy NavigationService nasz interfejs opakowujemy w klasę Lazy<>.

Dzięki temu zabiegowi możemy użyć interfejsu INavigation w naszym własnym serwisie, który posłuży nam do zmiany widoków.

Fabryka widoków

Widok w aplikacji tworzony jest na podstawie zadanego modelu widoku. Dlatego też, aby ułatwić tworzenie nowych stron powstała fabryka ViewFactory. Zawiera ona zmapowane typy, które są rejestrowane podczas uruchamiania aplikacji. Podobnie jak w przypadku serwisów zdecydowałem się tutaj użyć refleksji w celu automatycznej eksploracji folderów i wyciągnięcia z nich klas:

Mając już informację przez jaki widok używany jest dany model widoku mogę zwrócić odpowiedni Page z ustawionym BindingContextem na podstawie przekazanego modelu widoku:

Navigation Service

Jeżeli nadejdzie potrzeba, by z modelu widoku zmienić aktualnie wyświetlaną stronę to korzystamy z interfejsu INavigationService.
Wykorzystuje on INavigation oraz IViewFactory do przełączenia aktualnej strony. Za pomocą metody NavigatoToAsync możemy przejść do interesującego nas widoku:

Metoda NavigateToAsync na podstawie otrzymanego typu modelu widoku tworzy nową stroną używając do tego wcześniej zarejestrowane, powiązane typy i wyświetla ją użytkownikowi:

 Podsumowanie

Przedstawiony szkielet aplikacji wymaga jeszcze kilku poprawek. Następnym krokiem w jego udoskonaleniu będzie poprawa obsługi błędów oraz zarządzanie czasem życia obiektów. Poniżej przedstawiam zbiór linków wartych przejrzenia:

  • http://docs.autofac.org
  • https://developer.xamarin.com/guides/xamarin-forms/getting-started/introduction-to-xamarin-forms/
  • https://mallibone.com/post/xamarin.forms-navigation-with-mvvm-light
  • http://adventuresinxamarinforms.com/2014/11/21/creating-a-xamarin-forms-app-part-6-view-model-first-navigation/
  • https://visualstudiomagazine.com/articles/2015/06/01/navigation-with-xamarin-forms.aspx
Facebooktwitter

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *