📜 ⬆️ ⬇️

Self-sufficient controllers on Xamarin.Forms: "Re-use the code to the maximum!". Part 1



Even as an idea, Xamarin.Forms appealed to all WPF developers: the popularity of creating applications for Android and iOS grew, WPF became a relic, and the relevance of WPF developers steadily sought to zero - Forms sounded like salvation. There is a hope that we, with our knowledge of XAML and MVVM pattern, will be needed by someone. Of course, initially Xamarin.Forms was raw, with a lot of bugs and the absence of some vital things (remember at least input control without the possibility of specifying maxwith).

Three years have passed and Microsoft acquired Xamarin. Now he delivers it with Visual Studio, and as a result: there are fewer bugs, and more out of the box capabilities. But one problem remained: applications with a single interface are not obtained native. That is, if there is a difference in the interfaces of Android and iOS, the developer faces the pain of creating separate ViewModel for each platform ... and these are only flowers.
')
But we at Mobile Dimension specialize in enterprise applications, and in this case, the unity of the interface is a plus. Moreover, when a company has many solutions for different purposes, even entire functional controllers (authorization form or product catalog) should look the same.

In such a situation, I want to be able to reuse the code not only in different platforms, but also on different projects. Accordingly, it is necessary to provide self-sufficiency of the controllers, i.e. a controller that exists with all its business logic functions, interaction with the REST API, caching, etc. Moreover, such controllers should be independent of the presence of other controllers. It is necessary to create a situation in which the controller lives its own life and causes (invoke) some events when something happens.

Looking ahead, I note that everything looks simple and natural, but this approach came to us after long weeks of code refactoring, where the data layer was initially strongly separated from the interface layer with business logic. With this classic approach, everything is great: it is clear which module is responsible for what, the threshold for entering a new developer is low. However, when a project grows to several dozens of controllers, each of which interacts with the others, there are unexpected errors associated with the entanglement of calls to methods of different controllers. This all eventually led us to the so-called “hell of viewmodels”.

It is unraveled thanks to the approach through services and subscriptions to changes in these services. Those. we do not focus on the fact that the data layer was completely independent of logic or interface, but on the fact that a particular controller was absolutely indelible from other controllers.

Today I will show the simplest example - authorization. Imagine that we have a REST point for authorization. We create a controller that can authorize users in all systems in the company. Its interface is implemented once. All requirements for styles are indicated in the markup in the authorization controller:

Authorization Control Markup
<Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <ActivityIndicator Grid.RowSpan="2" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Color="Red" IsEnabled="True" IsVisible="{Binding IsLoading}" IsRunning="True"/> <Label Text="" FontSize="Large" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"/> <StackLayout Grid.Row="1" Orientation="Horizontal"> <Entry Text="{Binding Login, Mode=TwoWay}" VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand"/> <Button Text="OK" VerticalOptions="CenterAndExpand" Command="{Binding RegisterCommand}" BackgroundColor="Red" TextColor="White"/> </StackLayout> </Grid> 

The imposition of such a controller is the most difficult part of its creation: it is necessary to take into account the fact that it must be adaptive to any container where it may later end up. Literally beware of any particular value, trying to stretch the content. Here the Grid will help us with its automatic content stretching by cells and Vertical / HorizontalOptions. Unfortunately, ViewBox (a control that stretches content) from UWP to Forms has not yet been implemented, however such an initiative was proposed and can be cast on the developers page of Xamarin, where users literally prioritize the features that will be included in the next Xamarin.Forms builds.

Then we implement the ViewModel, in which the authorization service method from the business logic is called.

ViewModel Registration
 IAuthorizationService _authorizationService; public AuthorizationViewModel() { this.RegisterCommand = new Command(async () => { await Registration(); }); _authorizationService = Core.DI.Container.GetInstance<IAuthorizationService>(); } private async Task Registration() { IsLoading = true; await _authorizationService.Login(Login); IsLoading = false; } private string _login; public string Login { get { return _login; } set { _login = value; RaizePropertyChanged(nameof(Login));} } //INotifyPropertyChanged realization 

Using the controller
 <local:AuthorizationView WidthRequest="300" VerticalOptions="CenterAndExpand" HorizontalOptions="Center"/> 

You can subscribe to change this service from anywhere in the application and process it accordingly. The service is only responsible for the raise.

Subscribe to a controller event
 var authorizationService = Core.DI.Container.GetInstance<IAuthorizationService>(); authorizationService.AuthorizationChanged += AuthorizationService_ AuthorizationChanged; 

This controller can be added to any other place in the project, to any container of any size - it will look good everywhere, because we have taken care of the layout.

Read in part 2 : more complex controllers and their interaction with each other.

Github link

imageimage

Source: https://habr.com/ru/post/327576/


All Articles