-
Notifications
You must be signed in to change notification settings - Fork 2
Type Name Mapping
This page describes swift-winrt
's approach to mapping type names from .NET to Swift, especially as it concerns namespaces.
- .NET assemblies are mapped to Swift modules one-to-one or many-to-one (this is configurable). For example, the
Windows.Foundation.FoundationContract.winmd
assembly could map to aWindowsFoundation
Swift module, or the various reference contractwinmd
files could all map to a singleWindowsUniversalApi
Swift module. - .NET types names are mapped to Swift types with following the
NamespaceSubnamespace_TypeName
format. For example,Windows.UI.Composition.CompositionTarget
becomesWindowsUIComposition_CompositionTarget
. - .NET type parameter arity suffixes are dropped if there is no clash within the current assembly, and otherwise preserved with an
_
separator. For example,Windows.Foundation.IVector`1
becomesWindowsFoundation_IVector
, butSystem.Action`1
would becomeSystem_Action_1
. - Alias Swift modules can be generated to mimic using statements. For example, a
WindowsUIComposition
module can be generated which would import theWindowsUniversalApi
module and exporttypealias
es for every type in the relevant namespace usingtypealias CompositionTarget = WindowsUIComposition_CompositionTarget
. This also enables theWindowsUIComposition.CompositionTarget
syntax in consumers, which works with protocols too.
The .NET and Swift namespacing models are different and mostly incompatible:
- .NET has explicit
namespace
s, which are essentially an extension of the type name. Languages are expected to provide syntactic sugar to make them implicit at the point of use (using
declarations in C#). - .NET languages may also offer disambiguation by assembly name (
Assembly::
in C#), but such uses are very uncommon. - Swift supports disambiguation by module name (
Foundation.UUID
). - Swift supports types nesting and empty
enum
s may be used as a form of namespacing. However:- The
enum
requires a single central declaration, which can then be contributed to by arbitrarily manyextension
's - Protocols cannot be nested into enums or other types (until SE-0404 gets implemented)
- The language provides no syntactic sugar to bring all nested types into the global scope at the point of use.
-
typealias
es can be used to introduce global aliases to a nested type, but they target a single type and can clash.
-
- The
Also relevant to this topic:
- Both .NET assemblies and Swift modules package related code and are referenced in a directed acyclic dependency graph.
- Technically .NET assemblies can have circular dependencies but this is exceedingly rare. It happens between
C:\Windows\System32\WinMetadata
winmd
files, but not in the original Reference or UnionMetadatawinmd
's from the Windows SDK.
- Technically .NET assemblies can have circular dependencies but this is exceedingly rare. It happens between
- .NET supports overloading on the type parameter arity (
System.Action
vsSystem.Action`1
vsSystem.Action`1
). Swift does not.
As pertains to namespacing, a Swift WinRT bindings should provide:
- A bijective mapping of type names that preserves the ability to reference two types having the same short name in different namespaces of the same assembly (
Namespace1.MyType
vsNamespace2.MyType
). - An analogue to the concept of
using
declarations, since using fully qualified .NET names everywhere would be very verbose. - As much as possible, no special magic for Windows SDK
winmd
files.
.NET assemblies commonly defines types under multiple namespaces that reference one another in cyclic dependency graphs. Since Swift modules must form an acyclic dependency graph, .NET namespaces cannot be mapped to Swift modules directly. Some specific assemblies might define types in multiple namespaces such that all dependencies flow in a single direction (for example, Windows.UI
to Windows.Foundation
), but this assumption is not generalizable and may be brittle since there is no such enforcement in .NET. Unfortunately, this means that the huge Windows.Foundation.UniversalApiContract.winmd
file probably cannot be split into multiple modules.
There is a natural one-to-one mapping from .NET assemblies to Swift modules because both form acyclic dependency graphs. In some instances, however, it may be desirable to combine multiple .NET assemblies into a single Swift module (conceptually ilmerge
the .NET assemblies):
- With Windows SDK reference contract assemblies, whose high granularity doesn't need to be exposed in the binding.
- With
WinMetadata
assemblies, because they form dependency cycles. This is essentially the same as using the pre-mergedUnionMetadata
winmd
files.
Swift does yet (see SE-0404) support nesting protocols in types, so an enum-based namespacing strategy would work for structs, classes, enums and delegates but require a different scheme for interfaces. For consistency, we should have a single namespacing strategy.
.NET assembly authors tend to avoid using the same type names in different namespaces, but this nevertheless happens since it is not enforced. For example, in Windows.Foundation.UniversalApiContract.winmd
, we find:
AnimationDirection: Windows.UI.Composition, Windows.UI.Xaml.Controls.Primitives
BackgroundActivatedEventArgs: Windows.ApplicationModel.Activation, Windows.UI.WebUI
CompositionTarget: Windows.UI.Composition, Windows.UI.Xaml.Media
EnteredBackgroundEventArgs: Windows.ApplicationModel, Windows.UI.WebUI
EnteredBackgroundEventHandler: Windows.UI.WebUI, Windows.UI.Xaml
LeavingBackgroundEventArgs: Windows.ApplicationModel, Windows.UI.WebUI
LeavingBackgroundEventHandler: Windows.UI.WebUI, Windows.UI.Xaml
NavigatedEventHandler: Windows.UI.WebUI, Windows.UI.Xaml.Navigation
PackageStatus: Windows.ApplicationModel, Windows.Management.Deployment
Panel: Windows.Devices.Enumeration, Windows.UI.Xaml.Controls
PlaybackRateChangeRequestedEventArgs: Windows.Media, Windows.Media.PlayTo
SuspendingDeferral: Windows.ApplicationModel, Windows.UI.WebUI
SuspendingEventArgs: Windows.ApplicationModel, Windows.UI.WebUI
SuspendingEventHandler: Windows.UI.WebUI, Windows.UI.Xaml
SuspendingOperation: Windows.ApplicationModel, Windows.UI.WebUI
The only generalized solution to avoid name clashes is to generate qualified names in Swift. This could take a few forms:
class Windows_UI_Composition_CompositionTarget
class WindowsUIComposition_CompositionTarget
// Or, shortening common namespace prefixes of the assembly
class UI_Composition_CompositionTarget
class UIComposition_CompositionTarget