Перенос и обновление старых проектов Xamarin Native MvvmCross

6 шагов, предпринятых для выполнения миграции, и некоторый ретроспективный анализ

Xamarin Native был чрезвычайно популярен как кроссплатформенное решение, а MVVMC - самый популярный фреймворк, используемый с ним, в первую очередь благодаря его надежности и надежным возможностям. К счастью, Xamarin Native и MVVMCross часто получают обновления, но большинство компаний не обновляют регулярно свои приложения или платформы приложений, потому что в этом нет необходимости. По-прежнему существует масса приложений, написанных с использованием Xamarin Native и MVVMCross, как видно из пакета MvvmCross nuget, который по-прежнему получает ~ 1,5 тыс. Загрузок в день. Так что я надеюсь, что это руководство для всех, кто хочет перейти на новейшие фреймворки! Обязательно ознакомьтесь с этой статьей, прежде чем решать, нужно ли выполнять миграцию приложения.

Для людей, которые не используют MvvmCross, но пытаются перейти на AndroidX, вы можете перейти к шагу 5, и это может быть полезно. И если вы хотите изучить Xamarin Native, чтобы быть сверхпривлекательными для 10% опубликованных приложений Xamarin, которые все еще используют Xamarin Native (специальность первого прототипа), вы можете взглянуть на это мини-руководство.

Рассказ о трех миграциях

Перенесенное нами решение Xamarin было создано в 2017 году, поэтому оно использовало PCL вместо .NET Standard для двух основных проектов - одного для служб, а другого для ViewModels. Большинство зависимостей решения больше не поддерживают PCL в последних версиях и требуют .NET Standard 2.0. Таким образом, помимо обновления инфраструктуры MVVMCross с 4.4 до 7.1.2, мы также внесем это изменение. У нас был выбор остановиться на этом, но я принял вызов и решил также перейти на новейшие библиотеки AndroidX.

Методология: я сделал быстрый всплеск, чтобы увидеть, смогу ли я просто отключить все, используя только существующее решение, но у меня было слишком много проблем, было слишком много изменений, и в конце концов я достиг контрольно-пропускной пункт. Так что казалось более разумным начать со свежего решения .NET Standard Xamarin Native и добавлять к нему по одному проекту за раз. Я решил начать с основных проектов (ViewModels & Services), затем с iOS, а затем с Android (поскольку Android также требует миграции AndroidX).

Инструменты: я начал использовать Visual Studio, но Jetbrains Rider лучше подходит для миграции, потому что он вычисляет общее количество ошибок сборки и обновляет его на лету, без необходимости каждый раз нажимать кнопку восстановления. Как ни странно, когда у меня было ›15000 ошибок сборки, Visual Studio показывала только ~ 50. Также в Rider одним щелчком мыши вы можете волшебным образом добавить недостающее использование во все файлы вашего проекта и сэкономить массу времени.

Контроль версий: я создал для этого новый репозиторий, так как знал, что это займет у меня несколько дней, и я хочу иметь возможность сохранять свою работу по ходу. Я бы зафиксировал и продвинул проект только после того, как смогу построить. После внесения всех изменений я удалил все старые файлы из старого репозитория и вставил на его место новый код.

Шаг 1 - базовый проект MVVMCross Xamarin Native

  1. Создайте новое решение, используя пустой шаблон Xamarin Native. Убедитесь, что у него точно такое же имя.
  2. Переименуйте проект Core в свое старое имя проекта, так что .ViewModels в моем случае. Обновите файлы SLN и CSPROJ, чтобы они также указывали на переименованный проект ViewModels.
  3. Перейдите к параметрам проекта ViewModels и обновите параметры проекта - ›Target Framework до .NetStandard2.0. (Переход на .NETStandard2.1 дал мне некоторые проблемы с зависимостями).
    Примечание. Поскольку вы переходите с PCL на .NETStandard, вам не нужны project.json, app.config, AssemblyInfo.cs файлы, а CSProj может просто автоматически включать все в папки без необходимости указывать каждый файл.
  4. Добавьте последнюю версию MvvmCross (7.1.2) и убедитесь, что вы можете собрать iOS и Android с изменениями, запрошенными MvvmCross, для ViewModel и проектов пользовательского интерфейса. Я внес изменения из репозитория MvvmCross TipCalc и убедился, что приложение работает на обеих платформах (iOS и Android).

