The popular MVVMCross Nuget Package

Migrating & Updating old Xamarin Native MvvmCross projects

6 Steps taken to perform the migration, and some hindsight

The First Prototype

--

Xamarin Native was extremely popular as a cross platform solution, and MVVMCross the most popular framework used with it, primarily due to it’s reliability and robust capabilities. Fortunately, Xamarin Native and MVVMCross both get frequent updates, but most companies don’t regularly update their apps or app frameworks because they didn’t need to. There’s still a ton of apps still out there written with Xamarin Native & MVVMCross, as is evident from the MvvmCross nuget package still getting ~1.5k downloads a day. So I hope this acts as a guide for anyone looking to migrate to the latest frameworks! Be sure you check this article before determining whether to actually perform a migration for your app.

For people not using MvvmCross, but trying to migrate to AndroidX, you can skip to Step 5 and it might be useful. And if you are looking to learn Xamarin Native so you can be uber-attractive for the 10% of published Xamarin Apps still using Xamarin Native (The First Prototype’s speciality), you can look at this mini-guide.

A tale of three migrations

The Xamarin solution we migrated was created in 2017, so it was using PCL instead of .NET Standard for the two Core projects-one for Services and the other for ViewModels. Most of the solution’s dependencies did not support PCL anymore in it’s latest versions and required .NET Standard 2.0. So apart from updating the MVVMCross framework from 4.4 to 7.1.2, we would be making that change as well. We had a choice to stop there, but I took the challenge and decided to migrate to the latest AndroidX libraries as well.

Methodology: I did a quick spike to see if I’d just be able to switch everything out using just the existing solution, but I was having too many issues, there were too many changes and I eventually reached a roadblock. So it seemed much smarter to start from a fresh .NET Standard Xamarin Native solution and add one project to it at a time. I decided to start with the core projects (ViewModels & Services), then iOS & then Android (since Android requires the AndroidX migration as well).

Tooling: I started off using Visual Studio, but Jetbrains Rider is better for migrations because it calculates the total number of build errors and updates it on the fly without having to press rebuild everytime. Strangely, when I had >15000 build errors, Visual Studio only showed ~50. Also on Rider, with one click you can magically add a missing using in all the files of your project and save a ton of time.

Source Control: I created a new repository for this, since I knew it would take me several days, and I want to be able to save my work as I go. I would only commit and push a project once I was able to build. Once I made all the changes, I deleted all the old files from the old repository and pasted the new code in it’s place

Step 1 — Basic MVVMCross Xamarin Native project

  1. Create a New Solution using the Blank Xamarin Native template. Make sure it has the exact same name.
  2. Rename the Core project to your old project name, so .ViewModels in my case. Update the SLN and CSPROJ files so that they also point to the renamed ViewModels project
  3. Go to the ViewModels project options and update the Project Options->Target Framework to .NetStandard2.0. (Migrating to .NETStandard2.1 gave me some dependency issues).
    Note: Since you make the change from PCL to .NETStandard, you won’t be needing project.json, app.config, AssemblyInfo.cs files, and the CSProj can just automatically include everything in folders without needing to specify each file.
  4. Add the latest MvvmCross (7.1.2) and make sure you are able to build iOS and Android with the changes requested by MvvmCross to the ViewModel and UI Projects. I implemented the changes from the MvvmCross TipCalc repo, and made sure the app builds on both platforms (iOS & Android)

Step 2 — Migrating the Core ViewModels and Services Projects

  1. Right click on the ViewModel solution and select “Add->Files from Folder” and add select everything from the ViewModels directory of the old solution using the existing folder structure.
  2. Add the needed Nuget Packages like ACR Settings, AppCenter, MvvmCross, MvvmCross Plugins-Email/Messenger/Phone/WebBrowser, MvvmValidation, Plugin MediaManager, Newtonsoft.Json, Xamarin essentials. Xamarin Essentials can prevent the need of some other plugins like Connectivity and DeviceInfo
  3. Add the second Core project-Services(if needed) and make sure it has the right name, Target framework, and make sure the ViewModel project uses it as a reference.
  4. To the Services project, add the needed Nuget Packages (ACR Settings, AppCenter, MvvmCross, MvvmCross Messenger, Newtonsoft.Json)
  5. Remember to make the change from PCL to .NETStandard 2.0. Again, you won’t be needing project.json, app.config, AssemblyInfo.cs files, and the CSProj can just automatically include everything in folders without needing to specify each file. So you can just right click and “Add->Files from Folder” to add all the remaining files from the older Services directory.
  6. If using the older `Plugin.Connectivity` anywhere, you can convert CrossConnectivity.Current.IsConnected to Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.Internet
  7. Replace using MvvmCross.Platform with just using MvvmCross
  8. Replace using MvvmCross.Binding.ExtensionMethods with using MvvmCross.Binding.Extensions
  9. Replace using MvvmCross.Plugins.Messenger with using MvvmCross.Plugin.Messenger
  10. Replace Settings.Current.x with CrossSettings.Current.x
  11. Replace using MvvmCross.Core.ViewModels to using MvvmCross.Commands or using MvvmCross.ViewModels as needed.

