Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs - add expanded guide on platform-specific code, fix TOC links #1221

Merged
merged 8 commits into from
Jul 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added doc/articles/Assets/platform-specific-xaml.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/articles/Assets/solution-structure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions doc/articles/check_toc.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#
ghuntley marked this conversation as resolved.
Show resolved Hide resolved
# Reports dead links in the table of contents used by docfx and .md files that aren't linked anywhere in the TOC
#

$expectedMissingLinks = '.\implemented-views.md' #Implemented views is generated by the CI build
$expectedUnlinked = '.\roadmap.md'
$tempFileName = 'toc_additions.yml.tmp'

$allMdFiles = Get-ChildItem -Recurse -Filter *.md | Resolve-Path -Relative | Where-Object {$_.IndexOf('\implemented\') -lt 0}
$toc = Get-Content -Path .\toc.yml
$allMdLinks = $toc | foreach {$_ -split ":"} | foreach {$_.Trim(' ')} | foreach {'.\' + $_} | foreach {$_ -replace '/', '\'} | Where-Object {$_.EndsWith(".md")}
$badLinks = $allMdLinks | Where-Object {-not (Test-Path -Path $_)} | Where-Object {-not ($expectedMissingLinks -contains $_)}
$unlinkedFiles = $allMdFiles | Where-Object {-not ($allMdLinks -contains $_)} | Where-Object {-not ($expectedUnlinked -contains $_)}
if ($badLinks.Length -gt 0) {
Write-Host "Bad links found in toc.yml: $badLinks" -ForegroundColor Red
}
else {
Write-Host 'No bad links found in toc.yml'
}
if ($unlinkedFiles.Length -gt 0) {
Write-Host ".md files not linked in toc.yml: $unlinkedFiles"
'# UNLINKED .MD FILES: Add to toc.yml in appropriate category' | Out-File $tempFileName
$unlinkedFiles | foreach {
$header = Get-Content $_ | Where-Object {$_.StartsWith('#')}[0] | foreach {$_.Trim('#').Trim(' ')} | Select -First 1
" - name: $header" | Out-File $tempFileName -Append
$link = $_.Trim('.').Trim('\')
" href: $link" | Out-File $tempFileName -Append
}
Write-Host "Missing links added to $tempFileName" -ForegroundColor Green
}
else {
Write-Host 'No unlinked files found'
if (Test-Path $tempFileName) {
'# No unlinked .md files' | Out-File $tempFileName
}
}
File renamed without changes.
105 changes: 105 additions & 0 deletions doc/articles/platform-specific-csharp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Platform-specific C# code in Uno

Uno allows you to reuse views and business logic across platforms. Sometimes though you may want to write different code per platform, either because you need to access platform-specific native APIs and 3rd-party libraries, or because you want your app to look and behave differently depending on the platform.

This guide covers multiple approaches to managing per-platform code in C#. See [this guide for managing per-platform XAML](platform-specific-xaml.md).

## Project structure

There are two ways to restrict code or XAML markup to be used only on a specific platform:

* Use [conditionals](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives/preprocessor-if) within a shared file
* Place the code in a file which is only included in the desired platform head.

The structure of an Uno app created with the default [Visual Studio template](https://marketplace.visualstudio.com/items?itemName=nventivecorp.uno-platform-addin) is [explained in more detail here](uno-app-solution-structure.md). The key point to understand is that files in a shared project referenced from a platform head **are treated in exactly the same way** as files included directly under the head, and are compiled together into a single assembly.


## `#if` conditionals

The most basic means of authoring platform-specific code is to use `#if` conditionals:

```csharp
#if MY_SYMBOL
Console.WriteLine("MY_SYMBOL is defined for this compilation");
```

If the supplied condition is not met, eg if `MY_SYMBOL` is not defined, then the enclosed code will be ignored by the compiler.

The following conditional symbols are predefined for each platform:

| Platform | Symbol |
| ----------- | ------------- |
| UWP | `NETFX_CORE` |
| Android | `__ANDROID__` |
| iOS | `__IOS__` |
| WebAssembly | `__WASM__` |
| MacOS | `__MACOS__` |

Note that you can combine conditionals with boolean operators, eg `#if __ANDROID__ || __IOS__`.

You can define your own conditional compilation symbols per project in the 'Build' tab in the project's properties.

## Type aliases

Defining a [type alias](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-directive) with the `using` directive, in combination with `#if` conditionals, can make for cleaner code. For example:

```csharp
#if __ANDROID__
using _View = Android.Views.View;
#elif __IOS__
using _View = UIKit.UIView;
#else
using _View = Windows.UI.Xaml.UIElement;
#endif

...

public IEnumerable<_View> FindDescendants(FrameworkElement parent) => ...
```

## Partial class definitions

Heavy usage of `#if` conditionals makes code hard to read and comprehend. A better approach is to use [partial class definitions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods) to split shared and platform-specific code.

### A simple example

Shared code in `PROJECTNAME.Shared/NativeWrapperControl.cs`:

```csharp
public partial class NativeWrapperControl : Control {

...

protected override void OnApplyTemplate()
{
base.OnApplyTemplate();

_nativeView = CreateNativeView();
}
```

Platform-specific code in `PROJECTNAME.Droid/NativeWrapperControl.Android.cs`:

```csharp
public partial class NativeWrapperControl : Control {

...

private View CreateNativeView() {
... //Android-specific code
}
```

Platform-specific code in `PROJECTNAME.iOS/NativeWrapperControl.iOS.cs`:

```csharp
public partial class NativeWrapperControl : Control {

...

private UIView CreateNativeView() {
... //iOS-specific code
}
```

You can use [partial methods](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods#partial-methods) when only one platform needs specialized logic.
70 changes: 70 additions & 0 deletions doc/articles/platform-specific-xaml.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Platform-specific XAML markup in Uno
davidjohnoliver marked this conversation as resolved.
Show resolved Hide resolved

Uno allows you to reuse views and business logic across platforms. Sometimes though you may want to write different code per platform, either because you need to access platform-specific native APIs and 3rd-party libraries, or because you want your app to look and behave differently depending on the platform.

This guide covers multiple approaches to managing per-platform markup in XAML. See [this guide for managing per-platform C#](platform-specific-csharp.md).

## Project structure

There are two ways to restrict code or XAML markup to be used only on a specific platform:
* Use conditionals within a shared file
* place the code in a file which is only included in the desired platform head.

The structure of an Uno app created with the default [Visual Studio template](https://marketplace.visualstudio.com/items?itemName=nventivecorp.uno-platform-addin) is [explained in more detail here](uno-app-solution-structure.md). The key point to understand is that files in a shared project referenced from a platform head **are treated in exactly the same way** as files included directly under the head, and are compiled together into a single assembly.

## XAML conditional prefixes

The Uno platform uses pre-defined prefixes to include or exclude parts of XAML markup depending on the platform. These prefixes can be applied to XAML objects or to individual object properties.

Conditional prefixes you wish to use in XAML file must be defined at the top of the file, like other XAML prefixes. They can be then applied to any object or property within the body of the file.

For prefixes which will be excluded on Windows (eg `android`, `ios`), the actual namespace is arbitrary, since the Uno parser ignores it. The prefix should be put in the `mc:Ignorable` list. For prefixes which will be included on Windows (eg `win`, `not_android`) the namespace should be `http://schemas.microsoft.com/winfx/2006/xaml/presentation` and the prefix should not be put in the `mc:Ignorable` list.

### Example

```xaml
<Page x:Class="HelloWorld.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:android="http://uno.ui/android"
xmlns:ios="http://uno.ui/ios"
xmlns:wasm="http://uno.ui/wasm"
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:not_android="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d android ios wasm">

<StackPanel Margin="20,70,0,0">
<TextBlock Text="This text will be large on Windows, and pink on WASM"
win:FontSize="24"
wasm:Foreground="DeepPink"
TextWrapping="Wrap"/>
<TextBlock android:Text="This version will be used on Android"
not_android:Text="This version will be used on every other platform" />
<ios:TextBlock Text="This TextBlock will only be created on iOS" />
</StackPanel>
</Page>
```

This results in:

![Visual output](Assets/platform-specific-xaml.png)

### Available prefixes

The pre-defined prefixes are listed below:

| Prefix | Included platforms | Excluded platforms | Namespace | Put in `mc:Ignorable`? |
|---------------|------------------------------|------------------------------|-------------------------------------------------------------|------------------------|
| `win` | Windows | Android, iOS, web, macOS | `http://schemas.microsoft.com/winfx/2006/xaml/presentation` | no |
| `xamarin` | Android, iOS, web, macOS | Windows | `http:/uno.ui/xamarin` | yes |
| `not_win` | Android, iOS, web, macOS | Windows | `http:/uno.ui/not_win` | yes |
| `android` | Android | Windows, iOS, web, macOS | `http:/uno.ui/android` | yes |
| `ios` | iOS | Windows, Android, web, macOS | `http:/uno.ui/ios` | yes |
| `wasm` | web | Windows, Android, iOS, macOS | `http:/uno.ui/wasm` | yes |
| `macos` | macOS | Windows, Android, iOS, web | `http:/uno.ui/macos` | yes |
| `not_android` | Windows, iOS, web, macOS | Android | `http://schemas.microsoft.com/winfx/2006/xaml/presentation` | no |
| `not_ios` | Windows, Android, web, macOS | iOS | `http://schemas.microsoft.com/winfx/2006/xaml/presentation` | no |
| `not_wasm` | Windows, Android, iOS, macOS | web | `http://schemas.microsoft.com/winfx/2006/xaml/presentation` | no |
| `not_macos` | Windows, Android, iOS, web | macOS | `http://schemas.microsoft.com/winfx/2006/xaml/presentation` | no |
16 changes: 15 additions & 1 deletion doc/articles/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@
href: getting-started-tutorial-1.md
- name: Developing with Uno.UI
href: using-uno-ui.md
- name: Platform-specific C# code in Uno
href: platform-specific-csharp.md
- name: Platform-specific XAML markup in Uno
href: platform-specific-xaml.md
- name: Feature flags
href: feature-flags.md
- name: Uno.UI.Toolkit
href: Uno.UI.Toolkit.md
- name: Uno app solution structure
href: uno-app-solution-structure.md
- name: Performance tips
href: Uno-UI-Performance.md
- name: Debugging C# on WASM
Expand All @@ -46,8 +52,16 @@
href: features/SpeechRecognition.md
- name: PowerManager
href: features/windows-system-power.md
- name: PasswordVault
href: features\PasswordVault.md
- name: Windows.ApplicationModel.Calls
href: features\windows-applicationmodel-calls.md
- name: Windows.Devices.Sensors
href: features\windows-devices-sensors.md
- name: Windows.Phone.Devices.Notification
href: features\windows-phone-devices-notification-vibrationdevice.md
- name: CommandBar
href: controls\working-with-commandbar.md
href: controls\commandbar.md
- name: ListView and GridView
href: controls\ListViewBase.md
- name: MapControl
Expand Down
39 changes: 39 additions & 0 deletions doc/articles/uno-app-solution-structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Uno app solution structure

This guide briefly explains the structure of an app created with the default [Uno app template](https://marketplace.visualstudio.com/items?itemName=nventivecorp.uno-platform-addin). It's particularly aimed at developers who have not worked with multi-platform codebases before.

## The project files in an Uno app

Let's say we've created a new solution with the [Uno app template](https://marketplace.visualstudio.com/items?itemName=nventivecorp.uno-platform-addin), call it `HelloWorld`. It will already contain the following projects:

1. A `HelloWorld.[Platform].csproj` file for each platform that Uno supports: UWP (Windows), Android, iOS, and WebAssembly (Web). This project is known as the **head** for that platform. It contains typical information like settings, metadata, dependencies, and also a list of files included in the project. The platform *head* builds and packages the binary executable for that platform.

> The Android head is named `Droid` to avoid namespace clashes with the original Android namespace.

2. A single `HelloWorld.Shared.shproj` file, plus an accompanying `HelloWorld.Shared.projitems` file. This *shared project* contains files that are shared between all of the heads.
davidjohnoliver marked this conversation as resolved.
Show resolved Hide resolved

> The main reason the solution contains a shared project and not a cross-targeted library is related to a missing Visual Studio feature. At present time (VS16.1.x), building a head that references such a cross-targeted library implies that all target frameworks are built, leading to a slow developer inner loop.

Normally, your UI and business logic will go in the shared project. Bootstrapping code, packaging settings, and platform-specific code goes in the corresponding platform head. [String resources](using-uno-ui.md#localization) normally go in the shared project. [Image assets](features/working-with-assets.md) may go either in the shared project or under each head. [Font assets](using-uno-ui.md#custom-fonts) must be placed under each head.

![Uno solution structure](Assets/solution-structure.png)

## Understanding shared projects

Clearly understanding how shared projects work is important to using Uno effectively. A shared project in Visual Studio is really nothing more than a list of files. Let's repeat that for emphasis: **a shared project is just a list of files**. Referencing a shared project in an ordinary `.csproj` project causes those files to be included in the project. They're treated in exactly the same way as the files inside the project.

It's important to be aware that the code in a shared-project file is compiled separately for each platform head. This gives a great deal of flexibility, but it also means that shared code may work for one platform, but not another.

For example, we decide we need to use the `Json.NET` library in our app. We install the NuGet package in our `HelloWorld.Droid` head, and add a class to our `HelloWorld.Shared` project:

```csharp
using Newtonsoft.Json;

...
```

We run our app on Android and it works fine. But now the UWP head fails to compile, because we forgot to install the NuGet package there. The solution in this case is to install the NuGet package in all the heads we're targeting.

## Further information

See additional guides on handling platform-specific [C# code](platform-specific-csharp.md) and [XAML markup](platform-specific-xaml.md) in an Uno project.
Loading