Шаг 2 - Перенос основных моделей представлений и проектов служб

  1. Щелкните правой кнопкой мыши решение ViewModel и выберите «Добавить› файлы из папки »и добавьте все элементы из каталога ViewModels старого решения, используя существующую структуру папок.
  2. Добавьте необходимые пакеты Nuget, такие как настройки ACR, AppCenter, MvvmCross, MvvmCross Plugins-Email / Messenger / Phone / WebBrowser, MvvmValidation, Plugin MediaManager, Newtonsoft.Json, Xamarin essentials. Xamarin Essentials может предотвратить потребность в некоторых других плагинах, таких как Connectivity и DeviceInfo.
  3. Добавьте второй Core project-Services (при необходимости) и убедитесь, что у него правильное имя, Target framework, и убедитесь, что проект ViewModel использует его в качестве ссылки.
  4. В проект Services добавьте необходимые пакеты Nuget (настройки ACR, AppCenter, MvvmCross, MvvmCross Messenger, Newtonsoft.Json)
  5. Не забудьте перейти с PCL на .NETStandard 2.0. Опять же, вам не понадобятся файлы project.json, app.config, AssemblyInfo.cs, и CSProj может просто автоматически включать все в папки без необходимости указывать каждый файл. Таким образом, вы можете просто щелкнуть правой кнопкой мыши и «Добавить› файлы из папки », чтобы добавить все оставшиеся файлы из старого каталога Services.
  6. Если где-либо используется старый `Plugin.Connectivity`, вы можете преобразовать CrossConnectivity.Current.IsConnected в Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.Internet
  7. Замените using MvvmCross.Platform только using MvvmCross
  8. Заменить using MvvmCross.Binding.ExtensionMethods на using MvvmCross.Binding.Extensions
  9. Заменить using MvvmCross.Plugins.Messenger на using MvvmCross.Plugin.Messenger
  10. Заменить Settings.Current.x на CrossSettings.Current.x
  11. При необходимости замените using MvvmCross.Core.ViewModels на using MvvmCross.Commands или using MvvmCross.ViewModels.

Шаг 3 - Некоторые конкретные ошибки основного проекта:

  1. Скорее всего у вас будут ошибки в Навигации из-за отсутствия некоторых функций (Close, ShowViewModel) для начала использования Navigator.Close и Navigator.Navigate
  2. Способ передачи данных в модель представления изменился из-за отсутствия использования «MvxBundle», чтобы вместо этого начать использовать параметры, которые требуют переопределения функции жизненного цикла подготовки.
  3. Всем IMvxCommands и MvxCommands потребуется using MvvmCross.Commands в файле или просто добавьте MvvmCross.Commands. перед каждым упоминанием. Функциональность Rider по добавлению всех необходимых операторов using может решить эту проблему наилучшим образом.
  4. В одной из моих ошибок говорилось, что мне нужно добавить CSharp DLL, поэтому я добавил C # nuget, и он устранил ошибку.
  5. Любые страницы, использующие медиа, - это намного больше работы, потому что события изменились, а некоторые свойства недоступны. Все суффиксы статуса изменились на State. Я оставил кое-что из этого, чтобы исправить после того, как закончу с остальной частью миграции, чтобы я мог работать над этим, пока команда QA тестировала оставшуюся работу.
  6. Mvx.Trace перестало работать, поэтому я просто перешел на Debug.WriteLine. А во всем остальном, в чем я не был уверен, я бы прокомментировал код вместе с комментарием //TODO: Fix later.
  7. Выполнив все 5000+ ошибок и вы сможете создать основной проект, вы еще не сможете строить на iOS, потому что вам нужно будет добавить туда пакеты Nuget (настройки ACR, AppCenter, MvvmCross, MvvmCross Plugins-Email / Messenger / Phone / WebBrowser, Newtonsoft Json, MvvmValidation, Plugin Mediamanager, Xamarin Essentials).
  8. Исправление некоторых ViewModels также может занять больше времени в зависимости от того, как информация была передана, когда мы перешли на страницу.