Step 3 — Some specific Core project errors:

  1. Most likely you will have errors in the Navigation because of the riddance of some functions(Close, ShowViewModel) to start using Navigator.Close and Navigator.Navigate
  2. The way data is passed into a view model changed because of the lack of usage of “MvxBundle”, to instead start using Parameters which require the Prepare lifecycle function override.
  3. All the IMvxCommands and MvxCommands will need a using MvvmCross.Commands in the file or just add MvvmCross.Commands. before every single place it is mentions. Rider’s functionality to add all the required using statements can resolve this in the best way.
  4. One of my errors said that I needed to add the CSharp DLL, so I added the C# nuget and it resolved the error.
  5. Any pages using media is a lot more work, because the events have changed, and some properties are unavailable. All Status suffixes have changed to State. I left some of this to be fixed after I was done with the rest of the migration, so I could work on it while the QA team was testing the remaining work.
  6. Mvx.Trace stopped working so I just switched to use Debug.WriteLine. And for anything else I wasn’t sure of, I would comment the code along with a //TODO: Fix later comment.
  7. Once done with all 5000+ errors and you are able to build the core project, you will not be able to build on iOS yet because you will need to add the Nuget Packages there (ACR Settings, AppCenter, MvvmCross, MvvmCross Plugins-Email/Messenger/Phone/WebBrowser, Newtonsoft Json, MvvmValidation, Plugin Mediamanager, Xamarin Essentials).
  8. Some ViewModels may also take longer to fix depending on how information was passed when we got to the page

Step 4 — iOS Project change:

  1. Took the older files from the older directly, using “Add->Files from Folder” similar to the step mentioned above.
  2. Replace using MvvmCross.Binding.iOS.Views with using MvvmCross.Platforms.Ios.Binding.Views
  3. Replace using MvvmCross.Core.ViewModels to using MvvmCross.Commands or using MvvmCross.ViewModels as needed.
  4. Replace using MvvmCross.iOS.Support.Views with using MvvmCross.Platforms.Ios.Views
  5. Replace using MvvmCross.Platform.Converters with using MvvmCross.Converters
  6. Replace using MvvmCross.Binding.ExtensionMethods with using MvvmCross.Binding.Extensions`
  7. Removed using MvvmCross.Platform.Core and added the missing usings with the help of Rider: MvvmCross.Binding.BindingContext for IMvxBindingContext, MvvmCross.ViewModels for IMvxInteraction, and MvvmCross.Base for MvxValueEventArgs & IMvxDataConsumer
  8. Replace using MvvmCross.Platform to using MvvmCross
  9. Replace using MvvmCross.Core.Navigation with using MvvmCross.Navigation
  10. Removed the Bootstrap files since its not needed anymore, and remove references to it
  11. Updated the delicate AppDelegate file to work with the updated AppStart. Also updated the Setup.cs file as needed
  12. Updated the DebugTrace.cs to inherit from IMvxLog instead of IMvxTrace and fixed it up
  13. Since we are not using the older Plugin.Connectivity anywhere, convert CrossConnectivity.Current.IsConnected to Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.Internet
  14. Luckily, no changes on the media recording player were needed on the iOS UI side except for the using statements.
  15. If you are using a custom iOS view presenter (using a class that inherits from MvxIosViewPresenter), you might be in for a hard time. Especially if you are using the `view` from this override: Show(IMvxIosView view, MvxViewModelRequest request), because that override is not available anymore. You only have access to Show(MvxViewModelRequest request). I was able to use this new override instead CreateOverridePresentationAttributeViewInstance(Type viewType) by just saying var view = base.CreateOverridePresentationAttributeViewInstance(viewType); to update attributes on the UIViewController
  16. You might also have section header expand issues with TableSources inheriting from MvxExpandableTableViewSource. Upon debugging I realized, MvvmCross adds a hidden button automatically, that knows to tap to expand. I noticed that this class was using a container UIView to put labels and images inside. Upon removing the container, it started working again.
  17. Some of your TableViewSources might be using a variation of ItemsSource.ElementAt(i).IsExpanded = false; which is not available anymore. You can fix it by using this instead var group = (ItemsSource as IList)[i] as DocumentListGroup; if (group != null) group.IsExpanded = false;

Step 5 — MvvmCross update & Android->AndroidX

  1. Took the files using the same file and folder structure using the “Add->Files from Folder”. Then, just add the missing using statements and you’ll have some of the issues we saw from the iOS steps above.
  2. Replace `MvvmCross.Binding.Droid.Target;` with `using AndroidX.AppCompat.Widget`
  3. Replace using Android.App and using Android.Widget with using AndroidX.AppCompat.Widget. But for the [Activity Attribute, you can convert those to [Android.App.Activity
  4. Replace using MvvmCross.Droid.Support.V7.AppCompat with using MvvmCross.Platforms.Android.Views.AppCompat
  5. Replace using MvvmCross.Droid.Views with using MvvmCross.Views
  6. Replace Android.Support.V4.Content.Res with using AndroidX.Core.Content.Resources
  7. Replace Application.Context.x with CrossCurrentActivity.Current.AppContext.x using the Plugin.CurrentActivity in several places
  8. Replace using Toolbar = Android.Support.V7.Widget.Toolbar to using Toolbar = AndroidX.AppCompat.Widget.Toolbar
  9. Replace using Android.Support.V4.Widget and using Android.Support.V4.View as needed with using AndroidX.Core.View, using AndroidX.DrawerLayout.Widget or using MvvmCross.Platforms.Android.Views
  10. Replace using MvvmCross.Core.ViewModels to using MvvmCross.Commands or using MvvmCross.ViewModels as needed.
  11. Replace using MvvmCross.Platform.Converters with using MvvmCross.Converters
  12. Replace using MvvmCross.Binding.ExtensionMethods with using MvvmCross.Binding.Extensions
  13. Removed using MvvmCross.Platform.Core and added using MvvmCross.Binding.BindingContext for IMvxBindingContext, using MvvmCross.ViewModels for IMvxInteraction, and using MvvmCross.Base for MvxValueEventArgs & IMvxDataConsumer
  14. Replace using MvvmCross.Platform to using MvvmCross for Mvx.Resolve
  15. Replace using MvvmCross.Core.Navigation to using MvvmCross.Navigation
  16. Replace using Plugin.MediaManager.ExoPlayer & using Plugin.MediaManager with using MediaManager
  17. Replace using MvvmCross.Binding.Droid.BindingContext with using MvvmCross.Platforms.Android.Binding.BindingContext
  18. Replace using MvvmCross.Binding.Droid.Views with using MvvmCross.Platforms.Android.Binding.Views
  19. Replace using MvvmCross.Droid.Support.V7.RecyclerView with using MvvmCross.DroidX.RecyclerView for which you need to install the MvvmCross.DroidX.RecyclerView nuget
  20. Replace using MvvmCross.Droid.Views.Attributes with using MvvmCross.Platforms.Android.Presenters.Attributes
  21. Replace using MvvmCross.Droid.Support.V4 with using MvvmCross.Platforms.Android.Views.Fragments for Fragments or sometimes with using MvvmCross.DroidX
  22. Most of my activities required adding using AndroidX.Lifecycle
  23. I also needed to add using Google.Android.Material.BottomSheet and using Google.Android.Material.Snackbar in some classes
  24. As a personal choice, I converted the MvxAppCompatSpinner to MvxSpinner, and that layout also needed fixing
  25. Tabbed pages can be a bit challenging. You need to udate the MvxViewPagerFragmentInfo, since it now requires passing a MvxViewModelRequest instead of just the ViewModel. Passing in parameters is a little different now. I was kind of stuck there, looking at the docs. I found an example and a stackoverflow answer but it didn’t help much. Thanks to the MvvmCross Tomasz’s tip, I found that MvxViewModelInstanceRequest is a type of MvxViewModelRequest which will allow you to pass in the ViewModel instance and that makes it a breeze.
  26. Some of the tabbed pages might still break and might give you an System.Reflection.AmbiguousMatchFound exception crash, and finding the source of the crash can be difficult
  27. Removed the Bootstrap files since its not needed anymore, and remove references to it
  28. Converting the startup to the new style
  29. Converting the log in debugtrace
  30. Since we are using the older `Plugin.Connectivity` anywhere, convert `CrossConnectivity.Current.IsConnected` to `Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.Internet
  31. More than 70 Fragments, Activities and Layout file changes, but I stopped counting a long time ago. Updated the custom WebView Client
  32. Quite a few changes on audio media player were needed on the Android UI side
  33. Update all the dialogs including the AlertDialog which needed quite some work because of the AndroidX changes. Convert the classes that inherit from DialogFragment to start inheriting from MvxDialog instead. Now the dialog.Show() will need a SupportFragmentManager instead. This step might cause you some tricky icky issues. For your dialogs, if you are using EnsureBindingContextSet(savedState), you can use this instead this.EnsureBindingContextIsSet();

Step 6 — Non Csharp Android changes-layout & menu xml files

  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 to com.google.android.material.bottomsheet.BottomSheetBehavior
  19. Make sure you target the latest framework, and can download and upload files correctly. You can temporarily fix issues by enabling the legacy app storage permissions when targeting framework Android 10. Updating it to the new APIs is not too difficult.
  20. A strange one is if you are using MvxFrameControl, you might have some issues. You have to switch it from MvvmCross.Binding.Droid.Views.MvxFrameControl to MvvmCross.Platforms.Android.Binding.Views.MvxFrameControl
  21. In any menus, if you are using actionViewClass=“SearchView”, change that value to androidx.appcompat.widget.SearchView.
  22. This one wasn’t the hardest to figure out, I had to do some google searches. Alternatively, you can also take a look at this little guide.

There were other issues as well that I may not have documented through the process, but I hope this gives you and your team a good starting point to understand the work and complexity involved in the migration process. As always, feel free to reach out to me if you have any questions. Don’t forget to consult the migration guides provided by MvvmCross and StackOverflow for any roadblocks

MvvmCross migration guides

--

--