This commit is contained in:
Jerome Laban 2020-01-28 22:07:30 -05:00
Родитель 124dad91bf
Коммит ff7f9372a0
5 изменённых файлов: 150 добавлений и 1 удалений

Просмотреть файл

@ -0,0 +1,36 @@
# API Extensions
Uno provides a simple mecanism which allows for external provides to provide an implementation for some known interfaces. The goal behind this is two fold:
- Provide a way to remove some types of dependencies from the main Uno.UI package, reducing the payload size of an app if some features are not required
- Provide a way for developers to provide a custom implementation of a known WinRT or WinUI api for which the default implementation is not provided by default
A common scenario can be found on Android, where adding an external dependency can increase the build time and payload size unnecessarily.
## Declaring an extension
In a nuget package, depending on the Uno.UI package, define the follow code:
```csharp
[assembly: Uno.Foundation.Extensibility.ApiExtension(typeof(Windows.ISomeExtensibleType), typeof(MySomeExtensibleType))]
public class MySomeExtensibleType : ISomeExtensibleType
{
public MySomeExtensibleType(object owner)
{
}
}
```
When a nuget package containing such a declaration is found in the currently built application, the App.InitializeComponent() method will automatically add the following code to the app startup:
```csharp
ApiExtensibility.Register(typeof(Windows.ISomeExtensibleType), o => new MySomeExtensibleType(o));
```
This will make the `Windows.ISomeExtensibleType` available for the `ApiExtensibility.CreateInstance<Windows.ISomeExtensibleType(...)` invocation to be available.
## Available Extensible APIs
- `IApplicationViewSpanningRects` to provide a implementation for `ApplicationView.GetSpanningRects()`

Просмотреть файл

@ -410,6 +410,7 @@ namespace Uno.UI.SourceGenerators.XamlGenerator
private void BuildApplicationInitializerBody(IndentedStringBuilder writer, XamlObjectDefinition topLevelControl)
{
InitializeRemoteControlClient(writer);
GenerateApiExtensionRegistrations(writer);
writer.AppendLineInvariant($"global::Windows.UI.Xaml.GenericStyles.Initialize();");
writer.AppendLineInvariant($"global::Windows.UI.Xaml.ResourceDictionary.DefaultResolver = global::{_defaultNamespace}.GlobalStaticResources.FindResource;");
@ -451,6 +452,23 @@ namespace Uno.UI.SourceGenerators.XamlGenerator
}
}
private void GenerateApiExtensionRegistrations(IndentedStringBuilder writer)
{
var apiExtensionAttributeSymbol = _medataHelper.FindTypeByFullName("Uno.Foundation.Extensibility.ApiExtensionAttribute");
var query = from ext in _medataHelper.Compilation.ExternalReferences
let sym = _medataHelper.Compilation.GetAssemblyOrModuleSymbol(ext) as IAssemblySymbol
where sym != null
from attribute in sym.GetAllAttributes()
where attribute.AttributeClass == apiExtensionAttributeSymbol
select attribute.ConstructorArguments;
foreach(var registration in query)
{
writer.AppendLineInvariant($"global::Uno.Foundation.Extensibility.ApiExtensibility.Register(typeof(global::{registration.ElementAt(0).Value}), o => new global::{registration.ElementAt(1).Value}(o));");
}
}
private void InitializeRemoteControlClient(IndentedStringBuilder writer)
{
if (_isDebug)

Просмотреть файл

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Uno.Foundation.Extensibility
{
/// <summary>
/// Registry for API existensibility providers, used to provide optional
/// implementations for compatible parts of WinUI and WinRT APIs.
/// </summary>
public static class ApiExtensibility
{
private static object _gate = new object();
private static Dictionary<Type, Func<object, object>> _registrations = new Dictionary<Type, Func<object, object>>();
/// <summary>
/// Registers an extension instance builder for the specified type
/// </summary>
/// <param name="type">The type to register</param>
/// <param name="builder">A builder that will be provided an optional owner, and returns an instance of the extension</param>
/// <remarks>This method is generally called automatically when the <see cref="ApiExtensionAttribute"/> has been defined in an assembly.</remarks>
public static void Register(Type type, Func<object, object> builder)
{
type = type ?? throw new ArgumentNullException(nameof(type));
builder = builder ?? throw new ArgumentNullException(nameof(type));
lock (_gate)
{
_registrations.Add(type, builder);
}
}
/// <summary>
/// Creates an instance of an extension of the specified <typeparamref name="T"/> type
/// </summary>
/// <typeparam name="T">A registered type</typeparam>
/// <param name="owner">An optional owner to be passed to the extension constructor</param>
/// <param name="instance">The instance if the creation was successful</param>
/// <returns>True if the creation suceeded, otherwise False.</returns>
public static bool CreateInstance<T>(object owner, out T instance) where T : class
{
lock (_gate)
{
if (_registrations.TryGetValue(typeof(T), out var builder))
{
instance = (T)builder(owner);
return true;
}
}
instance = null;
return false;
}
}
}

Просмотреть файл

@ -0,0 +1,32 @@
using System;
namespace Uno.Foundation.Extensibility
{
/// <summary>
/// ApiExtension registration for the <see cref="ApiExtensibility"/> class.
/// </summary>
[System.AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = true)]
public sealed class ApiExtensionAttribute : Attribute
{
/// <summary>
/// Creates an instance.
/// </summary>
/// <param name="extendedType">The type to extend</param>
/// <param name="extensionType">The type to create an instance from</param>
public ApiExtensionAttribute(Type extendedType, Type extensionType)
{
ExtensionType = extensionType;
ExtendedType = extendedType;
}
/// <summary>
/// The type to extend
/// </summary>
public Type ExtensionType { get; }
/// <summary>
/// The Type to create
/// </summary>
public Type ExtendedType { get; }
}
}

Просмотреть файл

@ -40,6 +40,11 @@
<PackageReference Include="System.Runtime.InteropServices.WindowsRuntime" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Extensibility\ApiExtensibility.cs" />
<UpToDateCheckInput Remove="Extensibility\ApiExtensionAttribute.cs" />
</ItemGroup>
<Import Project="..\Uno.CrossTargetting.props" />
</Project>