Самодостаточные контроллы на Xamarin.Forms. Часть 3. Разбираемся с NuGet


    В прошлых главах мы строили архитектуру приложения на Xamarin таким образом, чтобы отдельные контролы можно было переиспользовать самым простым образом в других приложениях. В этой части мы обернем эти контролы в отдельный NuGet-пакет, опубликуем его на сайте nuget.org и попробуем переиспользовать их в другом приложении, при этом написав минимальное количество кода.

    В первой и во второй главах мы разработали приложение, которое состояло из 3 основных контролов: авторизация, каталог товаров, корзина. Это, пожалуй, типичный набор для любого интернет-магазина. Структура приложения была следующей:

    Авторизация → Каталог товаров → Корзина.

    Первоначально пользователь видит окно авторизации, далее добавляет товары из каталога в корзину. Представим себе ламповую ситуацию, когда ВНЕЗАПНО заказчик хочет поменять эту структуру на следующую: на первом экране показывать сразу каталог товаров, которые можно смотреть, но нельзя добавлять в корзину до тех пор, пока в отдельном окне пользователь не авторизуется, для наполнения корзины.

    … лирическое отступление
    В условиях Agile такая ситуация может происходить очень часто и мы, разработчики, должны быть готовы к таким изменениям требований. К сожалению, я все чаще сталкиваюсь с ситуациями, когда для реализации такого рода требований заказчика команда занимается вкручиваем костылей, чтобы выпустить новую версию приложения как можно скорее. Постепенно количество костылей увеличивается, они могут порождать новые баги и проект становится сложно поддерживать. В результате необходимо тратить недели, а то и месяцы на рефакторинг запутавшихся связей между модулями. С другой стороны, можно было потратить это время при старте проекта на создание архитектуры, готовой к таким изменениям. Основная идея этой архитектуры в том, что элементы управления (самодостаточные контролы) не должны зависеть от других элементов управления, чтобы их можно было изолировано менять, комбинировать между собой и т. д. Но их взаимодействие должно осуществляться по модели подписок в одном определенном месте. Получается что-то вида микросервисной архитектуры, но на фронтенде. :)

    Немного повозмущались, приступим к выполнению поставленной задачи.

    Глава 1. Создание NuGet пакета и публикация его в nuget.org.

    Для поставленной выше задачи создавать nuget пакеты, конечно, не обязательно, но почему бы попутно не разобраться с тем, как это все работает. А работает оно и в правду очень просто. Возьмем проект, который мы создавали на протяжении предыдущих 2 статей, он состоит из 3 модулей: Android, IOS и, так называемый, Portable Class Library. Мы будем создавать пакеты в Visual Studio for Mac.

    1) Заходим в свойства проекта PCL (правая кнопка мышки по названию проекта -> Options)
    2) Ищем вкладку NuGet Package → Metadata



    Заполняем поля в General. Они обязательные. Тут рекомендуется сразу придумать какой-то уникальный ID. Можно зайти на сайт nugget.org и попробовать найти библиотеку с вашим ID. Если такой нет – можете его занимать. К сожалению, проверки на уникальность прямо в VS нет. Версия: 1.0.0. Версию надо указывать именно такой, чтобы потом было проще обновлять сборку. Закрываем окно, делаем Build проекта.

    3) Кликаем по названию проекта правой кнопкой мыши, выбираем пункт “Create NuGet Package»



    Пакеты создадутся, и сохранятся в папке <название проекта>/<название проекта>/bin/debug/<ID пакета>.<Версия>.nupkg.

    В моем случае это …/test5/test5/bin/debug/my_test_nuget.1.0.0.nupkg

    Этот пакет мы и будем загружать на сайт nuget.org

    4) Заходим на сайт nuget.org. Авторизуемся/Регистрируемся. Заходим во вкладку Upload, и загружаем файл с разрешением .nupkg из пункта 3.



    Можем дополнить какой-то дополнительной информацией и нажимаем Submit. Этот пакет в поиске NuGet, мы пока не найдем (в нем он появится после индексации – обычно это час), однако этот пакет можно добавлять в любые проекты через консоль…



    Если вы видите такое сообщение, значит вы все сделали правильно.

    Глава 2. Использование самодостаточных контролов в других проектах

    Итак, я создал и опубликовал контролы из предыдущих глав (авторизация, каталог товаров и корзина)

    Создадим новый проект и добавим в него опубликованную сборку. Я назвал ее shop_controls_toolkit. Добавить её можно несколькими путями:

    1) Из Nuget консоли (Install-Package shop_controls_toolkit -Version 1.0.8)
    2) Или найти его в поиске NuGet (Packaged -> Add Package)

    Добавим необходимые контролы в MainPage.xaml

    <ContentPage 
        xmlns="http://xamarin.com/schemas/2014/forms" 
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
        xmlns:local="clr-namespace:testapp2" x:Class="testapp2.testapp2Page"
        xmlns:controls="clr-namespace:testcontrols;assembly=testcontrols">
        <Grid>
            <controls:ProductsListing x:Name="listing"></controls:ProductsListing>
            <Grid x:Name="menu"> 
                <controls:AuthorizationView x:Name="auth"></controls:AuthorizationView>
                <controls:BasketView x:Name="basket"></controls:BasketView>
            </Grid>
            <Button Clicked="Handle_Clicked" 
                    Image="burger.png">
            </Button>
        </Grid>
    </ContentPage> 
    

    Необходимо прописать xmlns, чтобы на этом уровне были видны контролы из добавленной библиотеки

    xmlns:controls="clr-namespace:testcontrols;assembly=testcontrols"

    Для соответствующего визуального эффекта так же нужно указать контролам их расположение Vertical и Horizontal Options.

    В code-behind надо проинициализировать использование всех контроллов и сервисов из библиотеки testcontrols, сделать это можно, например, в конструкторе App.cs

    testcontrols.Core.DI.Bootstrapper.RegisterIoC();

    Так же нужно подписаться на соответствующие сервисы, как это было описано во 2 части.

    
    public testapp2Page()
    {
        InitializeComponent();
        _authService = Container.GetInstance<IAuthorizationService>();
        _authService.AuthorizationChanged += AuthService_AuthorizationChanged;
        listing.AddProductRequest += Listing_AddProductRequest;
    } 
    
    
    void AuthService_AuthorizationChanged(bool isAuth)
    {
        if(isAuth)
        {
            basket.IsVisible = true;
            auth.IsVisible = false;
        }
        else
        {
            basket.IsVisible = false;
            auth.IsVisible = true;
        }
    } 
    

    Т.е. показываем корзину или авторизацию в зависимости от того, авторизован ли человек.

    Точно так же можно подписаться на события в листинге товаров. Например, мы хотим ограничить добавление товаров в корзину так, чтобы их можно было бы добавлять только при авторизованом клиенте.

    Подписываемся в конструкторе на Action контрола каталога товаров AddProductRequest

    
    async void Listing_AddProductRequest(string sku)
    {
        if(_isAuth)
        {
            _basketService.StartAddingProduct(sku);
            return;
        }
        await DisplayAlert("Авторизуйтесь", $"Для того, чтобы добавить товар в корзину необходимо авторизоваться", "Хорошо");
    } 
    

    Ну и далее можно подписываться на все event не вникая в саму реализацию контролов. Также у сервиса BasketService есть такие поля, как TotalCount или TotalPrice…



    Обновление Nuget пакетов

    Если вдруг мы хотим обновить что-то в библиотеке контролов, нам необходимо в проекте увеличить версию в Options проекта, и загрузить на сайте nuget.org .ngpkg файл с увеличенной версией, как будто это новая сборка (повторить действия из первой части этой статьи). Сам nuget поймет что это просто новая версия уже существующей библиотеки, и обновит ее версию.

    Через некоторое время (обычно это до 10 минут) в проекте, который использует эту библиотеку, появится лэйбл рядом с названием сборки, который и будет означать то, что версию можно обновить. Обновляем…



    Вот и всё.

    Благодаря такому подходу мы можем создавать различные контролы, оборачивать их в отдельный NuGet пакет и переиспользовать его во всех приложениях.

    Можно сделать перегрузку метода инициализации пакета, где будет доступна настройка дополнительных параметров, например: URL back-end API приложения, время хранения кэша данных и т.д., чтобы делать контролы еще более универсальными. В самом приложении мы не будем вникать в реализацию этих контролов – главное их правильно использовать. Скорость разработки увеличится, кол-во костылей уменьшится, а наши менеджеры будут радоваться скорости внесения изменений необходимых бизнесу.

    → Проект доступен в GitHub

    Интересно, что думают пользователи Хабра о таком подходе – оставляйте комментарии. В планах создание универсальной библиотеки для любого интернет магазина, чтобы упростить создание подобного рода B2C или B2B приложений.

    Предыдущие статьи цикла читайте здесь и здесь.
    • +13
    • 3,1k
    • 4
    Mobile Dimension 78,02
    Компания
    Поделиться публикацией
    Комментарии 4
    • 0

      А редактор там уже появился? Или так же всё только текстом писать?

      • 0

        Вы имеете в виду верстку UI?

        • 0
          Да. Последний раз, что я пробовал, приходилось постоянно перезапускать всё в эмуляторе просто чтобы посмотреть влезло ли всё на экран.
          • 0
            По факту существует так называемый Xamarin.Forms Previewer. (View > Other Windows > Xamarin.Forms Previewer), но на данный момент пользоваться им, я, конечно, бы не стал :)

            Во 1 — сильно грузит комп, Во 2 — он очень «нежный». Может внезапно выдать «An exception occured while rendering the control», причем нет кнопки rerender или перезапустить…

            Но вообще, в xaml никогда не было хорошо с preview. После нескольких месяцев верстки, развивая воображение, кол-во перезапусков приложения, чтобы посмотреть влезло ли, сводится к 0.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое