With the release of .net 7 and MAUI, I was thinking about performing an upgrade from Xamarin Forms to MAUI, as Xamarin Forms will be at end of life on 1-May 2024. I thought it would be appropriate to upgrade my first example on Xamarin Forms to MAUI.
A few links that I found on upgrading:
- Upgrading to MAUI
- Migrating your app from Xamarin Forms
- Build your first MAUI app
- Migrating eShop app from Xamarin Forms to MAUI
- Dotnet MAUI workshop
I mostly used the first link in my migration, so I’ll start my upgrade in that order. If you’d like to go along with me, this was my starting point.
Note: If you are on windows or are using windows on parallels, you may be interested in the upgrade assistant.
Conversion start
To see an end version of where I wanted to go, I generated out a scaffolded “Welcome to MAUI” project, so I could see what the destination was to look like. I referred back to this many times, so I advise you to do this as well.
The version 1 of SQLiteSample hadn’t been updated since 2018; so I needed to get it up to date under Xamarin Forms; so I went and did that in upgading the nugets; with the majority of the actual code changes occurring with the Fluent validation that broke with an upgrade a version or two behind it’s present version. I tested and then committed the changes and started going to MAUI.
From the guide, I performed the changes on the common project’s .csproj file and on the Android and iOS project’s .csproj file from the instructions. Unfortunately this caused these errors:
Error NETSDK1139: The target platform identifier android was not recognized. (NETSDK1139) (SQLiteSample)
Error NETSDK1139: The target platform identifier ios was not recognized. (NETSDK1139) (SQLiteSample)
This caused me to look at the instructions closer, as the instructions were for .net 6, not .net 7 which I was using. I switched the labels over to .net7, but this didn’t help. I looked here and it showed that I might be missing some of the components. The workload list showed what was installed and what was available. I did these installations to continue:
dotnet workload list
sudo dotnet workload install maui
sudo dotnet workload install maui-android
sudo dotnet workload install maui-ios
Note: sudo is for Mac and not needed for Windows
I then performed the replaces and removes. One item not noted in this list was this:
Replace all instances of On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea(true) with On<iOS>().SetUseSafeArea(true)
At this point I compared my scaffolded solution to the converted solution and then created the missing appropriate directories in the shared project and if there were files and those files which were not in the common or iOS / Android project solutions, I copied them over from the scaffolded solution into the current project.
Conversion points not covered in the checklist
At the end of that I compiled, but it said that the AndroidManifest file does not exist. I found a note here, and ended up copying the manifest into the common solution.
Compiled again and now it’s complaining about Application is an ambiguous reference. I then placed ‘Microsoft.Maui.Controls.’ prefix in front of Application to specify what version to use.
Next up was this error:
Error NETSDK1047: Assets file '{dir}/SQLiteSample/SQLiteSample/obj/project.assets.json' doesn't have a target for 'net7.0-ios/iossimulator-x64'. Ensure that restore has run and that you have included 'net7.0-ios' in the TargetFrameworks for your project. You may also need to include 'iossimulator-x64' in your project's RuntimeIdentifiers. (NETSDK1047) (SQLiteSample)
At this point, I ended up moving the iOS / Android project specific items into the common project; and then unloading the iOS / Android projects. While I didn’t think of this at the time that this error occurred, I didn’t look at the common .csproj file to see if some additional changes were automagically put into the file, as later on I did notice Visual Studio added additional changes to the .csproj file.
This occurred next:
Error: The MinimumOSVersion value in the Info.plist (15.2) does not match the SupportedOSPlatformVersion value (11.0) in the project file (if there is no SupportedOSPlatformVersion value in the project file, then a default value has been assumed). Either change the value in the Info.plist to match the SupportedOSPlatformVersion value, or remove the value in the Info.plist (and add a SupportedOSPlatformVersion value to the project file if it doesn't already exist).
I had to go in and change the supported os platform version under common project properties to 15.2.
Following that, the compile got much further along, but this showed up:
actool error : None of the input catalogs contained a matching stickers icon set or app icon set named "AppIcon".
I found this article that talked about the new MAUI icon set; note: there is not a build action named ForegroundFile as noted in this article. The common Info.plist file (copied over from the iOS project) and the scaffolded project’s Info.plist had the same line for XSAppIconAssets and neither solution had that directory. The build action on all 3 of the files was set to the same thing as on the scaffolded project; so I’m not sure why it didn’t work.
I decided to copy over the scaffolded solution’s Info.plist, to keep things moving. In this case, there’s nothing special about the Info.plist nor the solution. When that was done, I compiled and it worked – very odd. I created the values that were not in this file from the old Info.plist.
Converting Xamarin Forms platform specific dependency injection over to MAUI’s platform specific code calls as shown here; I tried continuing to use the interface, instead of the specific partial method name, but it didn’t work.
Conversion database issues
At this point, the app compiled and I started it on the iOS emulator. Sadly, it didn’t work and died with this error: System.TypeInitializationException: The type initializer for ‘SQLite.SQLiteConnection’ threw an exception. That lead me to this link, which in turn lead me here. Apparently, now it’s not possible to use the sqlite-net-pcl nuget alone, but must use it with other nugets, even w/o encryption. From the link, add these:
- SQLitePCLRaw.core – version 2.1.0 (or higher)
- SQLitePCLRaw.bundle_green – version 2.1.0 (or higher)
- SQLitePCLRaw.provider.dynamic_cdecl – version 2.1.0 (or higher)
- SQLitePCLRaw.provider.sqlite3 – version 2.1.0 (or higher) – note NOT lib.e_sqlite3!
In addition to the nugets, have to add this line to the AppDelegate:
raw.SetProvider(new SQLite3Provider_sqlite3());
As I noted in this article, sqlite-net-pcl doesn’t work well in a multi-threaded way, I converted it over to a singleton instance and used dependency injection to be sure that there was only 1 copy of the class repository which held a reference to that database class. I tried to use the new MAUI dependency injection, but it didn’t work how I needed it to work, so I turned to using Splat locator DI instead.
Wrap up
With that I tested out the functionality of both iOS and Android and both worked as expected.
Finally, I reloaded the unloaded old (and now unused) iOS Android projects and removed them from the solution and deleted them from my hard drive. Note: After I removed and physically deleted the projects; I had to close, reopen, clean and build the solution and visual studio Mac 2022 to get rid of the odd error that was occurring.
The updated SQLiteSample solution is on Github.
Hopefully, this will help folks out who get odd errors as I did. The best to luck to us all who need to say thank you and farewell to Xamarin and head to MAUI.