Add API extensions support
This commit is contained in:
Родитель
124dad91bf
Коммит
ff7f9372a0
|
@ -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,7 +452,24 @@ namespace Uno.UI.SourceGenerators.XamlGenerator
|
|||
}
|
||||
}
|
||||
|
||||
private void InitializeRemoteControlClient(IndentedStringBuilder writer)
|
||||
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>
|
||||
|
|
Загрузка…
Ссылка в новой задаче