Skip to content

Commit

Permalink
docs: Improve guidance for migrating UWP-only code
Browse files Browse the repository at this point in the history
  • Loading branch information
davidjohnoliver committed Dec 21, 2020
1 parent ace3bdb commit f237d52
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 156 deletions.
12 changes: 12 additions & 0 deletions doc/articles/howto-migrate-existing-code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Migrating UWP-only code to Uno

Uno Platform takes UWP code and makes it run on almost any target platform. If you have an existing application or library that was written to target UWP, you'll find guidance here for converting it to a cross-platform codebase, using UWP to target Windows 10 and Uno Platform to target other platforms. Depending on the APIs your code uses, it may run with minimal modifications, or you may need to add [platform-specific code](platform-specific-csharp.md) to cover missing functionality.

The articles in this guide cover:

- Initial [checklist](migrating-before-you-start.md)
- Steps for [migrating an application](migrating-apps.md)
- Steps for [migrating a class library](migrating-libraries.md)
- [General guidance](migrating-guidance.md) for converting UWP-only code to Uno-compatible code

See also the [guide to working with cross-targeted class libraries](cross-targeted-libraries.md).
149 changes: 30 additions & 119 deletions doc/articles/migrating-apps.md
Original file line number Diff line number Diff line change
@@ -1,146 +1,57 @@
# How to migrate existing UWP code to Uno Platform
# Migrating a UWP-only application codebase to an Uno Platform application

There are two separate paths for using an existing UWP codebase on top of Uno Platform:
- An existing UWP application
- An existing UWP library
This article describes how to migrate a C# and Xaml application targeting only UWP to one targeting multiple platforms using Uno Platform. The final application codebase will share 99% of the same code with the original, but the solution structure will be different.

For migrating a UWP library, see [this article](howto-migrate-existing-code.md).
It assumes you're using Visual Studio for Windows, but the steps are similar if you're using VS Code or another IDE. The basic principle is to create an empty Uno Platform app with the same name as your UWP-only app, and to copy the contents of the old app into the shared project of the new app.

# How to migrate UWP app to Uno Platform?
# Step by step guide.
After you've migrated the files and dependencies, the general [migration guidance article](migrating-guidance.md) will take you through the final steps to adjust your code to be Uno compatible.

This guide assumes that you have a UWP app, and want to convert this app to be multi-platform.
## Prerequisites

Follow the instructions to [set up your development environment for Uno Platform with Visual Studio for Windows](get-started-vs.md). You can also use another IDE such as [VS Code](get-started-vscode.md), however some steps may be slightly different.

## Migrating files to the Uno solution structure

In short:
* convert solution to use Uno Platform,
* check if any APIs you use are not implemented in Uno Platform.
* if so, you have two options:
* try to find a workaround for this, or
* extend Uno Platform
* launch and test the converted app.
Note: these steps will **destructively modify** your existing UWP-only solution. Make sure you have a copy in source control or in a back-up folder.

1. In order to reuse the name of the existing UWP project, we want to first rename it and its containing folder. (If you don't want to reuse the name, you can skip this step.)

## Create Uno Platform project
You have two options:
i. First, rename the project itself within the solution, by right-clicking on the project and choosing 'Rename'. For example, if the project is called `BugTracker`, we can rename it to `BugTracker_OLD`.

### 1. Adding new projects to existing solution
First, open your app solution in Visual Studio. Then, using the Solution Explorer window:
* Add new projects (right click on Solution, Add, New Project, Visual C#, Uno Plaform, Cross-Plaform App (Uno Platform), using same name as your existing project (APPNAME) with "\_Uno" suffix. In effect, APPNAME_Uno folder will be created, and inside it, APPNAME_Uno.Shared folder, and four 'heads', one for each target platform (as APPNAME_Uno.Droid, etc.).
* while reading next parts of this guide, treat `APPNAME[_Uno]` as `APPNAME_Uno`.
ii. Next the folder containing the project must be renamed (assuming it still has the default name from when the project was originally created). Navigate to the folder containing the project. (Eg, by right-clicking on the project in Visual Studio and choosing 'Open Folder in File Explorer'.) Close your Visual Studio instance. Rename the folder, so that the project path becomes `./BugTracker_OLD/BugTracker_OLD.csproj`.

### 2. Creating a new solution
* Create a new solution with the Uno app template for Visual Studio, using the same name as your existing project (APPNAME). In effect, APPNAME folder will be created, and inside it, APPNAME.Shared folder, and four 'heads', one for each target platform (as APPNAME.Droid, etc.).
* while reading next parts of this guide, treat `APPNAME[_Uno]` as `APPNAME`.
iii. Finally, before reopening Visual Studio, open the `.sln` file with your favorite text editor. Locate the line referencing your project, and update it to the new path. It should look something like:

### Tips for both options above
* description of Uno Platform app solution structure is [documented here](uno-app-solution-structure.md).
* you can unload your .Droid, .iOS and .WASM projects (right click in Solution Explorer) to make Visual Studio use less memory and start faster.
```
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BugTracker_OLD", "BugTracker_OLD\BugTracker_OLD.csproj", "{92E4C60C-336D-4DFB-B08D-80489617C1F2}"
```

## Converting your code
In simple terms, you have to copy all your content from old project (APPNAME) to APPNAME\[\_Uno\].Shared: all your XAML pages, all code (.cs), and replace folder Strings (delete just generated Strings folders). Copy also all other files and folders you created in previous project.
If you created new project in existing Solution, use Solution Explorer for this.
2. Open the solution. Right-click on the solution and choose 'Add > New Project'. Search for the 'Cross-Platform App (Uno Platform)' template. Enter the desired name (eg `BugTracker` in our fictional example). Hit 'Create'. You should now have a set of platform 'head' projects (`BugTracker.iOS`, `BugTracker.UWP`, etc), under the `Platforms` folder, and a Shared project (`BugTracker.Shared`).

If your code is in Visual Basic, you can use some simple translators, e.g. https://codeconverter.icsharpcode.net/ . It is not a perfect translation, some issues you will have to correct manually, but it is a good start. So, in Solution Explorer, for each XAML page:
* open .xaml file from old APPNAME,
* open .vb file from old APPNAME,
* right click on APPNAME\[\_Uno\].Shared, Add, New, C#, XAML, Blank Page - use same name as in APPNAME project,
* open both .xaml and .cs file you just created,
* copy contents of XAML page
* convert .vb code to .cs code, and insert it to .cs page - but do not remove constructor, with `this.InitializeComponent();`. From App.xaml.vb, convert only code you added. Take care of "namespace" - should be same as in .xaml (and as in manifest), without "\_Uno" and "Shared" suffixes.
3. Delete `App.xaml` and `MainPage.xaml` from the new Shared project - you will replace these with your existing files.

You have to copy also Package.appxmanifest (especially, app capabilities etc.) and Assets folder - but to APPNAME\[\_Uno\].UWP, not to APPNAME\[\_Uno\].Shared.
4. Transfer all code files (C# and Xaml) and assets from your old UWP-only project to the new Shared project.

Now if you wish you can unload your (old) APPNAME project (right click in Solution Explorer), not only to make Visual Studio use less memory and start faster, but also to be sure you don't mistakenly change something in your old code.
*Note: you can safely delete 'head' projects for platforms that you're sure you don't want to target.*

## Check conversion to Uno Platform (UWP)
Try to build your app - not the whole solution, but only the UWP project (choose Debug, and right click APPNAME\[\_Uno\].UWP, Build). If nothing unexpected happens (no errors), the first step of porting your app is done.
Launch UWP project, check if it is working as expected.
You can upload the new version of your app to the Microsoft Store.
## Adding dependencies

## Check if everything is implemented in Uno Platform
Now, reload APPNAME\[\_Uno\].Droid project if it is not loaded (right click on it in Solution Explorer). Give Visual Studio some time. It will rebuild the Intellisense database.
If your old UWP app project had dependencies, you will have to add those dependencies to each new target platform project you wish to support. These include:

Look into ErrorList window for warnings "is not implemented in Uno", e.g.
`"Warning Uno0001 Windows.UI.Xaml.Application.Exit() is not implemented in Uno".`
- .NET Standard projects within the solution: you should be able to add these as dependencies to any target platform project without difficulties.

If you don't have such warnings, you are in luck - your app is already 'multiplatformed'.
You can build for another platform (e.g. Android), to check if compilation is ok. Uploading your app to Google Store requires much more work, and is outside the scope of this text.
- UWP projects within the solution: these will have to be modified to be cross-platform class libraries - follow the instructions [here](migrating-libraries.md).

But most probably, you would find some Uno0001 warnings...
- external dependencies: [see information here](migrating-before-you-start.md) on finding equivalent dependencies for NuGet packages and framework libraries.

## Dealing with Uno0001 warnings
## Migrating app configuration

Now, you have several options, from simplest to hardest:
For UWP, the `Package.appxmanifest` file can be copied directly from your old UWP project to the new one in the `Platforms` folder. For other platforms, you will need to manually set the app name, capabilities, and other packaging information.

### Update Uno.UI and Uno.Core to latest stable version
As Uno gets new features constantly, maybe the missing UWP feature is already added.
Use Tools / NuGet Package Manager / Manage NuGet Packages for Solution, Updates tab, to check if Uno.Core/Uno.UI can be updated.
Or, check this file: [ReleaseNotes](https://github.com/unoplatform/uno/blob/master/doc/ReleaseNotes/_ReleaseNotes.md) (skip "next version" section)
## Adjusting code for Uno compatibility

### Update Uno.UI/Uno.Core to latest dev version
Or maybe feature is added, and you can use it - with small risk that something is broken.
Check "next version" section in previously mentioned file: [ReleaseNotes](https://github.com/unoplatform/uno/blob/master/doc/ReleaseNotes/_ReleaseNotes.md).
If it seems that your problem is resolved, install latest dev version (in Manage NuGet Packages for Solution, check 'include prerelease' checkbox).
[See the next section](migrating-guidance.md) for adjustments to make to get your code compiling on Uno.

### Check open PRs (Pull Requests)
Your next hope is that someone already make necessary changes to Uno Platform, but these changes are not included even in dev version (yet). It means that some discussion about implementation is under way, or maybe some tests are failing. But you can expect this feature in some near time appear in at least dev version.
## What if my existing app targets WinUI 3?

List of open PR can be found here: [Pull Requests](https://github.com/unoplatform/uno/pulls).

### Consider a workaround
Maybe feature is not implemented, but some "very close" feature is. For example, maybe your app is using CalendarDatePicker (not implemented in Uno 1.45). But Uno implement DatePicker, and this allows to select date, and besides that, this implementation in many ways resemble CalendarDatePicker from UWP.
So, you can use CalendarDatePicker in UWP, and DatePicker in all other. It can be done using XAML prefixes, in this case:

`<win:CalendarDatePicker Name="yourName" DateChanged="myDateChanged" ... />`

`<non_win:DatePicker Name="youName" DateChanged="myDateChangedDP" ... />`

and add second event handler, myDateChangedDP - it has to be new handler, as CalendarDatePicker.DateChanged and DatePicker.DateChanged handlers have different argument types.

All prefixes list: [platform-specific-xaml](https://github.com/unoplatform/uno/blob/master/doc/articles/platform-specific-xaml.md).

### Search for third-party NuGet packages for Xamarin
Maybe your app sends Toasts. They are not implemented in Uno Platform, but you can use this: https://github.com/edsnider/LocalNotificationsPlugin .
Similar solution exist for using Clipboard, and probably for many more problems.

### Create an Issue (feature request)
If all this fails, you can request adding new feature to Uno Platform.

Before submitting new Issue, check if someone else doesn't submit such issue before: [Issues](https://github.com/unoplatform/uno/issues).

If not, use this: [new feature request](https://github.com/unoplatform/uno/issues/new?labels=kind%2Fenhancement%2C+triage%2Funtriaged&template=enhancement.md).

### Contribute to Uno Platform
This is best (or: preferable) option, if using newer Uno versions / checking existing PR doesn't solve your problem. Best, but also it requires much more time from your side.
Check the 'Contributing to Uno' documentation for guides to [building Uno](uno-development/debugging-uno-ui.md), [creating samples](uno-development/working-with-the-samples-apps.md) and other topics.

### Make a stripped-down version
As last resort, you can make some app functions work only under Windows, and WASM/iOS/Android versions somehow limited.


Now it is time for compiling and test for platforms other than UWP.

## Special case: migrating app to Android

### Connect Android device
Similar to connecting Windows device - you have to enable debugging.
So, go to Settings > System > About , then tap the Build number seven times. This unhides Settings > System > Developer options. In this screen, you can enable USB debugging.

### Steps before compiling
Before you build/run app as Android, you should open .Droid project properties (right click on project in Solution Explorer), and:
1. in Application tab, check target framework (should be 9.0 - requirement, if you want to put app in Google Store)
2. in Android Manifest tab, check/enter
* app name,
* package name (in format `<yourid>.<app>` - app will be available in Store at link: `https://play.google.com/apps?id=<yourid>.<app>)`
* version number - (integer) number of your build
* version name - (string) set it to same value as in Manifest for UWP, in format 1.2.3.4
* minimum API version
* permissions - see [permission dictionary](https://developer.android.com/reference/android/Manifest.permission.html) and [features](https://developer.android.com/guide/topics/manifest/uses-feature-element) - it is not sufficient to add e.g. uses-permission for location, you have to add uses-feature location also.
* you can also convert .UWP\Assets\SmallTile.scale-100.png to .Droid\Resources\drawable\Icon.png by resizing from 71x71 to 72x72

### Compile and test
Build `Debug`, `Any CPU` configuration.
Prepare to long delay when app starts on Android device.
If all is ok (as it should be, with great probability), you can send app to e.g. Google Store. You can use [this tutorial](https://riptutorial.com/xamarin-android/example/29653/preparing-your-apk-in-the-visual-studio).
The basic idea is the same, however you'll use the [`dotnet new` templates](get-started-dotnet-new.md) to create a WinUI 3-compatible Uno Platform app, into which you can transfer files and dependencies in a similar manner.
56 changes: 56 additions & 0 deletions doc/articles/migrating-before-you-start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Migrating UWP-only code - Checklist

Before you start migrating a UWP-only application or library to be Uno compatible, it's a good idea to assess the degree of effort involved in getting the port fully functional. Depending on the code in question, it may be as easy as copying the files, or it may involve significant extra effort.

The key questions to ask are:

- what framework APIs is the code using?
- what third-party dependencies does the code rely upon?
- what general .NET functionality is used that may not be supported on specific platforms?

## Controls and framework APIs

The controls supported by Uno and those currently unsupported are [listed here](implemented-views.md). You can see supported non-UI APIs under the 'Features and Controls' section of the docs. Note that API coverage varies by target platform.

Once you migrate your code to Uno, unsupported API usages will be highlighted with a warning by Uno's included Roslyn analyzer.

## Third-party dependencies

Look at the NuGet packages referenced by the code. For each package, ask:

- is it supported on each platform you wish to target?
- if not, is there an acceptable alternative?

Third-party dependencies typically fall into one of a few categories:

### Platform-independent

Fully platform-independent dependencies will typically be packaged as .NET Standard binaries. They should be compatible with any target.

### Platform-dependent

These include dependencies that may be calling platform-specific functionality (eg push notifications), or that may depend on unmanaged binaries that must be separately compiled for each platform.

You can check if there is a version of the package for your target platform by visiting the package page on nuget.org and opening the 'Dependencies' expander. Typically you'll see `UAP 10.0.x` listed as a target. If you want to target Android and iOS with Uno, you should check that `MonoAndroid` and `Xamarin.iOS` are listed as supported targets. If you want to target macOS, check that `Xamarin.Mac` is listed.

Unfortunately it's less easy to check if a platform-dependent package supports WebAssembly or Linux. Uno builds .NETStandard binaries for these targets, but the fact that a .NETStandard version exists for any given dependency doesn't guarantee that it will function as expected on those platforms, if platform-specific APIs are involved. You'll generally have to do some additional research.

### Depends on UWP

Libraries that depend on UWP itself, such as the [Windows Community Toolkit](https://docs.microsoft.com/en-us/windows/communitytoolkit/), must be recompiled against Uno Platform in order to be used. A number of popular UWP libraries have already been retargeted to Uno; a partial list is given [here](https://github.com/unoplatform/Uno#uno-features).

## .NET runtime features

On certain target platforms, support for some .NET functionality is limited or unavailable.

### iOS

.NET code must be Ahead-Of-Time (AOT) compiled to run on iOS, as a fundamental platform limitation. As a result, certain APIs that require runtime code generation (eg `System.Reflection.Emit`) will not work. This includes code that uses the `dynamic` keyword. See the [Xamarin.iOS documentation](https://docs.microsoft.com/en-us/xamarin/ios/internals/limitations) for more detail.

### WASM

Currently, WebAssembly code in the browser executes on a single thread (much like Javascript code in the browser). This limitation is expected to be lifted in the future, but for now, code that expects additional threads to be available may not function as expected.

[This issue](https://github.com/unoplatform/uno/issues/2302) tracks support for multi-threading on WebAssembly in Uno Platform.

Additionally, if you're using full AOT compilation on WASM, the same API restrictions will apply as for AOT compilation on iOS.
Loading

0 comments on commit f237d52

Please sign in to comment.