Шаг 4 - изменение проекта iOS:

  1. Взял старые файлы напрямую из старых, используя «Добавить файлы из папки», аналогично шагу, упомянутому выше.
  2. Заменить using MvvmCross.Binding.iOS.Views на using MvvmCross.Platforms.Ios.Binding.Views
  3. При необходимости замените using MvvmCross.Core.ViewModels на using MvvmCross.Commands или using MvvmCross.ViewModels.
  4. Заменить using MvvmCross.iOS.Support.Views на using MvvmCross.Platforms.Ios.Views
  5. Заменить using MvvmCross.Platform.Converters на using MvvmCross.Converters
  6. Заменить using MvvmCross.Binding.ExtensionMethods на using MvvmCross.Binding.Extensions`
  7. Удалено using MvvmCross.Platform.Core и добавлены недостающие использования с помощью Rider: MvvmCross.Binding.BindingContext для IMvxBindingContext, MvvmCross.ViewModels для IMvxInteraction и MvvmCross.Base для MvxValueEventArgs и IMvxDataConsumer
  8. Заменить using MvvmCross.Platform на using MvvmCross
  9. Заменить using MvvmCross.Core.Navigation на using MvvmCross.Navigation
  10. Удалены файлы Bootstrap, поскольку они больше не нужны, и удалены ссылки на них.
  11. Обновил тонкий файл AppDelegate для работы с обновленным AppStart. Также обновил файл Setup.cs по мере необходимости
  12. Обновлен DebugTrace.cs для наследования от IMvxLog вместо IMvxTrace и исправлен.
  13. Поскольку мы нигде не используем старую Plugin.Connectivity, конвертируем CrossConnectivity.Current.IsConnected в Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.Internet
  14. К счастью, никаких изменений в проигрывателе мультимедиа со стороны пользовательского интерфейса iOS не потребовалось, за исключением операторов using.
  15. Если вы используете настраиваемый презентатор представлений iOS (использующий класс, унаследованный от MvxIosViewPresenter), вы можете столкнуться с трудностями. Особенно, если вы используете `view` из этого переопределения: Show(IMvxIosView view, MvxViewModelRequest request), потому что это переопределение больше не доступно. У вас есть доступ только к Show(MvxViewModelRequest request). Я смог использовать это новое переопределение вместо CreateOverridePresentationAttributeViewInstance(Type viewType), просто сказав var view = base.CreateOverridePresentationAttributeViewInstance(viewType); для обновления атрибутов в UIViewController
  16. У вас также могут возникнуть проблемы с расширением заголовка раздела с TableSources, унаследованными от MvxExpandableTableViewSource. После отладки я понял, что MvvmCross автоматически добавляет скрытую кнопку, которая знает, что нужно нажать, чтобы развернуть. Я заметил, что этот класс использовал контейнер UIView для размещения внутри него ярлыков и изображений. После снятия контейнера он снова заработал.
  17. Некоторые из ваших TableViewSources могут использовать вариант ItemsSource.ElementAt(i).IsExpanded = false;, который больше не доступен. Вы можете исправить это, используя вместо этого var group = (ItemsSource as IList)[i] as DocumentListGroup; if (group != null) group.IsExpanded = false;

Шаг 5 - Обновление MvvmCross и Android- ›AndroidX

  1. Взял файлы, используя ту же структуру файлов и папок, используя «Добавить файлы из папки». Затем просто добавьте отсутствующие операторы using, и у вас возникнут некоторые из проблем, которые мы видели на шагах выше для iOS.
  2. Замените `MvvmCross.Binding.Droid.Target;` на `using AndroidX.AppCompat.Widget`
  3. Замените using Android.App и using Android.Widget на using AndroidX.AppCompat.Widget. Но для атрибута [Activity вы можете преобразовать его в [Android.App.Activity
  4. Замените using MvvmCross.Droid.Support.V7.AppCompat на MvvmCross.Platforms.Android.Views.AppCompat
  5. Заменить using MvvmCross.Droid.Views на using MvvmCross.Views
  6. Заменить Android.Support.V4.Content.Res на using AndroidX.Core.Content.Resources
  7. Замените Application.Context.x на CrossCurrentActivity.Current.AppContext.x, используя Plugin.CurrentActivity в нескольких местах
  8. Заменить using Toolbar = Android.Support.V7.Widget.Toolbar на using Toolbar = AndroidX.AppCompat.Widget.Toolbar
  9. При необходимости замените using Android.Support.V4.Widget и using Android.Support.V4.View на using AndroidX.Core.View, using AndroidX.DrawerLayout.Widget или using MvvmCross.Platforms.Android.Views
  10. При необходимости замените using MvvmCross.Core.ViewModels на using MvvmCross.Commands или using MvvmCross.ViewModels.
  11. Заменить using MvvmCross.Platform.Converters на using MvvmCross.Converters
  12. Заменить using MvvmCross.Binding.ExtensionMethods на using MvvmCross.Binding.Extensions
  13. Удален using MvvmCross.Platform.Core и добавлен using MvvmCross.Binding.BindingContext для IMvxBindingContext, using MvvmCross.ViewModels для IMvxInteraction и using MvvmCross.Base для MvxValueEventArgs и IMvxDataConsumer
  14. Замените using MvvmCross.Platform на using MvvmCross для Mvx.Resolve
  15. Заменить using MvvmCross.Core.Navigation на using MvvmCross.Navigation
  16. Заменить using Plugin.MediaManager.ExoPlayer & using Plugin.MediaManager на using MediaManager
  17. Заменить using MvvmCross.Binding.Droid.BindingContext на using MvvmCross.Platforms.Android.Binding.BindingContext
  18. Заменить using MvvmCross.Binding.Droid.Views на using MvvmCross.Platforms.Android.Binding.Views
  19. Замените using MvvmCross.Droid.Support.V7.RecyclerView на using MvvmCross.DroidX.RecyclerView, для которого необходимо установить nuget MvvmCross.DroidX.RecyclerView.
  20. Заменить using MvvmCross.Droid.Views.Attributes на using MvvmCross.Platforms.Android.Presenters.Attributes
  21. Замените using MvvmCross.Droid.Support.V4 на using MvvmCross.Platforms.Android.Views.Fragments для фрагментов или иногда на using MvvmCross.DroidX
  22. Большинство моих действий требовало добавления using AndroidX.Lifecycle
  23. Мне также нужно было добавить using Google.Android.Material.BottomSheet и using Google.Android.Material.Snackbar в некоторые классы
  24. По своему личному выбору я преобразовал MvxAppCompatSpinner в MvxSpinner, и этот макет также нуждался в исправлении.
  25. Страницы с вкладками могут быть немного сложными. Вам необходимо обновить MvxViewPagerFragmentInfo, поскольку теперь для него требуется передача MvxViewModelRequest, а не только ViewModel. Передача параметров теперь немного отличается. Я как бы застрял там, глядя на документы. Я нашел пример и ответ на переполнение стека, но это не сильно помогло. Благодаря подсказке MvvmCross Tomasz я обнаружил, что MvxViewModelInstanceRequest - это тип MvxViewModelRequest, который позволит вам передать экземпляр ViewModel, что упрощает процесс.
  26. Некоторые страницы с вкладками могут по-прежнему сломаться, что может вызвать сбой System.Reflection.AmbiguousMatchFound исключения, а поиск источника сбоя может быть затруднен
  27. Удалены файлы Bootstrap, поскольку они больше не нужны, и удалены ссылки на них.
  28. Преобразование стартапа в новый стиль
  29. Преобразование журнала в debugtrace
  30. Поскольку мы везде используем более старый Plugin.Connectivity, конвертируем CrossConnectivity.Current.IsConnected в Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.Internet.
  31. Изменено более 70 файлов фрагментов, действий и макетов, но я давно перестал считать. Обновлен пользовательский клиент WebView.
  32. Со стороны пользовательского интерфейса Android потребовалось внести несколько изменений в аудиоплеер.
  33. Обновите все диалоговые окна, включая AlertDialog, которые потребовали некоторой работы из-за изменений AndroidX. Вместо этого преобразуйте классы, которые наследуются от DialogFragment, чтобы они начали наследовать от MvxDialog. Теперь dialog.Show() потребуется SupportFragmentManager. Этот шаг может вызвать у вас несколько неприятных проблем. Для ваших диалогов, если вы используете EnsureBindingContextSet (savedState), вы можете использовать это вместо this.EnsureBindingContextIsSet ();

Шаг 6 - XML-файлы макета и меню для Android, отличные от Csharp

  1. android.support.v4.widget.DrawerLayout to androidx.drawerlayout.widget.DrawerLayout
  2. android.support.v4.view.ViewPager to androidx.viewpager.widget.ViewPager
  3. android.support.v4.widget.NestedScrollView to androidx.core.widget.NestedScrollView
  4. MvvmCross.Droid.Support.V4.MvxSwipeRefreshLayout to MvvmCross.DroidX.MvxSwipeRefreshLayout
  5. MvvmCross.Droid.Support.V7.RecyclerView.MvxRecyclerView to MvvmCross.DroidX.RecyclerView.MvxRecyclerView
  6. android.support.v7.widget.AppCompatButton to androidx.appcompat.widget.AppCompatButton
  7. android.support.v7.widget.SwitchCompat to androidx.appcompat.widget.SwitchCompat
  8. android.support.v7.widget.SearchView to SearchView
  9. android.support.v7.widget.Toolbar to androidx.appcompat.widget.Toolbar
  10. android.support.v7.widget.CardView to androidx.cardview.widget.CardView
  11. android.support.design.widget.CoordinatorLayout to androidx.coordinatorlayout.widget.CoordinatorLayout
  12. android.support.design.widget.AppBarLayout to com.google.android.material.appbar.AppBarLayout
  13. android.support.design.widget.TextInputLayout to com.google.android.material.textfield.TextInputLayout
  14. android.support.design.widget.TextInputEditText to com.google.android.material.textfield.TextInputEditText
  15. android.support.design.widget.TabLayout to com.google.android.material.tabs.TabLayout
  16. android.support.design.widget.FloatingActionButton to com.google.android.material.floatingactionbutton.FloatingActionButton
  17. android.support.design.widget.NavigationView to com.google.android.material.navigation.NavigationView
  18. layout_behavior = от android.support.design.widget.BottomSheetBehavior до com.google.android.material.bottomsheet.BottomSheetBehavior
  19. Убедитесь, что вы нацелены на последнюю версию фреймворка и можете правильно скачивать и выгружать файлы. Вы можете временно исправить проблемы, включив разрешения на хранилище устаревших приложений при ориентации на платформу Android 10. Обновить его до новых API не так уж сложно.
  20. Странно то, что если вы используете MvxFrameControl, у вас могут быть проблемы. Вы должны переключить его с MvvmCross.Binding.Droid.Views.MvxFrameControl на MvvmCross.Platforms.Android.Binding.Views.MvxFrameControl
  21. В любом меню, если вы используете actionViewClass = «_ 164_», измените это значение на androidx.appcompat.widget.SearchView.
  22. Это было не так уж и сложно, мне пришлось поискать в Google. Как вариант, вы также можете взглянуть на это небольшое руководство.

Были и другие проблемы, которые я, возможно, не задокументировал в процессе, но я надеюсь, что это даст вам и вашей команде хорошую отправную точку для понимания работы и сложности, связанной с процессом миграции. Как всегда, не стесняйтесь обращаться ко мне, если у вас есть какие-либо вопросы. Не забывайте обращаться к руководствам по миграции, предоставленным MvvmCross и StackOverflow для любых препятствий.